python - privados - ¿Cómo hacer que len() funcione con diferentes métodos en diferentes instancias de una clase, sin modificar la clase?
poo python 3 (5)
¿Hay alguna manera de hacer que len()
funcione con métodos de instancia sin modificar la clase?
Ejemplo de mi problema:
>>> class A(object):
... pass
...
>>> a = A()
>>> a.__len__ = lambda: 2
>>> a.__len__()
2
>>> len(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type ''A'' has no len()
Nota:
- diferentes instancias de
A
tendrán diferentes métodos__len__
adjuntos - No puedo cambiar la clase
A
En realidad, es posible sin modificar la clase según esta answer de Alex Martelli:
class A(object):
def __init__(self, words):
self.words = words
def get_words(self):
return self.words
a = A("I am a")
b = A("I am b")
def make_meth(inst, _cls, meth, lm):
inst.__class__ = type(_cls.__name__, (_cls,), {meth: lm})
make_meth(a, A, "__len__", lambda self: 12)
make_meth(b, A, "__len__", lambda self: 44)
print(len(b))
print(len(a))
print(a.get_words())
print(b.get_words())
Si ejecutamos el código:
In [15]: a = A("I am a")
In [16]: b = A("I am b")
In [17]: make_meth(a, A, "__len__", lambda self: 12)
In [18]: make_meth(b, A, "__len__", lambda self: 44)
In [19]: print(len(b))
44
In [20]: print(len(a))
12
In [21]: print(a.get_words())
I am a
In [22]: print(b.get_words())
I an b
Según la última parte de la última parte de la respuesta vinculada, puede agregar cualquier método en función de cada instancia utilizando el inst.specialmethod
inst.__class__ = type(...
inst.specialmethod
vez que haya usado inst.__class__ = type(...
:
In [34]: b.__class__.__str__ = lambda self: "In b"
In [35]: print(str(a))
<__main__.A object at 0x7f37390ae128>
In [36]: print(str(b))
In b
Métodos especiales como __len__
(doble guión bajo o métodos "dunder") deben definirse en la clase. No funcionarán si solo se definen en la instancia.
Es posible definir métodos no dunder en una instancia. Sin embargo, debe convertir su función en un método de instancia agregándole un envoltorio, que es la forma en que se self
pasa. (Esto normalmente se haría al acceder al método, ya que un método definido en la clase es un descriptor que devuelve un envoltorio.) Esto se puede hacer de la siguiente manera:
a.len = (lambda self: 2).__get__(a, type(a))
Combinando estas ideas, podemos escribir un __len__()
en la clase que delega a un len()
que podemos definir en la instancia:
class A(object):
def __len__(self):
return self.len()
a = A()
a.len = (lambda self: 2).__get__(a, type(a))
print(len(a)) # prints 2
En realidad, puede simplificar esto en su caso porque no necesita self
para devolver su constante 2
. Así que solo puedes asignar a.len = lambda: 2
. Sin embargo, si usted necesita self
, entonces necesita hacer el envoltorio del método.
No especificaste si estás usando Python 2.xo 3.x, ¡y en este caso depende! En 3.x, tu pregunta ya ha sido respondida por otras personas. En 2.x, tu pregunta tiene una respuesta fácil: ¡deja de derivar de un objeto! (Por supuesto, si sigues este enfoque, no puedes actualizar a 3.x hasta que encuentres otra forma de hacerlo, así que no hagas lo que te estoy sugiriendo a menos que no tengas planes de actualizarlo pronto).
Type "copyright", "credits" or "license()" for more information.
>>> class A:pass
>>> a=A()
>>> a.__len__ = lambda: 2
>>> len(a)
2
>>>
Disfrutar :)
No. Python siempre busca métodos especiales a través de la clase del objeto. Hay varias buenas razones para esto, una de las cuales es que repr(A)
debe usar type(A).__repr__
lugar de A.__repr__
, que tiene la intención de manejar instancias de A
lugar de la clase A
Si desea que diferentes instancias de A
calculen su len
diferente, considere tener __len__
delegado a otro método:
class A(object):
def __len__(self):
return self._len()
a = A()
a._len = lambda: 2
Tienes que definir métodos especiales en el momento de la definición de la clase:
class A(object):
def __len__(self):
return self.get_len()
a = A()
a.get_len = lambda: 2
print len(a)