Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a class that is also a list?

Tags:

python

I want to create a class in Python with various attributes and methods, but for it to inherit the functionality of a list, so that I can append objects to the object itself, not any of its attributes. I want to be able to say 'graph[3]', rather than 'graph.node_list[3]'. Is there a way to do this?

like image 619
jukhamil Avatar asked Jul 22 '15 01:07

jukhamil


4 Answers

All you really need to do is provide a __getitem__

In [1]: class Foo:
   ...:     def __init__(self, *args):
   ...:         self.args = args
   ...:     def __getitem__(self, i):
   ...:         return self.args[i]
   ...:     

In [2]: c = Foo(3,4,5)

In [3]: c[2]
Out[3]: 5

In [4]: c[3]
IndexError: tuple index out of range #traceback removed for brevity

In [5]: for i in c: print(i) #look, ma, I can even use a for-loop!
3
4
5

Addendum: There are other methods you probably want to provide, too. __len__ is definitely one of them. There's a rather long list of magic methods, I'd recommend going through them and picking the ones that make sense.

like image 80
NightShadeQueen Avatar answered Oct 17 '22 03:10

NightShadeQueen


You can just inherit from list:

class Foo(list):
    def __init__(self):
        pass

But subclassing builtin types isn't necessarily a good idea.


collections.abc.Sequence (or since 3.5, typing.Sequence[T]) is the way I would do it.

like image 21
o11c Avatar answered Oct 17 '22 03:10

o11c


You want to add some special methods, such as __getitem__ in the case of lists. Take a look at:

https://docs.python.org/2/reference/datamodel.html#emulating-container-types

like image 1
Edmund Avatar answered Oct 17 '22 02:10

Edmund


Since Python uses duck typing, the primary difference between a list and any other object is the set of methods it exposes. You can easily see the primary attribues (methods and fields) of an object with the dir() function.

>>> [a for a in dir([]) if callable(getattr([], a))]
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__',
'__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__',
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__',
'__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort']

Obviously there's a lot of methods here, and you probably don't care about most of them. But if your goal was to really replicate the behavior of a list, you'd probably want to implement them. Most of the __...__ methods are defined in Python's data model, if you want to look them up.

From that page:

Mutable sequences should provide methods append(), count(), index(), extend(), insert(), pop(), remove(), reverse() and sort(), like Python standard list objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods __add__(), __radd__(), __iadd__(), __mul__(), __rmul__() and __imul__() described below; they should not define other numerical operators. It is recommended that both mappings and sequences implement the __contains__() method to allow efficient use of the in operator; for mappings, in should search the mapping’s keys; for sequences, it should search through the values. It is further recommended that both mappings and sequences implement the __iter__() method to allow efficient iteration through the container; for mappings, __iter__() should be the same as keys(); for sequences, it should iterate through the values.

The feature you're asking about in particular, index lookups, is provided by __getitem__. I would suggest also implementing __contains__() and __iter__() (and maybe __len__()), at a minimum.

like image 1
dimo414 Avatar answered Oct 17 '22 02:10

dimo414