8

Possible Duplicate:
The Python yield keyword explained

Okay, I've probably phrased the question badly but this is the situation I have.

I have this line of code in Python 2.7 which I'm trying to understand:

yield (padding_zeros + number_string).encode("ascii")

In this line of code, padding_zeros is a string of a variable number of '0's and number_string is a number in the form of a string which can be any number between 0 to, say 10000.

I'm pretty confident that the .encode("ascii") just converts the output of yield to ascii.

What I'm completely at sea about is what the yield (padding_zeros + number_string) does.

I know it initiates a generator but I've spent a lot of time searching online and reading up on the syntax but I still can't work out what the generator actually does. It doesn't help that this is my first time looking at python (my ultimate aim is to convert this code to C#).

So, basically, please could someone explain to me what this line of code does? Does it just add the two strings together or does it do something a bit more complicated?

For further context, this is the block that that line of code appears in:

for current_length in range(4, max_length + 1):
    for i in range(0, pow(10, current_length)):
        number_string = str(i)
        padding_zeros = "0" * (current_length - len(number_string))
        yield (padding_zeros + number_string).encode("ascii")

(max_length being exactly what it sounds like - a number indicating the maximum length of something)

Thanks in advance for any and all answers (even if they're telling me not to be such a fricking noob) :)

EDIT: Thanks very much for the answers - even though I could only pick one as the best answe they were all very helpful. And thanks for the comments as well - as some of them pointed out, What does the "yield" keyword do in Python? is a very good general guide to yield, generators and iterations even if I didn't find it an answer to my specific situation :)

Community
  • 1
  • 1
GeorgePotter
  • 889
  • 1
  • 10
  • 18
  • Is this Python 2 or Python 3? – Tim Pietzcker Mar 26 '12 at 10:36
  • Hi George, your question has been answered here quite well I feel: http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained – MattH Mar 26 '12 at 10:37
  • See this answer: http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained/231855#231855 – Linus Thiel Mar 26 '12 at 10:37
  • Python 2.7 Sorry, should have made that clear in the question. – GeorgePotter Mar 26 '12 at 10:37
  • Search google for ``introduction python generators``. – codeape Mar 26 '12 at 10:38
  • I'm afraid I've already the answer that MattH and Linus have linked to - my question isn't about iterators in general but about what this specific line does. I know it's a very basic question but I can't work out whether the generator just adds the two strings together or does something more complicated. Sorry. – GeorgePotter Mar 26 '12 at 10:39
  • 2
    Short answer you your question: You're mis-parsing the statement. `yield` yields the expression `(padding_zeros + number_string).encode("ascii")`, which is an ascii string as you have guessed. It probably helps to know that `yield`, like `print` in python 2, doesn't need parens around its argument. – alexis Mar 26 '12 at 10:49
  • Thanks alexis - this isn't actually my code but a solution in python that's been passed to me - so you're saying that I could delete the first pair of parenthesis and the code would still work? – GeorgePotter Mar 26 '12 at 10:55
  • @GeorgePotter: Then you would be adding `padding_zeros` and the "ASCII-encoded" `number_string`. Since that encoding doesn't do anything in this context, it would work, but it's not a good idea. Better drop the parenthese *and* the `.encode()`. – Tim Pietzcker Mar 26 '12 at 11:26
  • Okay, cheers for clarifying that. – GeorgePotter Mar 26 '12 at 11:39
  • Indeed I was *not* saying that. What you see is `.encode` applied to the result of a complex expression (the "sum", i.e. concatenation of two strings). The result is then yielded. If `yield` was a function, this would have been written `yield( (padding+number).encode('ascii') )`, i.e. with more parentheses, not fewer. – alexis Mar 26 '12 at 15:48
  • Ah right, now I understand. Thanks very much :) – GeorgePotter Mar 28 '12 at 14:09

4 Answers4

7

OK, you know about generators, so the yield part needs no explanation. Fine.

So what does that line actually do? Not very much:

It concatenates padding_zeros and number_string and then encodes the result to ASCII. Which in Python 2.7 is a no-op because the string is ASCII to begin with (it only consists of ASCII digits, by definition).

In Python 3, it would be different; here the .encode() would have converted the string to a bytes object. But in Python 2, it doesn't make any sense.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • Thanks - it might be that this code was originally written in Python 3 which would explain why it appears as a no op in Python 2. Can't be 100% certain though. Thanks for your answer and sorry for being so clueless. – GeorgePotter Mar 26 '12 at 11:10
4

yield is like return in a generator.

At the point that the yield is executed, execution of the generator function stops, and the value is returned. The difference is that when the generator is invoked again, execution restarts at the yield statement, and continues until another yield is hit, or an (unhandled) exception is raised, or a return is hit. The return or exception will terminate the generator.

The point of a generator is that one can invoke it as x = next(generator) or x = generator.next(), and each time one will receive the value from the yield inside the generator. Generators are also iterable, so they may be used as the source of a loop: for x in generator: print x.

Like in C#, the . operator invokes the method named on its right on the object appearing on the operator's left. Accordingly, (padding_zeros + number_string).encode("ascii") calls encode on the result of (padding_zeros + number_string).

For the meaning of encode, see here: http://docs.python.org/library/stdtypes.html#str.encode

For the language reference (assuming you are using python 2): http://docs.python.org/reference/index.html

Marcin
  • 48,559
  • 18
  • 128
  • 201
1

In this case yield is used to perform lazy evaluation. The next codes are roughly equivalent:

def f(...):
    for current_length in range(4, max_length + 1):
        for i in range(0, pow(10, current_length)):
            number_string = str(i)
            padding_zeros = "0" * (current_length - len(number_string))
            yield (padding_zeros + number_string).encode("ascii")

result = list(f())

versus

def f(...):
    result = list()
    for current_length in range(4, max_length + 1):
        for i in range(0, pow(10, current_length)):
            number_string = str(i)
            padding_zeros = "0" * (current_length - len(number_string))
            result.append((padding_zeros + number_string).encode("ascii"))
    return result

result = f()

You may just follow the second one in you code translation.

Roman Bodnarchuk
  • 29,461
  • 12
  • 59
  • 75
  • 1
    Except that the second version returns a list, while the generator will yield each element one-by-one. – Marcin Mar 26 '12 at 10:43
  • @Marcin sure. But I don't think that OP cares about that :) – Roman Bodnarchuk Mar 26 '12 at 10:52
  • Well it's nice to understand the differences between the two but this should be particularly helpful when I get round to the conversion (still working on pseudocode for the rest of it). – GeorgePotter Mar 26 '12 at 10:58
0

a generator is a statemachine which implements the iterator interface or __iter__ in python. it will wait after "yield" until you call next() on it.

try this:

def my_gen():
    for current_length in range(4, max_length + 1):
        for i in range(0, pow(10, current_length)):
            number_string = str(i)
            padding_zeros = "0" * (current_length - len(number_string))
            print "generate %s" % i
            yield (padding_zeros + number_string).encode("ascii")

for i in my_gen():
    print "iterate %s" % i
mo.
  • 3,474
  • 1
  • 23
  • 20
  • A generator is not a statemachine -1: http://en.wikipedia.org/wiki/Finite-state_machine. In addition, the primary generator interface is `next`. – Marcin Mar 26 '12 at 10:49
  • i think it is (a quite simple one): every call to next() change its state, which allows it to move forward or to stop. (i edited the second '__iter__' to 'next()') – mo. Mar 26 '12 at 11:22
  • That is not the definition of a statemachine. – Marcin Mar 26 '12 at 11:24
  • my understanding of this may come from .net.where yield generates a statemachine at compile time.i thougt it was similar in python – mo. Mar 26 '12 at 11:45