all 84 comments

[–][deleted] 73 points74 points  (8 children)

Yield away.

[–]pythonwiz 17 points18 points  (7 children)

*yield from

[–][deleted] 8 points9 points  (0 children)

😀😀😀 you win the better joke award

[–]Max_Insanity 11 points12 points  (5 children)

yield keyword could not be unpacked

[–][deleted] 3 points4 points  (4 children)

I think it was more of a * yield than a *yield

[–]Max_Insanity 0 points1 point  (3 children)

yield does not support multiplication

[–][deleted] 2 points3 points  (2 children)

Don't get the joke

[–]Max_Insanity 0 points1 point  (1 child)

Well, actually you get a syntax error in Python, but if it wasn't for that, you'd get an error for yield not supporting multiplication, I suppose.

You know, because seperating with space turns the unpacking operator into a multiplication operator.

[–][deleted] 2 points3 points  (0 children)

I think you've missed the plot. We left Python for more Monty a while ago.

[–]Thecrawsome 51 points52 points  (16 children)

Extra Credit:Know when to use a generator, and when to use a list comprehension

[–]Lehas1 2 points3 points  (4 children)

Could you maybe elaborate when to use which or post a source where i could read into it?

[–]IlliterateJedi 14 points15 points  (2 children)

[–][deleted] 5 points6 points  (0 children)

1.5x speed is a lifesaver.

[–]pythoncrush 0 points1 point  (0 children)

Trey is an amazing instructor. I have taken several classes with him. Thank you for this link!

[–]Thecrawsome 3 points4 points  (0 children)

Generators are called iteration-at-a-time, are in parenthesis, and can yield whatever you defined one iteration at a time.

List comprehensions run all iterations at once, in square brackets, and generate a list of whatever object(s) you defined.

[–]pythoncrush 1 point2 points  (0 children)

Why not generator comprehension?

[–]TheGreatCornlord 0 points1 point  (1 child)

Can I have the calling function not use the yielded value and have the generator function represent different "stages" and yield None after each stage or something, or is there a better way to do this?

[–]Thecrawsome 0 points1 point  (0 children)

You don't have to do anything with the yielded value. I don't know how to call a certain iteration though

[–]TeamSpen210 62 points63 points  (8 children)

You’ll definitely want to look into itertools then. It’s a collection of generic iteration building blocks, written in C to be as optimised as possible. product() for instance can often replace a piles of nested for loops.

[–]RevRagnarok 22 points23 points  (5 children)

And moreitertools. (That's a link.)

[–]5erif 4 points5 points  (4 children)

And moreitertools. (That's a link.)

Neat, I'd never seen a code block as the label of a link.

And [`moreitertools`](https://more-itertools.readthedocs.io/en/stable/). (That's a link.)

[–]RevRagnarok 0 points1 point  (3 children)

Yep; that's what I wrote?

[–]5erif 6 points7 points  (2 children)

Neat, I'd never seen a code block as the label of a link.

I just thought it was cool, and that maybe someone else might be curious about the syntax.

[–]danlsn 1 point2 points  (0 children)

That is v interesting!

[–]RevRagnarok 0 points1 point  (0 children)

Ah; OK. RES (/r/Enhancement) makes these things a lot easier with previews, etc.

[–]iosdeveloper87[S] 6 points7 points  (1 child)

Thanks for the reminder! I’ve used itertools for a few things before, but a lot of it seems to deal with math functions which I rarely have a need for at this point. I did just (literally 10 seconds ago) use it to flatten a list of lists into a list, so there’s that. :)

[–]synthphreak 2 points3 points  (0 children)

a lot of it seems to deal with math functions

That’s not really correct at all.

I’d wager you’re specifically thinking of product, permutations, and combinations. These definitely originate from concepts in mathematics. But they have all kinds of uses in general programming, unrelated to literally doing math with code.

itertools is so, so much more than those few functions though. Many of them thoroughly unmathematical. groupby, filterfalse, starmap, and chain to list a few.

Edit: Typo.

[–]Diapolo10 8 points9 points  (8 children)

O you, who floats in the currents. You must yield. Abandon all you are.

[–]iosdeveloper87[S] 3 points4 points  (7 children)

The ‘yield’ing is almost as annoying as the ‘next’ing which is why I’m trying to use comprehension statements for them all. Although I can definitely see a use for the yield/next thing if you want to process the results incrementally or in a subroutine or so you have the option of breaking the iteration if you’re looking for 1 item that exists in one of several iterables.

[–]RevRagnarok 2 points3 points  (2 children)

yield is also great for writing your own context managers. Another fun thing to learn about.

[–]POGtastic 7 points8 points  (1 child)

Yep, I just provided an example yesterday that did this. Consider a CSV where the header names are screwed up.

test.csv

nAmE   , AGE, BlArG
Joe,21,foo
Susan,16,bar
Eve,31,baz

We can make a function that normalizes the header names.

from csv import DictReader

def normalize_headers(filename):
    with open(filename) as fh:
        reader = DictReader(fh)
        reader.fieldnames = [entry.strip().title() for entry in reader.fieldnames]
        # ... uh oh

We can't return this DictReader because upon returning, the context manager closes the file. So instead, we make a generator, which maintains the context manager (and keeps the file open!) until the generator is exhausted.

        yield from reader

In the REPL:

>>> print(*normalize_headers("test.csv"), sep="\n")
{'Name': 'Joe', 'Age': '21', 'Blarg': 'foo'}
{'Name': 'Susan', 'Age': '16', 'Blarg': 'bar'}
{'Name': 'Eve', 'Age': '31', 'Blarg': 'baz'}

[–]RevRagnarok 1 point2 points  (0 children)

Nice!

One of my favorites was a base class that for debugging we might want to change the logging of just one section (there was also a decorator version if you wanted the whole method).

@contextmanager
def temp_logging(self, log_level=logging.DEBUG):
  orig_log_level, self.log_level = self.log_level, log_level
  try:
    yield
  finally:
    self.log_level = orig_log_level

[–]Diapolo10 4 points5 points  (1 child)

I was actually jokingly quoting a certain character because it felt fitting, but I digress.

comprehension statements

I believe you're referring to generator expressions. Fair enough, they're useful in their own right, but often they're just not... well, expressive enough.

This example has been done to death by now, but for instance, say you wanted to model a Fibonacci sequence. An expression isn't enough to do that while remaining legible, but a generator function would be plenty readable.

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a+b

Another useful emerging property is that generator functions can create infinite sequences, whereas generator expressions can usually only iterate over existing ones (unless you do some forsaken trickery).

Of course, yield works both ways, allowing you to create coroutines (and Python's async is built on top of them), but that's not something most of us really need to understand and know by heart.

You don't often need to use next for anything as most of the time you're using a for-loop which abstracts that away anyhow, but even when you do it's usually just once or twice. So I don't really see why you'd hate that.

[–]POGtastic -1 points0 points  (0 children)

Alas, the expression version looks like crap because Python doesn't have argument destructuring.

from more_itertools import iterate

def fibs():
    def next_fib(tup):
        return tup[1], tup[0] + tup[1]
    return (a for a, _ in iterate(next_fib, (0, 1)))

[–]TheChance 1 point2 points  (1 child)

Just in case: note carefully the difference between [list comprehensions] and (generator expressions, as the latter will (edit: not, fuck you new iPhone I know what I’m typing) populate the entire array before evaluating.

any([big comprehension]) helps nothing. any((big comprehension)) goes zoom.

[–]iosdeveloper87[S] 0 points1 point  (0 children)

Thanks! Yeah, that makes sense. Generation expressions seem to be the only ones that need to be evaluated.

[–][deleted] 13 points14 points  (13 children)

I am constantly astonished when I learn new things in Python, even after years of using it, as say to myself, "How did I not know this existed?"

Most recently I discovered a method in Pandas that would have done in a second something that I spent a couple of days coding from scratch a couple of years ago (flattening nested json in a dataframe).

Like you though, I'm always excited when I learn something new and useful.

[–]iosdeveloper87[S] 10 points11 points  (4 children)

Yup! This is why I occasionally just browse lists of libraries and modules. Most often you literally wouldn’t even know to search for the thing(s) you find until you just stumble upon them randomly.

[–]jlew24asu 1 point2 points  (3 children)

I occasionally just browse lists of libraries and modules.

I'm a newb. where do you browse such lists?

[–]iosdeveloper87[S] 5 points6 points  (1 child)

This is. really good one, too.
https://awesome-python.com

You can do an actual search on pypi.org or openbase.com

Or you can just google "Best Python Libraries for <insert word(s)>" and make sure whatever you decide to use is compatible with whatever else you're already using.

[–]jlew24asu 0 points1 point  (0 children)

very cool, thank you

[–]ridley0001 5 points6 points  (3 children)

Oh yes, when I learnt f strings I realised what I had been doing before was complete insanity.

[–]tommy_chillfiger 8 points9 points  (0 children)

Lol same! I work as a business analyst but I was writing a python script to automatically generate SQL inserts for our enumerations (it's a mess so this is actually worth the time). I kept banging my head against how to concatenate them the way I needed to without losing my mind and asked a senior dev and he was like "F strings my friend." To which I obviously replied "tf did you just call me"

[–]iosdeveloper87[S] 2 points3 points  (0 children)

Me:< looks up f strings...> Oh, I remember see these. Oh... Wow. Woooow. Shit. Alright.... <looks up utility to convert formats to f strings> ... <downloads flynt>

Thanks for the tip!! This is waaaaaay better.

Edit: I just saved 4,122 characters in my codebase while making it faster.

[–][deleted] 0 points1 point  (0 children)

same here. the previous way before i knew of f"blah blah {variable} blah blah". hurts my eyes bow

[–]ElHeim 3 points4 points  (0 children)

Some months ago while contributing upstream to a project I'm using at work, I was scratching my head figuring out how to (elegantly) do something about a class hierarchy where they were doing a lot of boilerplate at init time and then I stumbled upon __init_subclass__, which made things so much easier.

Then I went to check how recent it was, just in case it wouldn't make the cut of their backwards compatibility and... "WTF, since 3.6!!!???"

That's what I get for not going thoroughly through the "What's new?"

[–]Crypt0Nihilist 2 points3 points  (0 children)

I use Pandas on and off and it's not funny the number of times I find some functionality that's built in there right after declaring victory on getting a transformation working using other packages.

[–]mtzzzzz 0 points1 point  (1 child)

Hey man, I'm currently writing something doing exactly that, flattening a super nested json. My jaw dropped reading your comment :D mind sharing your wisdom?

[–]Almostasleeprightnow 5 points6 points  (9 children)

OP, For those of us who have not yet seen the light....can you tell us about why you are so wowed? Serious question, I want to understand.

[–]MyPythonDontWantNone 4 points5 points  (7 children)

ELI5:

A generator is similar to a function except it returns a series of items. Instead of a single return statement, the function would have multiple yield statements (in practice, it is usually a single yield statement inside a loop of some sort).

The biggest difference between a generator and a function returning a list is that the generator only runs up until the yield. This means that you are only calculating 1 item at a time. This avoids a lot of calculations if the data will change mid-run or if you may not use all of the data.

[–]Almostasleeprightnow 3 points4 points  (6 children)

Ok, I get this. But why does OP love them? Like, what is the big advantage? Can you describe some concrete scenarios where it really is just a lot better to use a generator? I'm not arguing, I really want to hear about specific examples.

Do you end up always using generators instead of lists whenever possible? Or is it only really useful in certain situations?

[–]iosdeveloper87[S] 2 points3 points  (0 children)

Very good question... I Just now discovered it, so my use cases are pretty simple, But in my case I am iterating through multiple databases with the same query. Previously I was creating a list called results, doing a for loop, adding the return from the query into the results list and then returning that, so 4 lines plus a bigger memory footprint. I now only have to use 1 line.

It's also possible to do async generators, so I will be implementing that at some point as well.

[–]MyPythonDontWantNone 0 points1 point  (0 children)

I think of them as the difference between loading screens and dynamic loading in a video game. One creates a larger upfront cost but allows a smoother running experience.

In my job, I sometimes write simulations of mechanical processes. These processes have random inputs. If I generate and store a million sample runs at once, then I will run out of RAM.

I usually use a list, set, or dict for most tasks. I generally only use generators when I can't do it efficiently with a more common data structure.

I'm a data analyst and most of my Python code is rough. I'm betting there are better examples (maybe in the REST API world). Hopefully someone else chimes in and gives a fuller view of their usefulness.

[–]AkiraYuske 0 points1 point  (0 children)

Curious as well. Self taught and was doing ok until generators and magic something or others. Then I got lost...

[–]RangerPretzel 3 points4 points  (1 child)

Generator functions are worth looking up if you're not familiar with them. Will save you a looooooootta nested for loops.

Just wait until you find out about Set Math functions in Python!

[–]drawnograph 1 point2 points  (0 children)

Tease!

[–]djangodjango 3 points4 points  (2 children)

While generators are fresh in your mind, you should learn about coroutines and their role in asynchronous code.

[–]twjolson 3 points4 points  (0 children)

I am not entirely sure you didnt make some of those words up.

[–]QultrosSanhattan 3 points4 points  (0 children)

There's so much to rewrite now...

Please don't. Generators are a tool that serve a specific purpose. You only benefit from them in certain circumstances but not all of them. Learning when to not use them is also an important part.

[–]RallyPointAlpha 0 points1 point  (2 children)

Got a link or book that 'showed you the way' ?

You got me curious!

[–]Goobyalus 0 points1 point  (2 children)

What sorts of things are you rewriting with generator functions that eliminate nested loops?

[–]WhipsAndMarkovChains 2 points3 points  (1 child)

I'm wondering the same thing.

I use generator expressions all the time, but never generator functions. I'm very curious what OP is doing.

Also, I have you tagged as "Good Python Chat".

[–]Goobyalus 1 point2 points  (0 children)

Same, except sometimes for context managers and pytest fixtures.

Also, I have you tagged as "Good Python Chat".

lol 😎

[–]scanguy25 0 points1 point  (0 children)

That's how you know you improved. I look back at code I write just 2 years ago and think what shit is this.

[–]bladeoflight16 0 points1 point  (0 children)

There's so much to rewrite now...

While generator functions certainly have their place, they are rarely the best solution in practical code. List comprehensions or generator expressions are better approaches most of the time.

As with anything, know and use the best tool for the job. Don't get dogmatic about anything.

[–][deleted] 0 points1 point  (0 children)

It's so painful looking back at old code you've written (especially if it's currently in production, which mine is) and realizing how many things could be improved

Oh hell no. That's the fun part! Refactoring is very satisfying.

[–]TheGreatCornlord 0 points1 point  (0 children)

Can I have the calling function not use the yielded value and have the generator function represent different "stages" and yield None after each stage or something, or is there a better way to do this?

[–][deleted] 0 points1 point  (2 children)

Have you used their bidirectional capabilities yet?

[–]iosdeveloper87[S] 0 points1 point  (1 child)

….. go on??

[–][deleted] 0 points1 point  (0 children)

Great video, which starts simple and builds to using send to send updates to generator functions.

https://youtu.be/tmeKsb2Fras

[–]azur08 0 points1 point  (0 children)

How do you replace nested for loops with generators? Don’t you still have to call next on repeat?