问题
Is there a way to make len()
work with instance methods without modifying the class?
Example of my problem:
>>> 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()
Note:
- different instances of
A
will have different__len__
methods attached - I cannot change the class
A
回答1:
It is actually possible without modifying the class based on this answer by 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())
If we run the code:
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
As per the last part of the last part of the linked answer, you can add any methods on a per instance basis using inst.specialmethod
once you have used 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
回答2:
No. Python always looks up special methods through the object's class. There are several good reasons for this, one being that repr(A)
should use type(A).__repr__
instead of A.__repr__
, which is intended to handle instances of A
instead of the A
class itself.
If you want different instances of A
to compute their len
differently, consider having __len__
delegate to another method:
class A(object):
def __len__(self):
return self._len()
a = A()
a._len = lambda: 2
回答3:
Special methods such as __len__
(double-underscore or "dunder" methods) must be defined on the class. They won't work if only defined on the instance.
It is possible to define non-dunder methods on an instance. However, you must convert your function to an instance method by adding a wrapper to it, which is how self
gets passed in. (This would normally be done when accessing the method, as a method defined on the class is a descriptor that returns a wrapper.) This can be done as follows:
a.len = (lambda self: 2).__get__(a, type(a))
Combining these ideas, we can write a __len__()
on the class that delegates to a len()
that we can define on the instance:
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
You can actually simplify this in your case because you don't need self
in order to return your constant 2
. So you can just assign a.len = lambda: 2
. However, if you need self
, then you need to make the method wrapper.
回答4:
You have to define special methods at definition time of the class:
class A(object):
def __len__(self):
return self.get_len()
a = A()
a.get_len = lambda: 2
print len(a)
回答5:
You did not specify whether you're using Python 2.x or 3.x, and in this case it depends! In 3.x, your question has already been answered by other people. In 2.x, your question actually has an easy answer: stop deriving from object! (Of course, if you follow this approach, you can't upgrade to 3.x until you find another way to do this, so don't do what I'm suggesting unless you have no plans to upgrade any time soon.)
Type "copyright", "credits" or "license()" for more information.
>>> class A:pass
>>> a=A()
>>> a.__len__ = lambda: 2
>>> len(a)
2
>>>
Enjoy :)
来源:https://stackoverflow.com/questions/36557079/how-to-make-len-work-with-different-methods-on-different-instances-of-a-class