Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subclassing and overriding a generator function in python

I need to override a method of a parent class, which is a generator, and am wondering the correct way to do this. Is there anything wrong with the following, or a more efficient way?

class A:
    def gen(self):
        yield 1
        yield 2

class B(A):
    def gen(self):
        yield 3
        for n in super().gen():
            yield n
like image 905
ejm Avatar asked Nov 10 '11 07:11

ejm


People also ask

How do you override Classmethod in Python?

In Python method overriding occurs by simply defining in the child class a method with the same name of a method in the parent class. When you define a method in the object you make this latter able to satisfy that method call, so the implementations of its ancestors do not come in play.

Can you override functions in Python?

Method overriding in Python is when you have two methods with the same name that each perform different tasks. This is an important feature of inheritance in Python. In method overriding, the child class can change its functions that are defined by its ancestral classes.

Can we override init function in Python?

Yes, you must call __init__ for each parent class. The same goes for functions, if you are overriding a function that exists in both parents.

What is difference between generator and function in Python?

Generator Functions are memory efficient, as they save a lot of memory while using generators. A normal function will return a sequence of items, but before giving the result, it creates a sequence in memory and then gives us the result, whereas the generator function produces one output at a time.


3 Answers

For Python 3.3 and up, the best, most general way to do this is:

class A:
    def gen(self):
        yield 1
        yield 2

class B(A):
    def gen(self):
        yield 3
        yield from super().gen()

This uses the new yield from syntax for delegating to a subgenerator. It's better than the other solutions because it's actually handing control to the generator it delegates to; if said generator supports .send and .throw to pass values and exceptions into the generator, then delegation means it actually receives the values; explicitly looping and yielding one by one will receive the values in the gen wrapper, not the generator actually producing the values, and the same problem applies to other solutions like using itertools.chain.

like image 155
ShadowRanger Avatar answered Oct 16 '22 09:10

ShadowRanger


What you have looks fine, but is not the only approach. What's important about a generator function is that it returns an iterable object. Your subclass could thus instead directly create an iterable, for example:

import itertools

class B(A):
    def gen(self):
        return itertools.chain([3], super().gen())

The better approach is going to depend on exactly what you're doing; the above looks needlessly complex, but I wouldn't want to generalize from such a simple example.

like image 20
Michael J. Barber Avatar answered Oct 16 '22 10:10

Michael J. Barber


To call a method from a subclass you need the keyword super.

New Source Code:

class B(A):
    def gen(self):
        yield 3
        for n in super().gen():
            yield n

This:

b = B()
for i in b.gen():
     print(i)

produces the output:

   3
   1
   2

In the first Iteration your generator stops at '3', for the following iterations it just goes on as the superclass normally would.

This Question provides a really good and lengthy explanation of generators, iterators and the yield- keyword: What does the "yield" keyword do in Python?

like image 3
Ria Avatar answered Oct 16 '22 09:10

Ria