Hacker News new | past | comments | ask | show | jobs | submit login
Python Shortcuts for the Python Beginner (maxburstein.com)
187 points by mburst on Jan 27, 2013 | hide | past | favorite | 53 comments



Another method that I find isn't used enough: dict.setdefault. Instead of doing this:

    if 'key' in d:
        val = d['key']
    else:
        val = d['key'] = 'some default value'
You can do this:

    val = d.setdefault('key', 'some default value')
This is super-useful for configuration in frameworks, for example - it's a one-liner to set a default config value if the user didn't provide one.


I find I use it most in multi-level data structures, not unlike Perl's auto-vivification system:

    logins = {}
    for user, date in getLogins():
      logins.setdefault(user, []).append(date)
Here you login ends up having one key per "user" with a list of "date".


Probably better done with a defaultdict:

  from collections import defaultdict
  logins = defaultdict(list)
  for user, date in getLogins():
    logins[user].append(date)


if you have a lot of them, plain dict may prove faster.


Not likely. Proof?

I'd agree if you'd said "not a lot of key/values," as the setup could supersede the subsequent speedups. However, in that case, the difference is negligible, and we should do what is "pythonic."


Yes, I meant a myriad of very small dicts. In this case a plain built-in is faster.


I think you might have glossed over another very useful "shortcut" not covered in this article, the use of multiple assignments in a single statement.:

    val = d['key'] = 'some default value'


Nice list. I only wish the author warned the readers that using the "get" method to get an item from the dictionary is not 100% equivalent to using try/except.

When using the method, the default value argument will always be evaluated, regardless of whether the dictionary key exists. If the expression that produces the default value is an expensive one or has side effects, the programmer might want to use the try/except variant instead.


Nice tips. One nitpick: the "inline if statement" is handy because it works as an expression, not just as a statement.

A few more useful things:

frozenset: immutable set values. these can be hashed, so you can use them as keys in dicts or put them in sets.

poor-man's transpose via zip:

  >>> x = [('a', 'b'), ('c', 'd')]
  >>> zip(*x)
  [('a', 'c'), ('b', 'd')]
slices can be constructed with the "slice" function and passed around as values:

  >>> f = lambda x, s : x[s]
  >>> first3 = slice(3)
  >>> last3 = slice(-3, None)
  >>> rev = slice(None, None, -1)
  >>> f('hello', first3)
  'hel'
  >>> f('hello', last3)
  'llo'
  >>> f('hello', rev)
  'olleh'


Good list. My only thoughts:

Prefer itertools.izip instead of zip to avoid materializing a new list.

Appending the comma operator to the print statement suppresses the newline character and appends a space instead, which is how print 1, "world" works.

The only tip I'd add is using mapping keys in string formatting operations:

    >>> d = {'cow': 'moo'}
    >>> print 'the cow says %(cow)s' % d
    the cow says moo


In Python 3, zip() fortunately already returns an iterator.


For python beginners i would not mention the print statement. It won't work for python 3.x.

So, beginners: Please use print() instead of print, to save you some hazzle when using python3 :)


Without `from __future__ import print_function`, print doesn't work the same way in Py2k, though.


In 2.7, it does.


Unfortunately not.. atleast not in my 2.7.3 prompt. Only with the __future__ import :(


In 2.6.6 also works.


If you use print() in py2k, you're still using the print statement and group the argument in parentheses. It falls apart, if you try to use more than one argument, because it's interpreted as a tuple then.

Python3:

    >>> print("hello", "world")
    hello world
Python2:

    >>> print("hello", "world")
    ('hello', 'world')


I actually would prefer people on my team NOT use these shortcuts. For example:

  numbers = [1, 2, 3, 4, 5, 6]
  even = []
  for number in numbers:
      if number % 2 == 0:
          even.append(number)
is much easier to read and maintain imho then

  numbers = [1, 2, 3, 4, 5, 6]
  even = [number for number in numbers if number % 2 == 0]
Also:

  for x in range(1,101):print"Fizz"[x%3*4:]+"Buzz"[x%5*4:]or x
is a piece of code I never want someone else to write that I may someday have to maintain. Write it out the long way and make your teams lives easier.


While it's OK for you to enforce not using trivial list-comprehensions on your team, I think you'll find the majority of Python's community disagreeing. Your example is really tame as far as comprehensions are concerned, and "beginners" are going to need these basics to comprehend more advanced patterns.


It's especially tame given the friendly variable name chosen.


There is a fundamental difference between the loop and the comprehension: the latter is far more declarative.

That is, the comprehension is equivalent to saying something like "even contains every number from numbers that is even". The loop is like saying "start with even as the empty list; for each number in numbers, append it to even". It's much easier to understand what even is from the first description.

The for-loop version is much less direct and has too many low-level details--why are you appending to the list and using extra state? From the second definition, you can know what even is just by looking at it; for the first one, you have to think about what the code is doing.

This is the fundamental delineation between imperative and declarative code. The former is about how and the latter is about what. In general, specifying the what is much simpler--and therefore easier to write, easier to work with and easier to maintain--than specifying how to get it.

I suspect you find the for-loop version easier not because it's simpler but because that's what you're used to. And while familiarity is certainly an important factor, it's always relative and temporary: every person is familiar with different things, and you get more familiar with different constructs over time.

Rich Hickey's "Simple Made Easy"[1] talk is a great description of this whole idea. He makes the matter far clearer than I have any hope of doing.

[1]: http://www.infoq.com/presentations/Simple-Made-Easy


There is a fundamental difference between the loop and the comprehension: the latter is far more declarative.

I completely agree with this, and the wider fundamental point throughout your post.

However, I’m not a fan of comprehension syntax. It often gets noisy even for trivial cases like this one: the letters “number” appear four times in just a single line here, which as it turns out is just as much accidental complexity as using the explicit loop control variable in the imperative version and you’ve lost the visual cues to what each means that the indentation gives with the loop. For more complicated transformations, I find comprehension syntax also scales poorly.

I suspect (though I’ve no hard data to back it up) that comprehension syntax actually isn’t very readable in many cases, and that this may be why some people prefer the kind of code in the imperative example rather than any innate preference for imperative style per se. Personally, I’d prefer to highlight the data flow but using more explicit transformations instead, such as (in a hypothetical functional programming syntax):

    evens = filter {_ % 2 == 0} numbers
Python’s own syntax for this isn’t quite as neat as some functional programming languages, IMHO, but I still prefer it to the comprehension:

    numbers = [1, 2, 3, 4, 5, 6]
    evens = filter(lambda n: n % 2 == 0, numbers)


To be fair, we should be comparing like tokens, if the proof is using them as part of its advantage:

  evens = filter(lambda n: n % 2 == 0, numbers)
with

  evens = [n for n in numbers if n % 2 == 0]
or

  evens = [n for n in numbers if not n % 2]
Choosing token names is certainly a different


Fair comment. I suppose that’s why a more declarative style tends to work much better in a language designed for it. For example, functional programming languages tend to have neat syntactic sugar for things like simple lambda expressions or applying a series of transformations like the filter here, without introducing accidental complexity like extra identifiers that the reader than has to keep track of. The moment you’ve added that kind of baggage, which is almost inevitable to some degree in a language like Python, the clarity tends to suffer.


Slicing a list is not the same as taking subsets, the examples he provides are also misleading in this context.


Great post! For those who know Python, this may be common knowledge, but for the uninitiated, this is a great showcase of some simple ways that Python makes coding more intuitive and enjoyable.

You might also consider mentioning both dictionary and set comprehensions along with list comprehensions, as they are the exact same concept, yet even a lot of intermediate Python programmers don't know about them!


Except the silly 'False = True' at the end. How's that useful?


I think it's just demonstrating how its possible to redefine even Python keywords.


Thanks for the idea! I added a dictionary comprehension section.


The last one is terrible ... I have seen

#define TRUE 0 #define FALSE 1

back in the day.

Too bad there was no source control in the company then, so I could not find out who was responsible for that travesty and "talk calmly" some sense into them.


Processes (and a common design for c functions) return zero on success and an error code otherwise. Although it might be unexpected (and certainly breaks flow constructs), it's probably not so bad with some context.

Worse, though, Windows defines TRUE and FALSE for you somewhere in windef.h - so you would have to be careful about where your code was included..


Shouldn't "the last 3" be [-3:] instead of [3:] ? Inyour case it gives the correct result, but that's because your list has 6 elements i think. ( note : typing on my phone so i can't verify).



Could it be possible to explain what does

    Print "Fizz" [x%3*4::]
Since range is going from 0 to 101 I have the impression the result won't match the directive.


"fizz" is a string - treated as a list in python

"fizz"[0] is the zeroth character in the list / string

Note "fizz"[:] will output all chars in the string (implicitly it's [0:] - from zeroth string to end

X % 3 is x modulo 3, producing 0 only if x is divisible by three - so we want to print fizz here. 4 is the number of chars in both fizz and buzz - so the meat is

"fizz"[x%3*4:] is "fizz"[0:] if x%3 is 0 - when we want tp print fizz, otherwise it's [somevaluegreaterthan4: to the end of the list (ie nothing). The extra : stops index error


Now I understand it. I wasn't aware one could append a subscript operator after a litteral string. I also didn't know of the second : role. Thank you.

But what do you get if x is 0 ? As I now uneerstand it, you would get "fizzbuzz". Shouldn't the list start at value 1 ? So shouldn't the range be (1, 101) instead of (101) ?


You're correct. That was my mistake. Here's the updated version:

for x in range(1,101):print"Fizz"[x%34:]+"Buzz"[x%54:]or x

I basically just removed the extra colons and added 1, to the range so that it starts at 1. lifeisstillgood was spot on with the reasoning that this works.


I don't see any difference between a single : and double :: in this case.

The second : might be included to make the slicing more visible.


A second : may be used to define a step value. In this case it's not used, so it's superfluous.


It prints a substring from "Fizz" using slicing. The thing is, the x%3*4 determines the start of that slice. It's either 0, which is when X modulo 3 == 0 (which is every third number in range(101) ), or something larger than 4, in which case the slice would start at the end of "Fizz" thus yielding an empty string.


nice list, agree with the other comments that the Fizzbuzz example is cool, but can be confusing to work out what it does, especially since this post is aimed at beginners.

I would also mention `getattr` similarly to `get`.


It's confusing that output is on the >>> lines while input is not -- backwards from the REPL.

More things to consider: dict.iteritems, collections.defaultdict, enumerate(seq, 1)


Somethings's up with your site. All I see are black boxes where your examples should be: http://imgur.com/N8vdzGX


You need to allow some JS from other domains for it to work.


Maybe it's your browser.


Expression `False = True` reminds me of http://stackoverflow.com/a/771974/458610.


Are inline if statements considered good form? I rarely see them in use and I've heard people complain about them before.


Very good list except the fizzbuzz example. It solves the problem but not easy to read, which is not python way.


Also good read for seasoned pythonistas, nice work, mburst


you should always check the length of lists before zip, otherwise you end up with surprises (try running zip with unequal lengths)


I didn't know about some of these. Nice!


Sharing this with my AS Computer Science students. Very clear explanations.


The article requires some corrections if presented to beginners.

`something` should be written as repr(something). `` (backticks) are gone in Python 3.

if 0 < x > 1: is syntactically correct but it should be written as: if x > 1. The combining syntax could be used for the same direction e.g., if -1 < x < 1.

Code golf is not the strong side of Python and it is not the point of FizzBuzz. Code golf might demonstrate language features but it doesn't provide the correct context for their usage i.e., code golf doesn't show to a beginner how something should be used correctly.

True/False are keywords Python 3 (you can't assign to them).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: