Python orientado a objetos: funciones avanzadas
En esto, veremos algunas de las características avanzadas que proporciona Python.
Sintaxis principal en el diseño de nuestra clase
En esto veremos cómo Python nos permite aprovechar los operadores en nuestras clases. Python es en gran parte objetos y los métodos llaman a objetos y esto continúa incluso cuando está oculto por alguna sintaxis 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']
Entonces, si tenemos que agregar el método mágico __add__ a nuestras propias clases, ¿podríamos hacerlo también? Intentemos hacer eso.
Tenemos una clase llamada Sumlist que tiene un constructor __init__ que toma list como un argumento llamado 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])
Salida
[103, 206, 309, 412, 515]
Pero hay muchos métodos que son administrados internamente por otros métodos mágicos. A continuación hay algunos de ellos,
'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__()
Herencia de tipos integrados
Las clases también pueden heredar de los tipos integrados, esto significa que hereda de cualquier integrado y aprovechar todas las funciones que se encuentran allí.
En el siguiente ejemplo, heredamos del diccionario, pero luego implementamos uno de sus métodos __setitem__. Este (setitem) se invoca cuando establecemos la clave y el valor en el diccionario. Como se trata de un método mágico, se llamará implícitamente.
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]))
Salida
setting a key and value!
setting a key and value!
a = 10
b = 20
Extendamos nuestro ejemplo anterior, a continuación hemos llamado a dos métodos mágicos llamados __getitem__ y __setitem__ mejor invocados cuando tratamos con el í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!
Salida
['a', 'b', 'c']
a
HELLO
En el ejemplo anterior, establecemos una lista de tres elementos en Mylist e implícitamente se llama al método __init__ y cuando imprimimos el elemento x, obtenemos la lista de tres elementos (['a', 'b', 'c']). Luego agregamos otro elemento a esta lista. Más tarde pedimos el índice 1 y el índice 4. Pero si ve el resultado, estamos obteniendo el elemento del (índice-1) que hemos pedido. Como sabemos, la indexación de la lista comienza desde 0, pero aquí la indexación comienza desde 1 (es por eso que obtenemos el primer elemento de la lista).
Convenciones de nombres
En esto, veremos los nombres que usaremos para las variables, especialmente las variables privadas y las convenciones utilizadas por los programadores de Python en todo el mundo. Aunque las variables están designadas como privadas, no hay privacidad en Python y esto por diseño. Como cualquier otro lenguaje bien documentado, Python tiene convenciones de nomenclatura y estilo que promueve aunque no las hace cumplir. Hay una guía de estilo escrita 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 propuesta de mejora de Python y es una serie de documentación que se distribuye entre la comunidad de Python para discutir los cambios propuestos. Por ejemplo se recomienda todo,
- Nombres de módulo: all_lower_case
- Nombres de clases y nombres de excepciones - CamelCase
- Nombres globales y locales - all_lower_case
- Funciones y nombres de métodos: all_lower_case
- Constantes: ALL_UPPER_CASE
Estas son solo la recomendación, puede variar si lo desea. Pero como la mayoría de los desarrolladores siguen estas recomendaciones, me parece que su código es menos legible.
¿Por qué ajustarse a la convención?
Podemos seguir la recomendación de PEP que nos permita obtener,
- Más familiar para la gran mayoría de desarrolladores
- Más claro para la mayoría de los lectores de su código.
- Coincidirá con el estilo de otros contribuyentes que trabajen en la misma base de código.
- Marca de un desarrollador de software profesional
- Todos te aceptarán.
Nomenclatura de variable: 'Pública' y 'Privada'
En Python, cuando se trata de módulos y clases, designamos algunas variables o atributos como privados. En Python, no existe una variable de instancia "Privada" a la que no se pueda acceder excepto dentro de un objeto. Privado simplemente significa que simplemente no están destinados a ser utilizados por los usuarios del código, sino que están destinados a ser utilizados internamente. En general, la mayoría de los desarrolladores de Python siguen una convención, es decir, un nombre precedido por un guión bajo, por ejemplo. _attrval (ejemplo a continuación) debe tratarse como una parte no pública de la API o cualquier código Python, ya sea una función, un método o un miembro de datos. A continuación se muestra la convención de nomenclatura que seguimos,
Atributos o variables públicos (destinados a ser utilizados por el importador de este módulo o el usuario de esta clase) -regular_lower_case
Atributos o variables privados (uso interno del módulo o clase) -_single_leading_underscore
Atributos privados que no deberían ser subclasificados -__double_leading_underscore
Atributos mágicos -__double_underscores__(úsalas, no las crees)
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)
Salida
setting the "var" attribute
10
no privacy!