método de encadenamiento en python
method-chaining (3)
Es posible si solo utiliza funciones puras para que los métodos no modifiquen self.data
directamente, sino que devuelvan la versión modificada. También tienes que devolver las instancias de Chainable
.
Aquí hay un ejemplo que utiliza la recopilación de datos con listas:
import itertools
try:
import builtins
except ImportError:
import __builtin__ as builtins
class Chainable(object):
def __init__(self, data, method=None):
self.data = data
self.method = method
def __getattr__(self, name):
try:
method = getattr(self.data, name)
except AttributeError:
try:
method = getattr(builtins, name)
except AttributeError:
method = getattr(itertools, name)
return Chainable(self.data, method)
def __call__(self, *args, **kwargs):
try:
return Chainable(list(self.method(self.data, *args, **kwargs)))
except TypeError:
return Chainable(list(self.method(args[0], self.data, **kwargs)))
Úsalo así:
chainable_list = Chainable([3, 1, 2, 0])
(chainable_list
.chain([11,8,6,7,9,4,5])
.sorted()
.reversed()
.ifilter(lambda x: x%2)
.islice(3)
.data)
>> [11, 9, 7]
Tenga en cuenta que .chain
refiere a itertools.chain
y no a la chain
OP.
(No debe confundirse con itertools.chain)
Estaba leyendo lo siguiente: http://en.wikipedia.org/wiki/Method_chaining
Mi pregunta es: ¿cuál es la mejor manera de implementar el encadenamiento de métodos en python?
Aquí está mi intento:
class chain():
def __init__(self, my_object):
self.o = my_object
def __getattr__(self, attr):
x = getattr(self.o, attr)
if hasattr(x, ''__call__''):
method = x
return lambda *args: self if method(*args) is None else method(*args)
else:
prop = x
return prop
list_ = chain([1, 2, 3, 0])
print list_.extend([9, 5]).sort().reverse()
"""
C:/Python27/python.exe C:/Users/Robert/PycharmProjects/contests/sof.py
[9, 5, 3, 2, 1, 0]
"""
Un problema es si el method(*args)
llamada method(*args)
modifica self.o
pero no devuelve None
. (entonces debo devolver self
o devolver el method(*args)
devuelve).
¿Alguien tiene mejores formas de implementar el encadenamiento? Probablemente hay muchas maneras de hacerlo.
¿Debo suponer que un método siempre devuelve None
así que siempre self.o
devolver self.o
?
Hay una biblioteca de Pipe
muy útil que puede ser la respuesta a su pregunta. Por ejemplo::
seq = fib() | take_while(lambda x: x < 1000000) /
| where(lambda x: x % 2) /
| select(lambda x: x * x) /
| sum()
No habrá ninguna forma general de permitir que se encadene ningún método de ningún objeto, ya que no puede saber qué tipo de valor devuelve ese método y por qué sin saber cómo funciona ese método en particular. Los métodos pueden devolver None
por cualquier razón; no siempre significa que el método haya modificado el objeto. Del mismo modo, los métodos que sí devuelven un valor aún podrían no devolver un valor que se pueda encadenar. No hay manera de encadenar un método como list.index
: fakeList.index(1).sort()
no puede tener muchas esperanzas de funcionar, porque todo el punto del index
es que devuelve un número, y ese número significa algo, y no se puede ignorar solo para encadenar el objeto original.
Si solo estás jugando con los tipos incorporados de Python para encadenar ciertos métodos específicos (como ordenar y eliminar), es mejor que envuelvas esos métodos en particular explícitamente (anulando en tu clase de envoltorio), en lugar de intentar hacer un Mecanismo general con __getattr__
.