让一个班级充当元组

时间:2017-05-24 15:51:17

标签: python

我试图让一个类在某种程度上像一个元组的行为,这个类是属性,所以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])。

3 个答案:

答案 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__