It is possible to write Java, C etc. in Python; that is, you use Python as your programming language, but you write code as if it wasn’t Python but another programming language and all you do is adjust the syntax to avoid (syntax) errors and get the program to run. Preferably, your Python code should be like Python code, not just be written in Python. Code that is written like that is called pythonic code; good Python code can be called pythonic. In this post, we will look at various techniques and idioms of the Python programming language that are typical in pythonic code.
Many techniques unique to Python, or at least not very common in other languages, are presented here. They are however not explained in very great detail and in such cases, links will be provided to more detailed documentation.
Use ‘in’ properly
A highly unpythonic way to iterate and print values in a list would be:
l=[8, 32, -53, 4] for i in range(len(l)): print l[i]
This might be acceptable in C or similar languages because of the lack of any better way to do it, but it is bad in Python, since a superior way is provided. The pythonic and encouraged way would be:
l=[8, 32, -53, 4] for i in l: print i
This is much simpler and clearer (these two are usually related). The important thing on the second line in the pythonic code example is that the in
keyword is used correctly. When the in
keyword is used with the for
statement, it will go through every one item of an iterable object (one per iteration of the for loop), and i
(or whatever variable you use) will will contain the value for the current object in the iterable. Because of this, we can also change the third line from print l[i]
to print i
.
It is good to know that the range()
builtin returns an iterable object with values from something (default is 0) to the integer given as an argument. In the unpythonic code, we basically iterate through 0, 1, 2 and 3 and use those numbers as indexes for different items in our list. In the pythonic code, we “directly” iterate through the list.
Avoid parenthesis around conditional statements and loops
The following is correct Python code:
if(True==False):
It is possible to leave the parenthesis out, and you should do so. The parenthesis are considered a code bloat, and the pythonic way to write that if
statement would be:
if True==False:
While C, Java and many other languages do require the parenthesis, Python doesn’t, so don’t use them (the reason the former is allowed is because the expression will just be treated as a parenthesized expression).
Checking for one out of multiple conditions
One way to check if, say, the variable x
is equal to 8, 0 or 6 using just one single if
statement is to write:
if x==8 or x==0 or x==6:
That code is, among other things, ugly and harder to maintain than how it should be. The in
keyword will help you here. Using a list with in
, you could have the following code, meaning exactly the same as the above, but with several advantages (pythonic and easier to read and maintain):
if x in [8, 0, 6]:
It iterates through every one of the list items, and in case one (or more) of them match, the statement evaluates to True
. The pythonic way here can only replace or
, not and
or any other operators bearing similar roles.
Use properties
Don’t write this kind of class in Python:
class Clock(object): def __init__(self): self.__hour=1 def setHour(self, hour): if 25>hour>0: self.__hour=hour else: raise BadHourException def getHour(self): return self.__hour
These traditional getters and setters should be replaced with properties in Python. To replace hour with a property, we can use the property()
builtin. property()
takes up to 4 arguments, the first being a function which is used as a getter, the second a function which is used as a setter, the third a deletion function and the fourth a docstring to be used for the property.
To upgrade our last example to use properties, we can use the following:
class Clock(object): def __init__(self): self.__hour=1 def __setHour(self, hour): if 25>hour>0: self.__hour=hour else: raise BadHourException def __getHour(self): return self.__hour hour=property(__getHour, __setHour)
Note how the old getters and setters are now hidden. Before, we would have used something like the following to manage the hour variable:
time=Clock() time.setHour(9) print time.getHour()
This is what Java code could (and even should) look like, but Python isn’t Java and Java isn’t very pythonic. Our later implementation, which uses properties, would work like the following:
time=Clock() time.hour=9 print time.hour
When we assign to time.hour
at the second line, Python actually calls our __setHour()
method, and the value we are giving it becomes the first (second if you include self
) argument of that method. Likewise, when we ask for the value at line 3, we are given the return value of the __getHour()
method, not necessarily the actual value of __hour
.
Note that properties work only on new-style classes (classes that extend object
).
List comprehensions
List comprehensions is one of my favorite Python features. If you were tasked to write a program that returns all odd values from a list, you might type something like the following if you were unaware of this feature:
l=[4, 2, 1, 9, 14, 25] new=[] for i in l: if i%2==1: new.append(i)
Using list comprehensions, you could type this on one line if we exclude the definition of the list:
new=[i for i in l if i%2==1]
The syntax might seem scary at first, but it isn’t, actually. Let’s break it down a bit. If you type the following in a Python interpreter, you will get the whole list:
[i for i in l]
This iterates through l
, and for each iteration, it adds the value of i
to a new list, and finally, when the iteration has finished, the new list is returned. We can add a statement which specifies a condition that each item must satisfy in order to be included in the new list. This is the if i%2==1
part.
That was the most basic form of a list comprehension. List comprehensions are extremely powerful, and you can do much more complex stuff than this. See the following for examples of complex list comprehensions.
Use enumerate()
You have a list of 5 names:
names=["Peter", "Jack", "Lucy", "Lucas", "Linus"]
You want to print them as a numbered list like the following:
1. Peter 2. Jack 3. Lucy 4. Lucas 5. Linus
The problem is that you need to know the index of the name you are printing in the list, but by using for i in names
, you won’t know that, and using range()
for this purpose is not just pythonic. Don’t fear, enumerate()
solves this problem in a pythonic fashion:
for i, name in enumerate(names, 1): print "{0}. {1}".format(i, name)
The first argument which we give to enumerate is an iterable. The second argument is optional (it defaults to 0), and it sets the value at which to start the enumeration (we want to start from 1, thus we used that).
Generators
Generators in Python is an advanced and powerful feature that, when correctly used, can ease the coding of many tasks. Let’s implement a custom range function (it will have less features than Python’s builtin, note that while
is used instead of a for i in range(n)
as we want to implement our own range-like function without using the builtin):
def myRange(length): n=0 result=[] while n<length: result.append(n) n+=1 return result
The 3rd line is highlighted; we don’t want that line; we don’t want a temporary list. This is where generators are useful. Instead, we could have used the following code for our range implementation:
def myRange(length): n=0 while n<length: yield n n+=1
The nice trick here is the yield
keyword; it adds a value to a list, which is later on returned as a generator object (note the lack of an explicit return
statement, the generator is returned when the function has finished its execution). Generator objects can, for instance, become iterators, now allowing us to use our own range implementation instead of the one built into Python:
for i in myRange(7): print i
Some final things
To swap two values, don’t use a temporary variable, instead use:
a, b=b, a
You don’t have to use the readlines()
method of files to iterate through files, instead you can directly iterate with a for loop:
for line in file: #do something with line
If possible, you should call the open()
function at the same line as the for
statement:
for line in open("file.txt"): #do something with line
Use tuple unpacking to return multiple values from a function:
def doubleUs(a, b) return a*2, b*2 x, y=doubleUs(4, 9)
The idiom for an infinite loop in Python is a while
loop with the expression 1
(not True
):
while 1: #Do things
Conclusion
Python is a language with many great features. These features are the things that make Python Python and not “just another programming language”. These features and idioms are pythonic and define the Python way of doing things. This was just an introduction to these, and there is much more to the word pythonic than these features.
Comments (306)