Python orientado a objetos - recursos avançados
Neste, veremos alguns dos recursos avançados que o Python fornece
Sintaxe central em nosso design de classe
Nisto veremos como o Python nos permite tirar proveito dos operadores em nossas classes. Python é em grande parte objetos e métodos chamam objetos e isso continua mesmo quando está oculto por alguma sintaxe conveniente.
>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']
Então, se tivermos que adicionar o método mágico __add__ às nossas próprias classes, poderíamos fazer isso também. Vamos tentar fazer isso.
Temos uma classe chamada Sumlist que tem um construtor __init__ que leva list como um argumento chamado my_list.
class SumList(object):
def __init__(self, my_list):
self.mylist = my_list
def __add__(self, other):
new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]
return SumList(new_list)
def __repr__(self):
return str(self.mylist)
aa = SumList([3,6, 9, 12, 15])
bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])
Resultado
[103, 206, 309, 412, 515]
Mas existem muitos métodos que são gerenciados internamente por outros métodos mágicos. Abaixo estão alguns deles,
'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()
Herdando de tipos integrados
As classes também podem herdar de tipos integrados, o que significa que herda de qualquer tipo integrado e aproveita todas as funcionalidades lá encontradas.
No exemplo a seguir, estamos herdando do dicionário, mas então estamos implementando um de seus métodos __setitem__. Este (setitem) é invocado quando definimos a chave e o valor no dicionário. Como esse é um método mágico, ele será chamado implicitamente.
class MyDict(dict):
def __setitem__(self, key, val):
print('setting a key and value!')
dict.__setitem__(self, key, val)
dd = MyDict()
dd['a'] = 10
dd['b'] = 20
for key in dd.keys():
print('{0} = {1}'.format(key, dd[key]))
Resultado
setting a key and value!
setting a key and value!
a = 10
b = 20
Vamos estender nosso exemplo anterior, abaixo chamamos dois métodos mágicos chamados __getitem__ e __setitem__ melhor invocados quando lidamos com índice de lista.
# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
def __getitem__(self, index):
if index == 0:
raise IndexError
if index > 0:
index = index - 1
return list.__getitem__(self, index) # this method is called when
# we access a value with subscript like x[1]
def __setitem__(self, index, value):
if index == 0:
raise IndexError
if index > 0:
index = index - 1
list.__setitem__(self, index, value)
x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list
print(x) # __repr__() inherited from builtin list
x.append('HELLO'); # append() inherited from builtin list
print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
# method. index is 1, but reflects 0!
print (x[4]) # 'HELLO' (index is 4 but reflects 3!
Resultado
['a', 'b', 'c']
a
HELLO
No exemplo acima, definimos uma lista de três itens em Mylist e implicitamente o método __init__ é chamado e quando imprimimos o elemento x, obtemos a lista de três itens (['a', 'b', 'c']). Em seguida, acrescentamos outro elemento a esta lista. Mais tarde, pedimos o índice 1 e o índice 4. Mas se você vir a saída, estamos obtendo o elemento do (índice-1) que pedimos. Como sabemos, a indexação de lista começa em 0, mas aqui a indexação começa em 1 (é por isso que estamos obtendo o primeiro item da lista).
Convenções de Nomenclatura
Nele, veremos os nomes que usaremos para variáveis, especialmente variáveis privadas e convenções usadas por programadores Python em todo o mundo. Embora as variáveis sejam designadas como privadas, não há privacidade no Python e isso por design. Como qualquer outra linguagem bem documentada, Python tem convenções de nomenclatura e estilo que promove, embora não as imponha. Existe um guia de estilo escrito por “Guido van Rossum” the originator of Python, that describe the best practices and use of name and is called PEP8. Here is the link for this, https://www.python.org/dev/peps/pep-0008/
PEP significa proposta de aprimoramento do Python e é uma série de documentação distribuída entre a comunidade Python para discutir as alterações propostas. Por exemplo, é recomendado todos,
- Nomes de módulos - all_lower_case
- Nomes de classes e nomes de exceção - CamelCase
- Nomes globais e locais - all_lower_case
- Funções e nomes de métodos - all_lower_case
- Constantes - ALL_UPPER_CASE
Estas são apenas recomendações, você pode variar se quiser. Mas, como a maioria dos desenvolvedores segue essas recomendações, talvez seu código seja menos legível.
Por que se conformar com a convenção?
Podemos seguir a recomendação PEP que ela nos permite obter,
- Mais familiar para a grande maioria dos desenvolvedores
- Mais claro para a maioria dos leitores de seu código.
- Irá corresponder ao estilo de outros colaboradores que trabalham na mesma base de código.
- Marca de um desenvolvedor de software profissional
- Todo mundo vai aceitar você.
Nomenclatura de variável - 'Pública' e 'Privada'
Em Python, quando estamos lidando com módulos e classes, designamos algumas variáveis ou atributos como privados. Em Python, não existe uma variável de instância “privada” que não pode ser acessada exceto dentro de um objeto. Privado significa simplesmente que eles não devem ser usados pelos usuários do código, em vez disso, eles devem ser usados internamente. Em geral, uma convenção está sendo seguida pela maioria dos desenvolvedores Python, ou seja, um nome prefixado com um sublinhado, por exemplo. _attrval (exemplo abaixo) deve ser tratado como uma parte não pública da API ou de qualquer código Python, seja uma função, um método ou um membro de dados. Abaixo está a convenção de nomenclatura que seguimos,
Atributos ou variáveis públicas (destinados a serem usados pelo importador deste módulo ou usuário desta classe) -regular_lower_case
Atributos ou variáveis privadas (uso interno pelo módulo ou classe) -_single_leading_underscore
Atributos privados que não devem ser subclassificados -__double_leading_underscore
Atributos mágicos -__double_underscores__(use-os, não os crie)
class GetSet(object):
instance_count = 0 # public
__mangled_name = 'no privacy!' # special variable
def __init__(self, value):
self._attrval = value # _attrval is for internal use only
GetSet.instance_count += 1
@property
def var(self):
print('Getting the "var" attribute')
return self._attrval
@var.setter
def var(self, value):
print('setting the "var" attribute')
self._attrval = value
@var.deleter
def var(self):
print('deleting the "var" attribute')
self._attrval = None
cc = GetSet(5)
cc.var = 10 # public name
print(cc._attrval)
print(cc._GetSet__mangled_name)
Resultado
setting the "var" attribute
10
no privacy!