recorrer - diccionarios python
Retornar uno mismo en python (4)
Tengo una clase que representa un objeto. Y tengo un montón de métodos que modifican este estado de objeto sin retorno obvio o, obviamente, sin retorno. En C # declararía todos estos métodos como void
y no vería alternativas. Pero en Python estoy a punto de hacer que todos los métodos se return self
a mí return self
para darme la capacidad de escribir frases impresionantes como esta:
classname().method1().method2().method3()
¿Es este Pythonic, o de otro modo aceptable en Python?
Aquí hay un correo de Guido van Rossum (el autor del lenguaje de programación Python) sobre este tema: https://mail.python.org/pipermail/python-dev/2003-October/038855.html
Me gustaría explicar una vez más por qué estoy tan convencido de que el tipo () no debería devolver "yo".
Esto proviene de un estilo de codificación (popular en varios otros idiomas, creo que especialmente Lisp se deleita en él) donde una serie de efectos secundarios en un solo objeto se puede encadenar así:
x.compress (). chop (y) .sort (z)
que seria lo mismo que
x.compress () x.chop (y) x.sort (z)
Encuentro que el encadenamiento es una amenaza para la legibilidad; requiere que el lector esté familiarizado con cada uno de los métodos. La segunda forma deja en claro que cada una de estas llamadas actúa sobre el mismo objeto, por lo que incluso si no conoce muy bien la clase y sus métodos, puede comprender que la segunda y la tercera llamada se aplican a x (y que todas las llamadas se hacen por sus efectos secundarios, y no a otra cosa.
Me gustaría reservar el encadenamiento para las operaciones que devuelven valores nuevos, como las operaciones de procesamiento de cadenas:
y = x.rstrip ("/ n"). split (":"). lower ()
Hay algunos módulos de biblioteca estándar que fomentan el encadenamiento de llamadas de efectos secundarios (pstat viene a la mente). No debería haber nuevos; pstat se deslizó a través de mi filtro cuando estaba débil.
Aquí hay un ejemplo (tonto) que demuestra un escenario cuando es una buena técnica.
class A:
def __init__(self, x):
self.x = x
def add(self, y):
self.x += y
return self
def multiply(self, y)
self.x *= y
return self
def get(self):
return self.x
a = A(0)
print a.add(5).mulitply(2).get()
En este caso, puede crear un objeto en el que el orden en el que se realizan las operaciones está estrictamente determinado por el orden de la llamada a la función, lo que puede hacer que el código sea más legible (pero también más largo)
Es una excelente idea para las API en las que se está construyendo el estado a través de métodos. SQLAlchemy usa esto con gran efecto, por ejemplo:
>>> from sqlalchemy.orm import aliased
>>> adalias1 = aliased(Address)
>>> adalias2 = aliased(Address)
>>> for username, email1, email2 in /
... session.query(User.name, adalias1.email_address, adalias2.email_address)./
... join(adalias1, User.addresses)./
... join(adalias2, User.addresses)./
... filter(adalias1.email_address==''[email protected]'')./
... filter(adalias2.email_address==''[email protected]''):
... print(username, email1, email2)
Tenga en cuenta que no se devuelve en muchos casos; devolverá un clon del objeto actual con un cierto aspecto alterado. De esta manera puede crear cadenas divergentes basadas en una base compartida; base = instance.method1().method2()
, luego foo = base.method3()
y bar = base.method4()
.
En el ejemplo anterior, el objeto de Query
devuelto por una Query.join()
o Query.filter()
no es la misma instancia, sino una nueva instancia con el filtro o unión aplicada.
Utiliza una clase base Generative
sobre la cual construir; así que en lugar de return self
, el patrón usado es:
def method(self):
clone = self._generate()
clone.foo = ''bar''
return clone
que SQLAlchemy simplificó aún más mediante el uso de un decorador :
def _generative(func):
@wraps(func)
def decorator(self, *args, **kw):
new_self = self._generate()
func(new_self, *args, **kw)
return new_self
return decorator
class FooBar(GenerativeBase):
@_generative
def method(self):
self.foo = ''bar''
Todo lo que tiene que hacer el método @_generative
es hacer las modificaciones en la copia, el decorador se encarga de producir la copia, vincular el método a la copia en lugar del original y devolverlo al autor de la llamada por usted.
Si así lo deseas, puedes usar un decorador aquí. Se destacará si alguien busca en su código para ver la interfaz, y no tiene que return self
explícitamente desde cada función (lo que podría ser molesto si tiene varios puntos de salida).
import functools
def fluent(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
# Assume it''s a method.
self = args[0]
func(*args, **kwargs)
return self
return wrapped
class Foo(object):
@fluent
def bar(self):
print("bar")
@fluent
def baz(self, value):
print("baz: {}".format(value))
foo = Foo()
foo.bar().baz(10)
Huellas dactilares:
bar
baz: 10