📜 ⬆️ ⬇️

We write beautiful idiomatic Python


Sometimes it is difficult to find the right and most importantly relevant “best practices” for the language on the Web. The documentation, of course, contains all the necessary information, but it is rather difficult to sort out the necessary thing in the paragraphs of the detailed (and documentation). But I recently smiled at Google search, and I came across very useful “patterns” of Python from one of the core developers - Raymond Hettinger.

Note : All recommendations are given in several ways: first, the “worst” options go, and then the best alternative is offered. Actually for the version of language 2.7, the differences for version 3.3 read in the notes to the specific “pattern”.


Loop through array of numbers

Bad : sometimes they write like that.
for i in [0, 1, 2, 3, 4, 5]: print i**2 

Good : the best, with a generator. But in a 32-bit system, a list of a million numbers will take ~ 32 mb of memory.
 for i in range(6): print i**2 

Excellent: the best option. Unlike the second, xrange returns only one value at a time, and no extra memory is needed to store the entire array.
 for i in xrange(6): print i**2 

Note : In Python 3.3, xrange already in the kernel and is simply called range .
')
List cycle

Bad : often former C programmers write like this.
 colors = ['red', 'green', 'blue', 'yellow'] for i in range(len(colors)): print colors[i] 

Good : the best option.
 colors = ['red', 'green', 'blue', 'yellow'] for color in colors: print color 

But if you need to go through the list back to before?

Bad : again, passed from C makes itself felt:
 colors = ['red', 'green', 'blue', 'yellow'] for i in range(len(colors)-1, -1, -1): print colors[i] 

Good : but in Python they write like this:
 colors = ['red', 'green', 'blue', 'yellow'] for color in reversed(colors): print color 


Cycle list with indexes

Bad, too, as above.
 colors = ['red', 'green', 'blue', 'yellow'] for i in range(len(colors)): print i, '-->', colors[i] 

Good : A more elegant option:
 colors = ['red', 'green', 'blue', 'yellow'] for i, color in enumerate(colors): print i, '-->', color 


Cycle on two lists

Bad, too, as above.
 names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue', 'yellow'] n = min(len(names), len(colors)) for i in range(n): print names[i], '-->', colors[i] 

Good : we make one list of tuples from two lists. The problem is that zip uses more memory than the first option.
 names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue', 'yellow'] for name, color in zip(names, colors): print name, '-->', color 

Excellent : unlike zip , izip uses caching, which helps to significantly save memory.
 names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue', 'yellow'] for name, color in izip(names, colors): print name, '-->', color 

Note : In the Python 3.3 version, izip inscribed in the kernel and is simply called zip .

Sort the list by algorithm

Bad : using the function to compare.
 colors = ['red', 'green', 'blue', 'yellow'] def compare_length(c1, c2): if len(c1) < len(c2): return -1 if len(c1) > len(c2): return 1 return 0 print sorted(colors, cmp=compare_length) 


Good : using key sorting. Uses much less memory.
 colors = ['red', 'green', 'blue', 'yellow'] print sorted(colors, key=len) 

Note : The cmp method has been removed from the Python 3.x core.

Cycle on the keys of the dictionary

The usual way to return keys. With this cycle, an iteration of the dictionary occurs, so it cannot be changed in the process.
 for k in d: print k 

To change the dictionary in a cycle, use the cycle by keys (Example: deleting all keys starting with R ):
 for k in d.keys(): if k.startswith('R'): del d[k] 

In this case, d.keys() makes a copy of the dictionary keys, which allows us to work freely with the original structure.

Loop by keys and dictionary value

Bad : loop on keys and return value on last. Slow way:
 for k in d: print k, '-->', d[k] 

Well : faster to do a loop on the values:
 for k, v in d.items(): print k, '-->', v 

Excellent : But the best and fastest way is to use an iterator:
 for k, v in d.iteritems(): print k, '-->', v 


Combining two lists into one dictionary

Very fast method, only one tuple is used to generate the dictionary.
 names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue'] d = dict(izip(names, colors)) # d    : # {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'} 


Counting Items in the Dictionary

Bad : The usual way:
 colors = ['red', 'green', 'red', 'blue', 'green', 'red'] d = {} for color in colors: if color not in d: d[color] = 0 d[color] += 1 #{'blue': 1, 'green': 2, 'red': 3} 

Good : uses the get() function:
 colors = ['red', 'green', 'red', 'blue', 'green', 'red'] d = {} for color in colors: d[color] = d.get(color, 0) + 1 

Excellent : the most advanced way is to use defaultdict() . But you should know how it works .
 d = defaultdict(int) for color in colors: d[color] += 1 


Grouping list items

Bad : if you need to group the elements of a list by some attribute (in the example, the length of a string), often use this method:
 names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie'] d = {} for name in names: key = len(name) if key not in d: d[key] = [] d[key].append(name) {5: ['roger', 'betty'], 6: ['rachel', 'judith'], 7: ['raymond', 'matthew', 'melissa', 'charlie']} 

Well : but there is a way much more elegant and faster:
 d = defaultdict(list) for name in names: key = len(name) d[key].append(name) 


Total

That's all for today. I hope these trivial, but useful examples will help someone improve their code, as they helped me do it. Their author is Raymond Hettinger ( @raymondh ), Python Core Developer.

UPD : Video of the report at PyCon 2013, from which code samples were taken: http://youtu.be/OSGv2VnC0go (in English), contains a lot of interesting information not transferred to the article, as well as the speaker's humor and charisma.

Source: https://habr.com/ru/post/204476/


All Articles