如何使len()在不同的类实例上使用不同的方法,而不修改类?

ARF*_*ARF 18 python instance-methods

有没有办法在len()不修改类的情况下使用实例方法?

我的问题的例子:

>>> 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()
Run Code Online (Sandbox Code Playgroud)

注意:

  • 不同的实例A__len__附加不同的方法
  • 我不能改变班级 A

use*_*ica 26

不,Python总是通过对象的类查找特殊方法.有几个很好的理由,一个是repr(A)应该使用type(A).__repr__而不是A.__repr__,它旨在处理实例A而不是A类本身.

如果您希望不同的实例A以不同方式计算它们len,请考虑__len__委托另一个方法:

class A(object):
    def __len__(self):
        return self._len()

a = A()
a._len = lambda: 2
Run Code Online (Sandbox Code Playgroud)

  • 任何名称以"__"开头和结尾的方法都需要这种处理.由于双下划线,这些有时被称为"dunder"方法."肮脏的行为和他们的dunder酋长!" (5认同)
  • 我真的很想看到downvoters的评论? (4认同)
  • 好的答案,考虑到OPs要求让不同的实例使用不同的方法来确定len.虽然这是一个要求超出我的原因... (2认同)

kin*_*all 10

__len__必须在类上定义特殊方法,例如(双下划线或"dunder"方法).如果仅在实例上定义,它们将无法工作.

可以在实例上定义非dunder方法.但是,您必须通过向其添加包装器将您的函数转换为实例方法,这是self传入的方式.(这通常在访问方法时完成,因为在类上定义的方法是返回包装器的描述符.)这可以按如下方式完成:

a.len = (lambda self: 2).__get__(a, type(a))
Run Code Online (Sandbox Code Playgroud)

结合这些想法,我们可以编写一个__len__()代表len()我们可以在实例上定义的类的类:

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
Run Code Online (Sandbox Code Playgroud)

你可以在你的情况下实际简化这个,因为你不需要self为了返回你的常量2.所以你可以分配a.len = lambda: 2.但是,如果需要self,则需要创建方法包装器.


Pad*_*ham 6

实际上,根据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())
Run Code Online (Sandbox Code Playgroud)

如果我们运行代码:

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
Run Code Online (Sandbox Code Playgroud)

根据链接答案的最后一部分的最后一部分,您可以在使用inst.specialmethod后 基于每个实例添加任何方法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
Run Code Online (Sandbox Code Playgroud)


Dan*_*iel 5

您必须在类的定义时定义特殊方法:

class A(object):
    def __len__(self):
        return self.get_len()

a = A()
a.get_len = lambda: 2
print len(a)
Run Code Online (Sandbox Code Playgroud)