I offer the readers of Habrahabr a translation of the article “Python Tips, Tricks, and Hacks” . The article will be useful in the initial and middle stages of learning Python.Want to write more concise and readable code? Do you want to fit as much as possible into one expression? You think that reading about a few tricks is better than spending the rest of your life reading documentation? You came to the right address. We'll start with the little tricks you might have encountered if you worked a little bit with Python. But I promise that closer to the end of the article you expect more crazy things.
Content
1. Little tricks. Four types of quotes. The veracity of various objects. Check for occurrence of a substring. Beautiful listing output. Integer division and floating point division. Lambda functions.
2. Lists. List generators and generator expressions.
I tried to make all the code fragments run without additional changes. If you want, you can copy them into the Python shell and see what happens. Please note that many examples contain “incorrect” snippets that are commented out. Nothing prevents you from uncommenting a line and see what happens.
')
There is a small distinction between true and True in this article: when I say that an object is true, it means that when cast to a boolean type, it becomes True. Similar to false and false.
1 Little tricks
1.1 Four types of quotes
Let's start with what you may already know. In some programming languages, single and double quotes are intended for different things. Python allows you to use both options (but the string must begin and end with the same type of quotes). In Python, there are two more types of quotes: '' '(triple single) and "" "(triple double). Thus, you can use several levels of quotes before you have to take care of escaping them. For example, this code is correct:
print """ , : ''' : " : ' '"'''"""
1.2 The veracity of various objects
Unlike some programming languages, in Python an object is considered false only if it is empty. This means that you do not need to check the length of a string, tuple, or dictionary — it suffices to check it as a logical expression.
It is easy to predict that 0 is also false, and the remaining numbers are true.
For example, the following expressions are equivalent. In this case, my_object is a string, but there could be a different type (with appropriate changes to the conditions of the if block).
my_object = 'Test' # True example # my_object = '' # False example if len(my_object) > 0: print 'my_object ' if len(my_object): # 0 False print 'my_object ' if my_object != '': print 'my_object ' if my_object: # False print 'my_object '
So, there is no need to check the length of the object, if you are only interested in whether it is empty or not.
1.3 Check for occurrence of a substring
This is a small, rather obvious hint, but I only found out about it after a year of learning Python. You must know that you can check whether the item you need is in a tuple, list, dictionary, using the 'item in list' or 'item not in list' construct. I could not imagine that it would work for strings. I always wrote something like this:
string = 'Hi there' # True example # string = 'Good bye' # False example if string.find('Hi') != -1: print 'Success!'
This code is pretty awkward. It works in exactly the same way if substring in string:
string = 'Hi there' # True example # string = 'Good bye' # False example if 'Hi' in string: print 'Success!'
Easier and clearer. It may be obvious for 99% of people, but I would like to know about it before I learned.
1.4 Beautiful listing output
The usual output format of the list using print is not very convenient. Of course, it becomes clear what the list is, but more often than not, the user does not want to see quotes around each element. There is a simple solution using the string join method:
recent_presidents = [' ', ' ', ' '] print ' %s.' % ', '.join(recent_presidents) # " , , ."
The join method converts a list into a string, treating each element as a string. The delimiter is the string for which join was called. It is smart enough not to insert a separator after the last element.
Extra bonus: join works linear time. Never create a string by folding list items in a for loop: it's not just ugly, it takes
quadratic time!
1.5 Integer division and floating point division
If you divide an integer by integer, by default the result is truncated to integer. For example, 5/2 returns 2.
There are two ways to fix this. The first and easiest way is to convert one of the numbers to type float. For constants, it suffices to add ".0" to one of the numbers: 5.0 / 2 returns 2.5. You can also use the float (5) / 2 construct.
The second method gives a cleaner code, but you must make sure that your program does not break from this significant change. After calling 'from __future__ import division', Python will always return float as the result of the division. If you need integer division, use the //: 5 // 2 operator always returns 2.
5/2 # 2 5.0/2 # 2.5 float(5)/2 # 2.5 5//2 # 2 from __future__ import division 5/2 # 2.5 5.0/2 # 2.5 float(5)/2 # 2.5 5//2 # 2
In one of the following versions of Python, this behavior will be defaulted. If you want your code to remain compatible, use the // operator for integer division, even if you do not use this import.
1.6 Lambda functions
Sometimes you need to pass a function as an argument or do a short but complicated operation several times. You can define a function in the usual way, or you can use a lambda function — a small function that returns the result of a single expression. The following two definitions are completely identical:
def add(a,b): return a+b add2 = lambda a,b: a+b
The advantage of a lambda function is that it is an expression and can be used inside another expression. Below is an example using the map function, which calls a function for each item in the list and returns a list of results. (In the next paragraph, I will show that map is practically useless. But it gives us the opportunity to bring a good example on one line.)
squares = map(lambda a: a*a, [1,2,3,4,5])
Without lambda functions, we would have to define the function separately. We just saved one line of code and one variable name.
Syntax lambda functions: lambda variables: expression
variables is a comma separated list of arguments. Cannot use keywords. Arguments should not be enclosed in brackets.
expression is an in-line Python expression. The scope includes local variables and arguments. The function returns the result of this expression.
2 Lists
2.1 List Generators
If you used Python long enough, you should at least hear about the concept of “list comprehensions”. This is a way to fit a for loop, an if block and a single-line assignment.
In other words, you can map (map) and filter lists with a single expression.
2.1.1 List display
Let's start with the simplest example. Suppose we need to square all the elements of the list. A freshly python programmer can write code like this:
numbers = [1,2,3,4,5] squares = [] for number in numbers: squares.append(number*number) # squares = [1,4,9,16,25]
We "mapped" one list to another. This can also be done using the map function:
numbers = [1,2,3,4,5] squares = map(lambda x: x*x, numbers)
This code is definitely shorter (one line instead of three), but still ugly. At first glance it is difficult to say what the map function does (it takes a function and a list as arguments and applies a function to each element of the list). In addition, we are forced to determine the function, it looks quite random. If only there was a more beautiful way ... for example, a list generator:
numbers = [1,2,3,4,5] squares = [number*number for number in numbers] # squares = [1,4,9,16,25]
This code does exactly the same thing, but it is shorter than the first example, and more understandable than the second. A person will easily determine what the code is doing; you don't even need to know Python to do this.
2.1.2 Filtering the list
And what if we are interested in filtering the list? For example, you want to remove items greater than or equal to 4. (Yes, the examples are not very realistic. However ...)
A newbie will write like this:
numbers = [1,2,3,4,5] numbers_under_4 = [] for number in numbers: if number < 4: numbers_under_4.append(number) # numbers_under_4 = [1,4,9]
Very simple, isn't it? But the code takes 4 lines, contains two levels of indents and at the same time does a trivial thing. You can reduce the size of the code using the filter function:
numbers = [1,2,3,4,5] numbers_under_4 = filter(lambda x: x < 4, numbers)
Similar to the map function, which we talked about above, filter shortens the code, but it looks pretty ugly. What the hell is going on? Like map, filter gets a function and a list. If the function from the element returns true, the element is included in the resulting list. Of course, we can do this through a list generator:
numbers = [1,2,3,4,5] numbers_under_4 = [number for number in numbers if number < 4] # numbers_under_4 = [1,2,3]
Again we got shorter, clearer and more understandable code.
2.1.3 Simultaneous use of map and filter
Now we can use all the power of the list generator. If I have not yet convinced you that map and filter are wasting too much of your time, I hope you will now agree with me.
Suppose you want to display and filter the list at the same time. In other words, I want to see the squares of the list items smaller than 4. Once again, the neophyte will write this:
numbers = [1,2,3,4,5] squares = [] for number in numbers: if number < 4: squares.append(number*number) # squares = [1,4,9]
Alas, the code began to stretch to the right. Maybe you can simplify it? Let's try to use map and filter, but I have a bad feeling ...
numbers = [1,2,3,4,5] squares = map(lambda x: x*x, filter(lambda x: x < 4, numbers)) # squares is = [1,4,9]
Previously, map and filter was difficult to read, now it is impossible. Obviously, this is not the best idea. Again the list generator saves the situation:
numbers = [1,2,3,4,5] squares = [number*number for number in numbers if number < 4] # square = [1,4,9]
It turned out a little longer than the previous examples with the generator lists, but, in my opinion, quite readable. Definitely better than a for loop or using map and filter.
As you can see, the list generator first filters and then displays. If you definitely need the opposite, it will be more difficult. You have to use either nested generations, or map and filter, or the usual for loop, whichever is simpler. But this is already beyond the scope of the article.
List Generator Syntax: [element for variable (s) in list if condition]
list - any item to iterate
variable (s) - a variable or variables that are equal to the current item in the list, similar to a for loop
condition - inline expression: if it is true, the element is added to the result
element is an inline expression whose result is used as an element of the result list
2.1.4 Generator expressions
There is a downside to the list generator: the entire list must be in memory. This is not a problem for small lists, as in the previous examples, and even several orders of magnitude more. But in the end it becomes ineffective.
Generator expressions (
Generator Expressions ) appeared in Python 2.4. Of all the Python chips, they probably get the least attention. Their difference from the list generators is that they
do not load the entire list into memory, but create a 'generator object', and at each moment only one list item is loaded.
Of course, if you want to use the list for something, it will not help much. But if you simply pass it somewhere where any iterable object is needed (for loop, for example), you should use the generator function.
Generator expressions have the same syntax as list generators, but round brackets are used instead of square brackets:
numbers = (1,2,3,4,5) # , ;) squares_under_10 = (number*number for number in numbers if number*number < 10) # squares_under_10 - generator object, , .next() for square in squares_under_10: print square, # '1 4 9'
This is more efficient than using a list generator.
So, it makes sense to use generator expressions for large lists. If you need the entire list for some other purpose, you can use any of the options. But to use expression-generators is a good habit if there are no arguments against it. True, do not expect to see the acceleration of work, if the list is small.
As a final touch, I want to note that expressions-generators are enough to be enclosed in one parentheses. For example, if you call a function with one argument, you can write something like some_function (item for item in list).
2.1.5 Conclusion
I don’t want to say this, but we only touched on what can be done with the help of expression-generators and list generators. Here you can use all the power of for and if, as well as operate with anything, as long as it is an iterable object.
Full article in PDF