Introducción
Varios aspectos del comportamiento de Python pueden personalizarse a través de métodos especiales o llamados "mágicos". Esta sección presenta esa idea. Además, se discuten el acceso dinámico a atributos y los métodos vinculados.
This tutorial is from open-source community. Access the source code
💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí
Varios aspectos del comportamiento de Python pueden personalizarse a través de métodos especiales o llamados "mágicos". Esta sección presenta esa idea. Además, se discuten el acceso dinámico a atributos y los métodos vinculados.
Las clases pueden definir métodos especiales. Estos tienen un significado especial para el intérprete de Python. Siempre están precedidos y seguidos por __
. Por ejemplo __init__
.
class Stock(object):
def __init__(self):
...
def __repr__(self):
...
Hay docenas de métodos especiales, pero solo veremos algunos ejemplos específicos.
Los objetos tienen dos representaciones en forma de cadena.
>>> from datetime import date
>>> d = date(2012, 12, 21)
>>> print(d)
2012-12-21
>>> d
datetime.date(2012, 12, 21)
>>>
La función str()
se utiliza para crear una salida imprimible agradable:
>>> str(d)
'2012-12-21'
>>>
La función repr()
se utiliza para crear una representación más detallada para los programadores.
>>> repr(d)
'datetime.date(2012, 12, 21)'
>>>
Esas funciones, str()
y repr()
, utilizan un par de métodos especiales en la clase para producir la cadena que se va a mostrar.
class Date(object):
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
## Utilizado con `str()`
def __str__(self):
return f'{self.year}-{self.month}-{self.day}'
## Utilizado con `repr()`
def __repr__(self):
return f'Date({self.year},{self.month},{self.day})'
Nota: La convención para __repr__()
es devolver una cadena que, cuando se le pasa a eval()
, volverá a crear el objeto subyacente. Si esto no es posible, se utiliza en su lugar una representación fácil de leer.
Los operadores matemáticos implican llamadas a los siguientes métodos.
a + b a.__add__(b)
a - b a.__sub__(b)
a * b a.__mul__(b)
a / b a.__truediv__(b)
a // b a.__floordiv__(b)
a % b a.__mod__(b)
a << b a.__lshift__(b)
a >> b a.__rshift__(b)
a & b a.__and__(b)
a | b a.__or__(b)
a ^ b a.__xor__(b)
a ** b a.__pow__(b)
-a a.__neg__()
~a a.__invert__()
abs(a) a.__abs__()
Estos son los métodos para implementar contenedores.
len(x) x.__len__()
x[a] x.__getitem__(a)
x[a] = v x.__setitem__(a,v)
del x[a] x.__delitem__(a)
Puedes utilizarlos en tus clases.
class Sequence:
def __len__(self):
...
def __getitem__(self,a):
...
def __setitem__(self,a,v):
...
def __delitem__(self,a):
...
La invocación de un método es un proceso en dos pasos.
.
()
>>> s = stock.Stock('GOOG',100,490.10)
>>> c = s.cost ## Búsqueda
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c() ## Llamada de método
49010.0
>>>
Un método que aún no ha sido invocado por el operador de llamada de función ()
se conoce como un método vinculado. Opera en la instancia en la que se originó.
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s
<Stock object at 0x590d0>
>>> c = s.cost
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()
49010.0
>>>
Los métodos vinculados a menudo son una fuente de errores inadvertidos y no obvios. Por ejemplo:
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> print('Cost : %0.2f' % s.cost)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: float argument required
>>>
O un comportamiento engañoso que es difícil de depurar.
f = open(filename, 'w')
...
f.close ## Ay, no hizo nada en absoluto. `f` todavía está abierto.
En ambos casos, el error se produce por olvidarse de incluir los paréntesis finales. Por ejemplo, s.cost()
o f.close()
.
Existe una forma alternativa de acceder, manipular y administrar atributos.
getattr(obj, 'name') ## Lo mismo que obj.name
setattr(obj, 'name', value) ## Lo mismo que obj.name = value
delattr(obj, 'name') ## Lo mismo que del obj.name
hasattr(obj, 'name') ## Comprueba si el atributo existe
Ejemplo:
if hasattr(obj, 'x'):
x = getattr(obj, 'x'):
else:
x = None
*Nota: getattr()
también tiene un valor predeterminado útil *arg*.
x = getattr(obj, 'x', None)
Modifica el objeto Stock
que definiste en stock.py
de modo que el método __repr__()
produzca una salida más útil. Por ejemplo:
>>> goog = stock.Stock('GOOG', 100, 490.1)
>>> goog
Stock('GOOG', 100, 490.1)
>>>
Observa lo que sucede cuando lees una cartera de acciones y visualizas la lista resultante después de haber hecho estos cambios. Por ejemplo:
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> portfolio
... ver cuál es la salida...
>>>
getattr()
es un mecanismo alternativo para leer atributos. Puede usarse para escribir código extremadamente flexible. Para comenzar, prueba este ejemplo:
>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.1)
>>> columns = ['name','shares']
>>> for colname in columns:
print(colname, '=', getattr(s, colname))
name = GOOG
shares = 100
>>>
Observa detenidamente que los datos de salida se determinan enteramente por los nombres de atributos enumerados en la variable columns
.
En el archivo tableformat.py
, toma esta idea y amplíala en una función generalizada print_table()
que imprima una tabla que muestra atributos especificados por el usuario de una lista de objetos arbitrarios. Al igual que la función print_report()
anterior, print_table()
también debe aceptar una instancia de TableFormatter
para controlar el formato de salida. Aquí es cómo debería funcionar:
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> from tableformat import create_formatter, print_table
>>> formatter = create_formatter('txt')
>>> print_table(portfolio, ['name','shares'], formatter)
name shares
---------- ----------
AA 100
IBM 50
CAT 150
MSFT 200
GE 95
MSFT 50
IBM 100
>>> print_table(portfolio, ['name','shares', 'price'], formatter)
name shares price
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
>>>
¡Felicitaciones! Has completado el laboratorio de Métodos Especiales. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.