I am trying to have a list returned when I call list()
on a class. What's the best way to do this.
class Test():
def __init__(self):
self.data = [1,2,3]
def aslist(self):
return self.data
a = Test()
list(a)
[1,2,3]
I want when list(a)
is called for it to run the aslist
function and ideally I'd like to implement asdict
that works when dict()
is called
I'd like to be able to do this with dict
, int
and all other type casts
Method 2: Another way of finding a list of attributes is by using the module inspect . This module provides a method called getmemebers() that returns a list of class attributes and methods. Method 3: To find attributes we can also use magic method __dict__ . This method only returns instance attributes.
To be more concrete, list is a class object (remember that “class” and “type” are synonymous) - it is the same sort of object that is produced when a class definition is executed.
To call a class method, put the class as the first argument. Class methods can be can be called from instances and from the class itself. All of these use the same method. The method can use the classes variables and methods.
Printing a list in python can be done is following ways: Using for loop : Traverse from 0 to len(list) and print all elements of the list one by one using a for loop, this is the standard practice of doing it.
Unlike many other languages you might be used to (e.g., C++), Python doesn't have any notion of "type casts" or "conversion operators" or anything like that.
Instead, Python types' constructors are generally written to some more generic (duck-typed) protocol.
The first thing to do is to go to the documentation for whichever constructor you care about and see what it wants. Start in Builtin Functions, even if most of them will link you to an entry in Builtin Types.
Many of them will link to an entry for the relevant special method in the Data Model chapter.
For example, int
says:
… If x defines
__int__()
,int(x)
returnsx.__int__()
. If x defines__trunc__()
, it returnsx.__trunc__()
…
You can then follow the link to __int__
, although in this case there's not much extra information:
Called to implement the built-in functions complex(), int() and float(). Should return a value of the appropriate type.
So, you want to define an __int__
method, and it should return an int
:
class MySpecialZero:
def __int__(self):
return 0
The sequence and set types (like list
, tuple
, set
, frozenset
) are a bit more complicated. They all want an iterable:
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as
list
,str
, andtuple
) and some non-sequence types likedict
, file objects, and objects of any classes you define with an__iter__()
method or with a__getitem__()
method that implements Sequence semantics.
This is explained a bit better under the iter
function, which may not be the most obvious place to look:
… object must be a collection object which supports the iteration protocol (the
__iter__()
method), or it must support the sequence protocol (the__getitem__()
method with integer arguments starting at 0) …
And under __iter__
in the Data Model:
This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container. For mappings, it should iterate over the keys of the container.
Iterator objects also need to implement this method; they are required to return themselves. For more information on iterator objects, see Iterator Types.
So, for your example, you want to be an object that iterates over the elements of self.data
, which means you want an __iter__
method that returns an iterator over those elements. The easiest way to do that is to just call iter
on self.data
—or, if you want that aslist
method for other reasons, maybe call iter
on what that method returns:
class Test():
def __init__(self):
self.data = [1,2,3]
def aslist(self):
return self.data
def __iter__(self):
return iter(self.aslist())
Notice that, as Edward Minnix explained, Iterator and Iterable are separate things. An Iterable is something that can produce an Iterator when you call its __iter__
method. All Iterators are Iterables (they produce themselves), but many Iterables are not Iterators (Sequences like list
, for example).
dict
(and OrderedDict
, etc.) is also a bit complicated. Check the docs, and you'll see that it wants either a mapping (that is, something like a dict
) or an iterable of key-value pairs (those pairs themselves being iterables). In this case, unless you're implementing a full mapping, you probably want the fallback:
class Dictable:
def __init__(self):
self.names, self.values = ['a', 'b', 'c'], [1, 2, 3]
def __iter__(self):
return zip(self.names, self.values)
Almost everything else is easy, like int
—but notice that str
, bytes
, and bytearray
are sequences.
Meanwhile, if you want your object to be convertible to an int
or to a list
or to a set
, you might want it to also act a lot like one in other ways. If that's the case, look at collections.abc
and numbers
, which not provide helpers that are not only abstract base classes (used if you need to check whether some type meets some protocol), but also mixins (used to help you implement the protocol).
For example, a full Sequence
is expected to provide most of the same methods as a tuple
—about 7 of them—but if you use the mixin, you only need to define 2 yourself:
class MySeq(collections.abc.Sequence):
def __init__(self, iterable):
self.data = tuple(iterable)
def __getitem__(self, idx):
return self.data[idx]
def __len__(self):
return len(self.data)
Now you can use a MySeq
almost anywhere you could use a tuple
—including constructing a list
from it, of course.
For some types, like MutableSequence
, the shortcuts help even more—you get 17 methods for the price of 5.
If you want the same object to be list-able and dict-able… well, then you run into a limitation of the design. list
wants an iterable. dict
wants an iterable of pairs, or a mapping—which is a kind of iterable. So, rather than infinite choices, you only really have two:
__getitem__
with those keys for dict
, so list
gives a list of those keys.dict
, so list
gives a list of those key-value pairs.Obviously if you want to actually act like a Mapping
, you only have one choice, the first one.
The fact that the sequence and mapping protocols overlap has been part of Python from the beginning, inherent in the fact that you can use the []
operator on both of them, and has been retained with every major change since, even though it's made other features (like the whole ABC model) more complicated. I don't know if anyone's ever given a reason, but presumably it's similar to the reason for the extended-slicing design. In other words, making dicts and other mappings a lot easier and more readable to use is worth the cost of making them a little more complicated and less flexible to implement.
This can be done with overloading special methods. You will need to define the __iter__
method for your class, making it iterable. This means anything expecting an iterable (like most collections constructors like list
, set
, etc.) will then work with your object.
class Test:
...
def __iter__(self):
return iter(self.data)
Note: You will need to wrap the returned object with iter()
so that it is an iterator (there is a difference between iterable and iterator). A list is iterable (can be iterated over), but not an iterator (supports __next__
, raises StopIteration
when done etc.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With