我试图让一个类在某种程度上像一个元组的行为,这个类是属性,所以len(实例)与len(instance.tup),实例相同[] 3]将返回instance.tup [3],等等。这是班级:
class mytup(object):
def __init__(self, a):
self.tup = tuple(a)
def __getattr__(self, nm):
f = types.MethodType(lambda self:getattr(self.tup, nm)(), self, type(self))
f.__func__.func_name = nm
setattr(self, nm, f)
return f
我可以
mt = mytup(range(10))
但如果我尝试:
In [253]: len(mt)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-253-67688b907b8a> in <module>()
----> 1 len(mt)
TypeError: object of type 'mytup' has no len()
实际上我确实有一个__len__,我可以打电话:
In [254]: mt.__len__
Out[254]: <bound method mytup.__len__ of <__main__.mytup object at 0x2e85150>>
In [255]: mt.__len__()
Out[255]: 10
(我甚至将其重命名为__len__)。尽管我可以说,这应该看起来就像我做的那样:
def __len__(self, *a):
return self.tup.__len__(*a)
但是python不会让我len(mt)(或者mt [2]或mt [1:5])。
答案 0 :(得分:2)
新式课程查找&#34;特殊方法&#34; - 以实例的类开头和结尾两个下划线字符,而不是涉及的实例,所以当调用len()
时,它会尝试调用typeof(mt).__len__()
。所以做你想做的事的正确方法是使用collections
模块中的Abstract Base Classes for Containers之一(自Python 3.3开始)
import collections
class MyTuple(collections.Sequence):
def __init__(self, a):
self.tup = tuple(a)
def __len__(self):
return len(self.tup)
def __getitem__(self, index):
return self.tup[index]
def __getattr__(self, nm):
f = types.MethodType(lambda self: getattr(self.tup, nm)(), self, type(self))
f.__func__.func_name = nm
setattr(self, nm, f)
return f
mt = MyTuple(range(10))
print(len(mt)) # -> 10
print(mt[4]) # -> 4
答案 1 :(得分:1)
len
未使用__getattr__
获取__len__
功能 - 它直接调用__len__
。
调用 x.__len__
就像调用getattr(x, '__len__')
一样 - 它将返回x.__len__
方法对象。
len
在幕后工作,因此它可以直接访问此方法,而无需调用__getattr__
帮助程序。
__getattr__
中添加打印语句,以便在调用len
时查看打印内容(提示:无)。 答案 2 :(得分:1)
这是因为你没有希望工作的原因是因为:
setattr(self, nm, f)
不等同于
def __len__(self, *a):
return self.tup.__len__(*a)
在后一种情况下,您的方法是类的属性,因为它是在类范围中定义的。它将是setattr(cls, nm, f)
的等值网。如果您选中MyTup.__dict__
,您会在那里看到它。但是,在前一种情况下,__len__
是实例的属性。所以它将在my_instance.__dict__
。 len
检查类的__len__
方法,但找不到。因此错误。您__getattr__
实际上从未被调用过,即使它被调用,也不允许您使用len
。但可以直接使用an_instanec.__len__
。