Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3: How to write a __iter__ method for derived class so that it extends on the behaviour of the base class' __iter__ method

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
like image 810
Sushovan Mandal Avatar asked Jan 22 '18 07:01

Sushovan Mandal


People also ask

What is the __ ITER __ in Python?

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.

What is __ class __ in Python?

__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 .

What are the methods that iterator object must implement?

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.


2 Answers

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
like image 79
Bailey Parker Avatar answered Oct 06 '22 00:10

Bailey Parker


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
like image 39
juanpa.arrivillaga Avatar answered Oct 05 '22 22:10

juanpa.arrivillaga