Say I have a base class:
class Base:
A = False
B = ''
C = ''
def __iter__(self):
yield 'a', self.A
yield 'b', self.B
yield 'c', self.C
And then a class that derives from this base:
class Data(Base):
D = ''
def __iter__(self):
yield 'd', self.D
This ofcourse only creates a dictionary containing { 'd': <value> }
on dict( Data() )
, when instance of data class if converted to dict
type; because as I understand it the derived class __iter__
method is effectively overwriting the base class __iter__
method.
Then I tried to call base class method from the derived class overwritten method like we do in __init__()
function:
def __iter__(self):
super().__iter__()
yield 'd', self.D
But the IDE flagged that as an error. Why does this not work? And how to define the derived iter method to extend on the already existing base class iter method, so that I only have to add yield for the added variables in derived class? Is manually writing out all the yields again in the derived class iter method, which is how for the moment I have implemented it, the only solution? Why?
class Data(Base):
D = ''
def __iter__(self):
yield 'a', self.A
yield 'b', self.B
yield 'c', self.C
yield 'd', self.D
The __iter__() function returns an iterator for the given object (array, set, tuple, etc. or custom objects). It creates an object that can be accessed one element at a time using __next__() function, which generally comes in handy when dealing with loops.
__class__ is an attribute on the object that refers to the class from which the object was created. a. __class__ # Output: <class 'int'> b. __class__ # Output: <class 'float'> After simple data types, let's now understand the type function and __class__ attribute with the help of a user-defined class, Human .
Technically speaking, a Python iterator object must implement two special methods, __iter__() and __next__() , collectively called the iterator protocol. An object is called iterable if we can get an iterator from it. Most built-in containers in Python like: list, tuple, string etc. are iterables.
This won't work because super().__iter__()
is a generator and calling a generator in this context doesn't make sense. What you want to do is iterate over the things returned by that generator and yield them from your __iter__
in Data
:
Python 2:
def __iter__(self):
for i in super().__iter__():
yield i
yield 'd', self.D
But in Python 3 this can be written more succinctly as:
def __iter__(self):
yield from super().__iter__()
yield 'd', self.D
You must delegate to the base class:
In [1]: class Base:
...: A = False
...: B = ''
...: C = ''
...:
...: def __iter__(self):
...: yield 'a', self.A
...: yield 'b', self.B
...: yield 'c', self.C
...:
In [2]: class Data(Base):
...: D = ''
...:
...: def __iter__(self):
...: yield from super().__iter__()
...: yield 'd', self.D
...:
In [3]: print(list(Data()))
[('a', False), ('b', ''), ('c', ''), ('d', '')]
In [4]: print(dict(Data()))
{'c': '', 'b': '', 'd': '', 'a': False}
Python 3 allows the yield from
syntax, in Python 2 use:
class Base(object): # make sure to inherit from object for super to work
A = False
B = ''
C = ''
def __iter__(self):
yield 'a', self.A
yield 'b', self.B
yield 'c', self.C
class Data(Base):
D = ''
def __iter__(self):
for x in super(Data, self).__iter__():
yield x
yield 'd', self.D
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