Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a class to act as a tuple

Tags:

python

I'm trying to have a class act in every way like a tuple that's an attribute of the class, so len(instance) would be the same as len(instance.tup), instance[3] would return instance.tup[3], etc. Here's the class:

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

I can

mt = mytup(range(10))

But if I try to:

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()

mt does in fact have a __len__ that I can call:

In [254]: mt.__len__
Out[254]: <bound method mytup.__len__ of <__main__.mytup object at 0x2e85150>>
In [255]: mt.__len__()
Out[255]: 10

(I even renamed it __len__). As near as I can tell, this should look just as if I did:

def __len__(self, *a):
    return self.tup.__len__(*a)

But python won't let me len(mt) (or mt[2] or mt [1:5] for that matter).

like image 757
mlv Avatar asked Oct 18 '22 11:10

mlv


1 Answers

New-style classes look-up "special methods"—those that start and end with two underscore characters—on an instance's class not the instance involved, so when len() is called it tries to call typeof(mt).__len__(). So the proper way to do what you want would be to use one of the Abstract Base Classes for Containers in the collections module (since Python 3.3)

import collections.abc

class MyTuple(collections.abc.Sequence):
    def __init__(self, a):
        self.tup = tuple(a)

    def __len__(self):
        return len(self.tup)

    def __getitem__(self, index):
        return self.tup[index]

mt = MyTuple(range(10))
print(len(mt))  # -> 10
print(mt[4])  # -> 4
like image 141
martineau Avatar answered Oct 20 '22 09:10

martineau