Python Idioms [pdf]
safehammad.com
safehammad.com
To me personally, testing for 'truthy' and 'falsy' values, or relying on exceptions rather than checking values in advance, feels like sloppy and imprecise programming.
A string being empty or not, or an array having items or not, or a boolean being true or false, are all qualitatively totally different things to me -- and just because Python can treat them the same, doesn't mean a programmer should take advantage of that fact. Sometimes it's possible to over-simplify things in a way that obfuscates instead of clarifying.
When I read:
if name and pets and owners
I have no intuitive idea of what that means, of what's going on in the program. When I read if name != '' and len(pets) > 0 and owners != {}
I understand it exactly.But by this point, I've come to understand that, for a lot of people, it seems to be the opposite. It seems to be more of a philosophical difference, not right/wrong.
name = None
instead of name = ''
Then the second conditional would fail, whereas the first would still be fine. name is None
since None is a unique distinct valueedit: didn't notice at first that you wrote an assignment, not a comparison
The reason for this is that 'is' tests to see if both operands are the same object; it's not a test of equivalence on any level.
There is only ever one instance of None, and you simply get or create references to it. Likewise, there is only one instance of integers up to 256 (presumably a performance optimization for small loops, indexes, etc.). Integers after 256 get separate instances each time, so 'is' fails after that point.
A lot of newcomers to Python come across the 'is None' construct and make assumptions about what 'is' does; the worst part is that they're often correct, and the behaviour becomes difficult to track down when it changes.
None and '' are two completely different things, and could mean different thing sin the context of the application. It's quite likely that choosing either is a conscious choice of the programmer.
If you are using truthy/falsey values, I think it can be a code smell that you are not doing enough to catch invalid values up front or should normalize the values closer to their creation point.
if something:
do_this()
elif something_else:
do_that()
else:
# hope we don't end up here
raise UserWarning('We shouldn't have ended up here') if something==True:
do_this()
elif something==False:
do_that()
else:
raise UserWarning("Shouldn't be here")
Which is a code smell to me. What I would do is: assert isinstance(something, boolean), "Shouldn't happen"
do_this() if something else do_that()
(replace assert with something else if you want it not to be optimized away with -O; assert is a debug-only construct in Python. Or just drop the assert altogether. In most places, I would - there's no end to the amount of validation you could do, and most of it is unnecessary)Relying on implicit conversions is just sloppy. What if that variable was never supposed to be None in the first place. Better with an exception than continuing with corrupt data.
Remember another python motto: Explicit is better than implicit.
`len` blows up on None, so this blows up completely instead of just failing.
> Relying on implicit conversions is just sloppy.
There is no implicit conversion. Truthiness is a protocol, it does not convert anything anywhere.
That's the whole point! As I said, Better with an exception than continuing with corrupt data. Or maybe I should say unsupported data type rather than corrupt data.
It's entirely possible that upstream code is content with any type as long as it's coercible. A lot of Python code just wants an iterable, for example, or something that can be coerced to a string.
It's definitely possible to have a situation where you need a string specifically (or an integer specifically), but it's generally better to have your code coerce the data whenever possible so as to allow for more logical code.
e.g. 's.send(str(object))' (if object has a relevant __str__()) makes perfect sense in code. You don't necessarily need a string, you just need something that can behave as a string in a sensible manner.
You also end up with scenarios where None is a 'valid value' in e.g. the SQL sense of 'value not specified'; for example, company_name might be '' (empty string provided) or None (no value provided); either way company_name is Falsey, and your logic should work the same.
While this is true, I think it's important to keep in mind that a big benefit of being able to handle a situation programatically in different ways is that you, as a programmer, are more able to accurately describe intent.
For example, I think it's foolish to always catch exceptions instead of checking beforehand (look-before-you-leap). However, being able to do either allows me to more accurately represent the intent of a statement. Blindly following rules is almost always sure to lead to poor decisions.
If I'm retrieving an element from an array and I know that for the majority of possible application states there will be an element there, I can just access it, and handle an exception (because it's an "exceptional" case). However if I'm expecting that it could go either way in typical circumstances, then I might more explicitly check the index in the array to illustrate my intent that yes, there are two distinct paths here and that is how the program's control flows naturally.
Of course all of these things are only worthwhile if the reader can see and interpret them as purposeful decisions rather than coincidence. Conveying that is something else entirely.
Exactly. It's philosophically different from other languages, but it's standard in Python.
> if name and pets and owners
>I have no intuitive idea of what that means, of what's going on in the program.
I agree with you, when I read that I think if name and pets and owners, what? All equal each other? Seems like an unfinished statement.
My response to this is, why do you care what their types are? What the statement is saying, conceptually, is "if there's a name and there's pets and there's owners, do this". Why should I have to explicitly tell the language how to tell whether there's a name and there's pets and there's owners, when it already knows that? To me that's just extra verbosity for no good reason. Do you really care that name is a string but pets and owners are containers? (And even if you do, isn't that evident from the variable names anyway? Good naming conventions can do a lot of the work of clarifying what's going on while keeping the code compact.)
if name != '' and len(pets) > 0 and owners != {}
would tell you that name is a non-empty string, pets has values, and owners is a non-empty dict (except it doesn't work, as pdonis noticed).But Python allows immoral implicit conversions, so that's not what that line means! If name is a function, pets is the value '7' and owners is a list, the test passes.
if name and pets and owners
Would pass as well, but it has the advantage of not implying it does more than it does: all you can infer from the test passing is that none of name,pets,owners are special falsy values.If you actually wanted to test what the longer line is implying, you'd write something like
if isinstance(name, str) and isinstance(pets, list) and isinstance(owners, set) and name and pets and owners
(don't do this, it violates duck-typing and LBYL)No, it doesn't tell you that. {} denotes an empty dict, not an empty set; and an empty set will return True for owners != {}, not False. As I noted in another post upthread, you would need to write
len(owners) > 0
to get the correct semantics, making owners indistinguishable from pets even if they are different container types. If you really wanted to make all the types clear, you would need to include the isinstance tests.Really? Containers of different types have a len method; which type of container is pets? The line you wrote doesn't tell you. And is owners supposed to be a set? If so, your comparison to an empty dict will give the wrong semantics if owners is an empty set (owners != {} will return True for an empty set, not False). You would have to write
len(owners) > 0
to get the correct semantics. Which, of course, already obscures the type of owners, just as the type of pets is obscured.In short, your suggested "improvement" over idiomatic Python still obfuscates rather than clarifying.
E.g. something like:
try:
something = myfunc(d['x'])
except ValueError:
something = None
The programmer's intent is probably to only catch errors in the key lookup d['x'], but if there is some bug in the implementation of myfunc() or any of the functions called by myfunc() which causes a ValueError to unintentionally be raised, it will be caught by the except.For dictionary lookups specifically, get() is usually preferable:
something = d.get('x')
if something is not None:
something = myfunc(something)
Or if dictionary may potentially contain None values: if 'x' in d:
something = myfunc(d['x']) try:
open(FILE)
except IOError:
it failed something = d.get('x', default) something = myfunc(d['x']) if 'x' in d else NoneIn any event don't optimize prematurely and use a profiler rather than guessing if performance is an issue. ;-)
Additionally, for all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs.
Yes:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
No: try:
# Too broad!
return handle_value(collection[key])
except KeyError:
# Will also catch KeyError raised by handle_value()
return key_not_found(key)When your try block is a single lookup, you might as well use an if statement or get. However, when the 'absolute minimum' is nontrivial try/except is still a good option, e.g.
try:
name = employee['name']
first_name = name['first_name']
last_name = name['last_name']
except KeyError:
print "Bad employee data"
return'_' is often aliased as gettext to ease translation of string:
from django.utils.translation import ugettext as _
translated_str = _('Something to translate')
so using it will overwrite the alias. Instead, you can use '__' (double underscore) as ncoghlan suggests below his answer [1].
or you can use the 'unused_' prefix as Google Python Style Guide suggests [2]
or you can change your code, so you don't need to use '_' as Alex Martelli suggests in his answer [3].[1]: http://stackoverflow.com/a/5893946/720077
[2]: http://google-styleguide.googlecode.com/svn/trunk/pyguide.ht...
Aliasing it to "t" or "txl" seems like a saner way, if I'm honest.
[1]: http://stackoverflow.com/a/2745537/720077
http://stackoverflow.com/a/1739541/720077
http://developer.plone.org/i18n/internationalisation.html#ma...
Quoting from http://www.python.org/workshops/1997-10/proceedings/loewis.h... :
> Here are several ways of producing the message catalogs. This is the one suggested by the GNU gettext documentation. First, all translatable message strings in the source code must be marked. In order to disturb readability as little as possible, the wrapper function around each string should be called _ (underscore). This use of the underscore usually does not interfere with its meaning in the interactive mode. A gettextized module would then begin with
import intl
_=intl.gettextNot very intuitive.
with open("x.txt") as f:
data = f.read()
# do something with data1) If you are in the mindset of "I want a single file which is both a library and a program," how will you name it? Files to import must end with ".py" and follow C-style name rules, so cannot start with a number cannot contain hyphens. This leads many people to break conventions when naming their programs, because on Unix, hyphens are conventional in program names but underscores are not (count the examples of each in /usr/bin on any system). And naming your program something like "2to3" is impractical if you want it to be importable also.
2) It is unclear where to install files which are both programs and libraries. Programs usually go in some sort of "bin" directory (again, on Unix systems), but libraries do not. Where do you put a file which is both?
3) Sometimes the __name__ == '__main__' trick is used to include unit tests within a module. That's not bad, but consider using Python's excellent doctest feature instead, which often serves the same need but in a richer way.
I don't know if this is a generally applicable technique, but a lot of my modules interact with hardware or physical measurements, so I have to "see" the results in order to believe that the units are working. Often, what I'm looking for is problems with what's actually being measured, and the effect of changing operating conditions, not just my own copious programming bugs.
For this reason, my unit tests can be pretty elaborate, with GUI, graphs, and other stuff. The unit test also functions as a "demo" of the module.
Unfortunately, for every good use of this trick, there seem to be two poor ones. Oh well. Most of the things in TFA are more generally applicable.
In 2.7+, I'd recommend a dictionary comprehension instead.
{k: v for k, v in zip(keys, values)}
edit:As I mention below, this becomes useful when you want to do e.g.
{f(k): g(v) for k, v in zip(keys, values)} dict(zip(keys, value))
is more concise, doesn't introduce extra variables, and doesn't repeat itself, and explicitly names a dict rather than using a symbol. {k.upper(): v ** 2 for k, v in zip(keys, values)}
Or generally {f(k): g(v) for k, v in zip(keys, values)}
I find this very extensible. In terms of conciseness, they both fit on a single line and I like the whitespace inside a dict comprehension.In particular:
>>> 0 == False
True
Which makes the idiom of testing truthiness quite annoying in parsing code such as: def is_digit_char(s):
""" Return a parsed integer from 's'."""
try:
return int(s)
except (ValueError, TypeError):
return None
Which is harmless enough except that as a predicate it sucks because parsing "0" will return False in a context where I'd rather know whether I parsed an integer or not... which leads to non-idiomatic code.This is mainly because True/False are essentially aliases for 1/0 and as such won't identify so:
>>> 0 is False
False
>>> 0 is 0
True
So it's important to remember another Tim Peters-ism: Although practicality beats purity. As read in the Zen of Python it seems he's referring to special cases which this might be.As a shameless aside, you should see what we're working on in Hy. There will likely come a point where we'll be able to do kibit-style suggestions of idiomatic transformations to your Python code.
Update: I ran into this while trying to write token parsers for a parser-combinator lib in Hy.
class I(int):
def __nonzero__(self):
return True
def is_digit_char(s):
try:
return I(s)
except (ValueError, TypeError):
return None
>>> if is_digit_char('0'): print "True"
TrueTo write it like that and have a problem with the falseness of 0 it means that you use it in a way to both use it as conditional expression and an integer value, e.g.
digit = is_digit_char('0')
if digit: # fail
print(digit)
else:
print('not a digit')
You should then check for his equality to None digit = is_digit_char('0')
if digit is None: # pass
print('not a number')
else:
print(digit)
But I would argue that you were in search for troubles when you wrote an is_something() function that doesn't return a boolean. That is not idiomatic.p.s. Hy is too crazy :-)
It's not that peculiar -- instead of parsing the same character twice you simply return the value that you parsed or None. My inspiration was from the CLHS predicate function, DIGIT-CHAR-P [0].
The real point I was making is that Python has warts that make writing idiomatic code impractical in some situations. I suggest that practicality take precedence over purity. There are some situations that lead to non-idiomatic code and that's okay.
[0] http://clhs.lisp.se/Body/f_digi_1.htm
Update: forgot the link. :)
Update update: Perhaps peculiar to Python because all values of integers are not False except for 0 whereas in another language that doesn't have this wart, anything that isn't False is True... even 0. In other words, anything that isn't False is True. :D
Except for `nil` (lisps, ruby), and possibly a host of other things depending on the language (empty strings in javascript).
CL, for all intents and purposes, treats nil as False... but some find the conflation of nil and the empty list runs into the same issue when operating on s-exprs.
CL-USER> (if '() 1 0)
0
vs scheme> (if '() 1 0)
1 return len(s) == 1 and s[0] in string.digits
Or return re.search('^\d$', s)
Then it can be used idiomatically. The function you defined I'd call parse_int: def parse_int(s)
"Return a parsed integer from 's' or None if it's not an int."""
...
Which would then be used as: i = parse_int(s)
if i is None:
...your function definition...
$0.02.Ultimately it was my fault for not being better acquainted with the language, but that one still caught me by surprise.
For Python in particular, Jeff Knupp's "Writing Idiomatic Python"[1] (not free, but not expensive, either) goes into detail on a lot of the concepts in the OP's slides. (I'm not affiliated with Jeff in any way, just a satisfied customer.)
Note: Also not affiliated in any way.
[edit: given:
http://www.goodreads.com/book/show/17354838-writing-idiomati...
it doesn't look like it's of all that much value to an experienced python developer. I'd love to hear some other comments, though.]
That being said, I think there are some situations where you want to check for problems up front (possibly in addition to exception handling). In particular, if you are parsing some data from outside the program, you may want to provide some context about what was wrong. KeyError is not very helpful to your users.
while True:
break
What's the problem? I supose the use case is while True:
# do stuff
if some_condition:
break
What is the alternative? 'while some_condition'? That means we must have the 'some_condition' variable outside of the loop. And if we have multiple exit points it may become a mess.I'm not sure what you mean by that, this works just fine. Though you would probably use range in the real world.
i=0
while i !=5:
i+=1
Instead of i=0
while True:
i+=1
if i == 5:
break while True:
result = do_something()
if not result:
break
rather than: result = True
while result:
result = do_something()
[[edit apparently tab ret submits, not whatever I was trying to do with the actual editing]] for _ in xrange(100000):
break
else:
logging.error("ran into an infinite loop")
unless I really do need an infinite loop for things like event handler loop, which is admittedly quite rare.Maybe having a statement like that when testing is okay, but in production code that looks insane.
It's certainly not as idiomatic, but it's more correct in the long run. My eyes were opened to this when reading through the NASA C guidelines. Closing the door on infinite loops lets programs recover gracefully and do the correct thing for the duration of the programming, as opposed to thing that may be correct for that moment of operation: i.e. looping 1-2 more times beyond the limits of the xrange counter.
(f(x) for x in list_of_inputs)
Just like a list comprehension, but with (...) rather than [...] and with lazy evaluation.These are useful when you don't need to evaluate all of the inputs at once but still want to iterate over them at some point later on.
values = (f(x) for x in list)
g(*values)I noticed one small mistake in section 9:
d[keys] = values[i]
Should be: d[key] = values[i] for i in range(len(input_list)):
v = input_list[i]
...do something with v and i...
enumerate is much nicer! for i, v in enumerate(input_list):
...There are some really interesting things that Python allows:
>>> d = {}
>>> d[23], d[14], d['hmm'] = 'abc'
>>> d
{'hmm': 'c', 14: 'b', 23: 'a'} >>> from string import ascii_letters
>>> ors = ['|'] * (len(ascii_letters) * 2 - 1)
>>> ors[::2] = ascii_letters
>>> ''.join(ors)
'a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z'
(Obviously, in this example '|'.join(ascii_letters) would suffice, but if the objects weren't strings...) from string import ascii_letters
'|'.join( ascii_letters )
(I also like list slicing... but only when necessary.) '|'.join(str(x) for x in y) # or
'|'.join(map(str, y))Aw, heck, code speaks louder than words:
https://github.com/PhoenixBureau/PigeonComputer/blob/master/...
intersperseM or $ map chartok whitespace
Oh... wait... sorry... wrong language. ''.join('Thanks!') 'Thanks!'
(It is faster also! :D) set('abcd')
idiom.On page 18, the Java comment: it depends. E.g. see [1]
[1] http://stackoverflow.com/questions/299068/how-slow-are-java-...
[str(x) for x in array]
Do this: map(str, array)>for pet in pets:
> print('A', pet, 'can be very cute!')
This may be nit picking but I prefer output like this:
print 'A %s can be very cute!' %(pet)
print("A {0} can be very cute!".format(pet))
.format() is very versatile. d = {'first': 'Robert', 'last': 'Paulson'}
print("His name was {first} {last}!".format(**d))
>> His name was Robert Paulson!
class Person:
def __init__(self, first, last):
self.first, self.last = first, last
p = Person('Robert', 'Paulson')
print("His name is {0.first} {0.last}!".format(p))
>> His name was Robert Paulson!
You also do not need to know what type is being passed in as the __str__ method is used for .format().As an example, you might represent __bool__ on a database connection object to reflect whether there is a live connection, allowing you to:
if not conn:
conn = MySQLdb.Connect()[1] http://docs.python.org/2/reference/datamodel.html#object.__n...
[2] http://docs.python.org/3/reference/datamodel.html#object.__b...
How about Java?