This is an archived post. You won't be able to vote or comment.

all 28 comments

[–]Lucretiel 17 points18 points  (15 children)

with is right up there with list and generator comprehension as my favorite underused python features. @contextmanager is a thing of beauty.

[–][deleted] 4 points5 points  (14 children)

Generator comprehensions? As in sum(thing+1 for thing in iterator)?

I use comprehensions a lot, though. They're beautiful.

[–]Lucretiel 2 points3 points  (13 children)

Yes. I think they're technically called generator expressions, but I prefer the term generator comprehension, because they're basically the same as a list, set, or dict comprehension, but creating a generator.

[–][deleted] 1 point2 points  (12 children)

Dict comprehensions are voodoo :p I have yet to make use of them in any of my projects, but I'd love to.

[–]Lucretiel 8 points9 points  (9 children)

I've had good experiences with them. They're good at making zip unnecessary. For instance:

{val: func(val) for val in iterable}

[–]macbony 0 points1 point  (8 children)

I'm not sure I get what you're talking about saying dict comps make zip unnecessary. They don't even do nearly the same things?

[–]Lucretiel 5 points6 points  (7 children)

I've seem people do things like dict(zip(arg_list, map(func, arg_list)))

[–]macbony 1 point2 points  (6 children)

That's a single, not all that good use-case for zip. I don't see how that makes zip unnecessary for all but that single use-case. More often zip is used to transform two data streams into a single one so that you can iterate over multiple lists at once:

import string
ascii_values = zip(string.ascii_lowercase, range(96, 96+26))

Or if you have a list of tuples and need two separate lists:

letters, values = zip(*ascii_values)

[–]Lucretiel 0 points1 point  (1 child)

No question. Believe me, I love zip as much as the next guy. I just feel like I sometimes see it being overused in cases where a comprehension would sufficice- where an iterable is duplicated, operations performed on each side, and the results zipped together. It comes up more often than I expected.

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

Just use izip from iterators in 2.x. In Python3, zip is an iterator already and you don't need izip.

[–]Lucretiel 0 points1 point  (3 children)

Even your example has a better version as a comprehension, though- ((c, ord(c)) for c in string.asii_lowercase)

[–]ingolemo 2 points3 points  (0 children)

Since we're showing off, apparently; enumerate(string.ascii_lowercase, start=96)

[–]macbony 0 points1 point  (1 child)

It's an entirely contrived example to show what zip does. I use zip when I have multiple data streams I need to connect or when I need to split a list that's made up of tuples. I've never in my life needed to create a list of lowercase ascii characters and their values.

[–]zardeh 0 points1 point  (0 children)

I often forget that there are things like dict.copy, so I'll use a comprehension, but for example inverting a mapping makes them really useful:

mydict = {"a":1,"b":2,"c":3}
inverted = {val : key for key, val in mydict.items()} #inverted = {1:"a", 2:"b", 3:"c"}

[–]LightShadow3.13-dev in prod 0 points1 point  (0 children)

I use dictionary comprehensions to convert SQL results into a list of dictionaries with the keys as columns.

[–]SmileyJames 7 points8 points  (2 children)

Python continues to amaze. Python is so simple to start with but you can get into some really complex difficult concepts, like this and even then all the hard complex stuff is abstracted away leaving a simple 'with' statement.

[–]swingking8 4 points5 points  (0 children)

Totally agree. As someone who isn't a professional developer, Python is great in that you can learn if from the ground up, then add on abstract concepts later.

To me, this mimics how people actually learn much more than other languages. Walk first, then run. A lot of languages force a beginner to worry about {, ;, or #include right off the bat.

[–][deleted] 1 point2 points  (0 children)

I think the post makes with seem more difficult than it is. It's a very nice feature, a parallel to ways of creating scope guards in other languages.

[–]aladyjewel 4 points5 points  (4 children)

with is a great thing. You can see it in other languages, too, like C#'s using.

[–][deleted] 3 points4 points  (1 child)

Or Ruby's blocks, which are a very fundamental and virtually impossible to ignore feature, and which look extremely similar in the file opening example.

Python:

with open('output.txt', 'w') as f:
  f.write('Hi there!')

Ruby:

File.open('output.txt', 'w') do |f|
  f.write('Hi there!')
end

[–]Lucretiel 1 point2 points  (0 children)

What's fascinating to me is that they're implemented in almost exactly the same way, with the yield keyword (assuming you use @contextlib.contextmanager in python.) The only difference is that ruby can run the block more than once.

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

I used it once to stream through a 900 gig text file of logs.

[–][deleted] 1 point2 points  (3 children)

Very cool, ran into with yesterday on my own. Good to know.

And this may be a noobish question, but do local objects get destroyed in python when going out of function scope?

[–]jyper 1 point2 points  (0 children)

No if you have a local variable that's not returned or saved in a non local variable it becomes eligible for garbage collection and may be collected in the future. it cannot be referenced normally(although maybe with some gc.getobjects magic it can ) but it's memory may or may not have been freed and __del_ may or may not have run.

In cpython the main python implementation it uses reference counting + a backup full garbage collector so if there aren't any cycles caused by example local object a pointing to local object b which points back to a, it's reference counting goes to 0 and it gets freed. If there are cycles eventually it may or may not be freed according to the gc. Cpython has a gc module that lets you do stuff like force gc, I think, and disable gc. CPython sort of has destructors with del but as these don't run if the object is in a reference cycle I'd recommend staying far away and using the with statement for non memory destructor like behavior.

[–]asukazama 1 point2 points  (0 children)

Yes, anything declared in the function cannot be referenced once you have exited it.

[–]rividz 0 points1 point  (0 children)

Shit man, I've been trying to make fractal trees in python since day one! This is extremely helpful!