Search code examples
pythonfunctiongeneratorlanguage-features

Can I be warned when I used a generator function by accident


I was working with generator functions and private functions of a class. I am wondering

  1. Why when yielding (which in my one case was by accident) in __someFunc that this function just appears not to be called from within __someGenerator. Also what is the terminology I want to use when referring to these aspects of the language?
  2. Can the python interpreter warn of such instances?

Below is an example snippet of my scenario.

class someClass():
    def __init__(self):
        pass

    #Copy and paste mistake where yield ended up in a regular function
    def __someFunc(self):
        print "hello"
        #yield True #if yielding in this function it isn't called

    def __someGenerator (self):
        for i in range(0, 10):
            self.__someFunc()
            yield True
        yield False

    def someMethod(self):
        func = self.__someGenerator()
        while func.next():
            print "next"

sc = someClass()
sc.someMethod()

I got burned on this and spent some time trying to figure out why a function just wasn't getting called. I finally discovered I was yielding in function I didn't want to in.


Solution

  • I'll try to answer the first of your questions.

    A regular function, when called like this:

    val = func()
    

    executes its inside statements until it ends or a return statement is reached. Then the return value of the function is assigned to val.

    If a compiler recognizes the function to actually be a generator and not a regular function (it does that by looking for yield statements inside the function -- if there's at least one, it's a generator), the scenario when calling it the same way as above has different consequences. Upon calling func(), no code inside the function is executed, and a special <generator> value is assigned to val. Then, the first time you call val.next(), the actual statements of func are being executed until a yield or return is encountered, upon which the execution of the function stops, value yielded is returned and generator waits for another call to val.next().

    That's why, in your example, function __someFunc didn't print "hello" -- its statements were not executed, because you haven't called self.__someFunc().next(), but only self.__someFunc().

    Unfortunately, I'm pretty sure there's no built-in warning mechanism for programming errors like yours.