Guide

1
Lesson
Advanced

About this course

**A Python one-liner is a snippet of code that solves a problem in a single line. ** Want to learn to write Pythonic code? Study Python one-liners written by the pros!

In this article, we’ll show you the top 10 most elegant Python one-liners. Here is an overview of all the Python one-liners in this article – ranked from easiest to hardest. Don’t worry if you don’t understand them now, we explain them in detail below.

# 10. Palindrome Python One-Linerphrase.find(phrase[::-1])# 9. Swap Two Variables Python One-Linera, b = b, a# 8. Sum Over Every Other Value Python One-Linersum(stock_prices[::2])# 7. Read File Python One-Liner[line.strip() for line in open(filename)]# 6. Factorial Python One-Linerreduce(lambda x, y: x * y, range(1, n+1))# 5. Performance Profiling Python One-Linerpython -m cProfile foo.py# 4. powerset Python One-Linerlambda l: reduce(lambda z, x: z + [y + [x] for y in z], l, [[]])# 3. Fibonacci Python One-Linerlambda x: x if x<=1 else fib(x-1) + fib(x-2)# 2. Quicksort Python One-linerlambda L: [] if L==[] else qsort([x for x in L[1:] if x< L[0]]) + L[0] + qsort([x for x in L[1:] if x>=L[0]])# 1. Sieve of Eratosthenes Python One-linerlambda n: reduce( (lambda r,x: r-set(range(x**2,n,x)) if (x in r) else r), range(2,int(n**0.5)), set(range(2,n)))

Let's dive in each of those Python one-liners.

https://youtu.be/moed3LxqDsQ

**What is a palindrome? ** A palindrome is a sequence of characters or numbers *“which reads the same backward as forward, such as madam or race car or the number 10201” * ( Wikipedia ).

Write a Python one-liner that returns the integer 0, if the sequence is a palindrome. Otherwise, return -1.

# THE DATAphrase = "anna"# THE ONE LINERis_palindrome = phrase.find(phrase[::-1])# THE RESULTprint(is_palindrome)# 0 (if it wasn't a palindrome, the result would be -1)

This Python one-liner uses two tools to achieve the goal: the find() function and slicing.

The find() function returns the start index of a given subsequence within a sequence. For example, you can call y.find(x) on any string y in Python. If the string y contains the string x, the find function returns the start index of the string x within y. If the string y does not contain the substring x, it returns -1

Slicing is a Python-specific concept for selecting a range of values from sequences such as lists or strings. Slicing notation is [start:stop:step]. It selects a sequence starting at index “start” (inclusive) and ending at index “stop” (exclusive). The parameter “step” parameter defines the number of characters your slice skips before selecting the next one. By default it is 1. Setting step=2 means that your slice consists of every second character.

A negative step size means the slice is selected “backwards”, i.e. from right to left. In this way, the one-liner reverses the string by slicing phrase[::-1].

Why reverse the string at all? Easy: If the original string contains the reversed string, it is a palindrome. Think about it for a moment and enjoy this wonderful concise Python one-liner.

Suppose you have two variables a and b. You want to assign the value of a to variable b and the value of b to variable a. How do you do this in Python?

# THE DATAa = "hello"b = "bye"# THE ONE-LINERa, b = b, a# THE RESULTprint(a)# byeprint(b)# hello

This is a nice little trick of the Python programming language. The standard way of doing this in other languages is to create a third “container” variable. This does nothing but store the value of a for a moment while both variables a and b have the same value:

# THE UGLY THREE-LINERc = aa = bb = c

Two lines saved while improving readability – not bad for a one-liner!

Given a list of values of either integers or floats, we want to sum over all the values with an even index. That is, values with index 0, 2, 4, 6, 8 etc. How do we do this in a single line of code?

# THE DATAstock_prices = [23, 24, 26, 29, 41, 29, 35]# THE ONE-LINERres = sum(stock_prices[::2])# THE RESULTprint(res)# 125

This one-liner uses advanced slicing notation. Recall that slicing selects a range of values from a list using the notation [start:stop:step]. The first index of the slice is “start” (inclusive) and the last index is “stop” (exclusive). As you may have guessed, the third parameter “step” defines the step size. That is, how many characters from the original sequence are skipped before selecting the next character. Setting step=2 means your slice will consist of every other character.

Finally, the sum operation takes any iterable and sums over all values in it. First, The slice selects every other value and then sum() adds them together.

Your program must communicate with the outside world to have any impact. One way of doing this is to use the file system for input and output.

How can you read all the lines from a file in one line? As a bonus, how can you get rid of trailing whitespaces while reading the lines (e.g. the newline character ‘n’)?

For this, I’ve saved The Zen of Python in the file zen_of_python.txt. If you don’t know what it is, type import this into your Python terminal and have a read.

# THE DATAfilename = 'zen_of_python.txt'# THE ONE-LINERlines = [line.strip() for line in open(filename)]# THE RESULTprint(lines)# ['The Zen of Python, by Tim Peters','Beautiful is better than ugly.','Explicit is better than implicit.',..."Namespaces are one honking great idea -- let's do more of those!"]

Complex Python one-liners are built upon simpler ones. You have to master the simple ones first to be able to understand the more complex ones.

This one-liner is simple and very important. We create a new list ‘lines’ using a list comprehension. Then we store all lines from the file as separate string values.

List comprehensions are a compact way to create lists. The simple formula for them is [ expression + context ].

- Expression: What to do with each list element?
- Context: Which list elements to select? This consists of an arbitrary number of for and if statements.

The one-liner uses the expression line.strip(). The function strip() removes the leading and trailing whitespace (e.g. the newline character ‘n’) from each line the context returns.

The context iterates over the file object returned by open(filename). It specifies the objects on which the expression will be performed. In this case, it selects all the lines (string objects) from the file ‘zen_of_python.txt’.

*Note: whilst this one-liner works, it is best practice to close files once they have been opened. The with statement is the most Pythonic way to do this. Once you exit the with block, the file is automatically closed. *

with open('zen_of_python.txt') as f: lines = [line.strip() for line in f]>>> lines['The Zen of Python, by Tim Peters','Beautiful is better than ugly.','Explicit is better than implicit.',..."Namespaces are one honking great idea -- let's do more of those!"]

This is a fun challenge that is often asked in Python programming interviews . There are many ways of solving this problem, but why not impress the interviewer with a beautiful Python one-liner solution?

# THE DATAfrom functools import reducen = 100# THE ONE-LINERfactorial = reduce(lambda x, y: x * y, range(1, n+1))# THE RESULTprint(factorial)# 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

The one-liner uses the reduce() function. The reduce function takes two arguments: a function and a sequence. It takes two values from the sequence and combines them according to the function argument. Then it repeats this procedure until only one value is left and returns that value. It’s that simple.

In Python 3, the reduce function is not a built-in function (but it is in Python 2). So you have to import it from the functools library.

The one-liner defines an anonymous function using the lambda expression. The lambda function takes two arguments, x and y, and multiplies them together - x * y. In this way, the reduce function multiplies all values in the sequence with each other.

Instead of assigning n before we write our one-liner. We can define our function to work with any n by adding a lambda call before reduce.

Factorial = lambda n: reduce(lambda x, y: x * y, range(1, n+1))>>> Factorial(3)6>>> Factorial(10)3628800>>> Factorial(20) 2432902008176640000

Performance optimization is important for all applications. This is especially true when you start working on bigger projects.

Profiling is the term we use when we measure the performance of a program. The Python documentation says “A profile is a set of statistics that describes how often and for how long various parts of the program executed”.

This one-liner can be executed in the terminal without opening Python. Let’s say, you want to profile the Python script ‘foo.py’.

# THE ONE-LINER$ python -m cProfile foo.py

The one-liner uses the cProfile application to profile the performance bottlenecks of ‘foo.py’. You don’t have to execute the Python script to profile it – the cProfile application does it for you. Then, it tracks statistics (e.g. which functions took the most amount of time) and outputs these statistics to the console.

Here is an example output of such a profiling call ( source ).

197 function calls (192 primitive calls) in 0.002 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.001 0.001 <string>:1(<module>) 1 0.000 0.000 0.001 0.001 re.py:212(compile) 1 0.000 0.000 0.001 0.001 re.py:268(_compile) 1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset) 1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset) 4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction) 3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)

The profiler keeps track of the number of calls each function makes and how long each function runs (its latency). These statistics will help you greatly when optimizing your code. They have certainly helped me a lot.

A powerset is the set of all subsets of a given set.

Unfortunately, it’s not possible to define a set of sets in Python. So our one-liner creates a list of lists.

Sets can only contain immutable (i.e. hashable) objects. Python uses the hash value of an element when checking if it’s a member of a set. This makes the operation incredibly fast. Doing the same with a list is much slower. This is one advantage sets have over lists.

# THE DATAfrom functools import reducedataset = {1,2,3}# THE ONE-LINERf = lambda l: reduce(lambda z, x: z + [y + [x] for y in z], l, [[]])# THE RESULTprint(f(dataset))# [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

The source code is modified from this article .

The one-liner shows an elegant way to solve the problem of calculating the superset. Still, I have to admit, it’s hard to understand.

To aid your understanding, let’s first write out a powerset function over several lines. But before we do that, we need to understand the process of creating a powerset.

**Creating a Powerset Manually **

Recall that a powerset is the set of all subsets of a given set.

By definition, every set contains the empty set as a subset. Thus a powerset is never empty - it always contains at least the empty set. The most basic powerset is [[]] and this is the powerset of []. So any code we write will use this basic powerset as its foundation.

Let’s create the powerset of {1}. From above, we know it contains the empty set []. The only element left is 1. Thus the powerset is [[], [1]].

The powerset of {1, 2} follows a similar pattern. It contains [] and [1] like above. But also contains [2]. Lastly, as is the case with every set, the entire set is classed as a subset. So the powerset also contains [1, 2]. The final answer is [[], [1], [2], [1, 2]].

We will now write the process above as an algorithm. Let’s start with pseudo-code and build the powerset of {1, 2}. The process is adding each element in the set, to all the elements already in the powerset.

Remember that you cannot have a set of sets in Python so our powerset will be a list of lists.

my_set = {1, 2}# Our powerset starts as a list containing the empty listpowerset = [[]]# Take first value of my_set and add it to every list in powersetfirst_value = 1# Only list in powerset is [], so we have one sum to do[] + 1 = [1]# The result of the sum is the next subset to include in powerset# Append result to powersetpowerset.append([1])# Our powerset is nowpowerset = [[], [1]]# Take next value of my_set and add it to every list in powersetnext_value = 2# Lists in powerset are [] and [1], so we have 2 sums to do[] + 2 = [2][1] + 2 = [1, 2]# Append each result to powersetpowerset.append([2])powerset.append([1, 2])# And we havepowerset = [[], [1], [2], [1, 2]]# We have run out of values in my_set and so are finished

**Creating a Powerset with Code **

We will follow the pseudo-code above but will make one change. Instead of using append we are going to use the list concatenation operator +

# These are the samepowerset.append([1])powerset + [[1]]

We know that a powerset is a list of lists. We want to append the values in my_set to each list in this list of lists. We do this until we run out of values in my_set. Writing this as a for loop, we get

for lst in list_of_lists: lst.append(value)

This is the same as

for lst in list_of_lists: lst + [value]

To make our code return a list of lists, we add a couple of lines before and during our for loop

# Initialise empty listfinal_list_of_lists = []# Do our iterationfor lst in list_of_lists: next_list = lst + [value] # Append value we want to our final_list_of_lists final_list_of_lists.append(next_list)

Since lst.append(x) is the same as lst + [x], we can write it as a list comprehension and achieve the same result in 1 line

[lst + [value] for lst in list_of_lists]

Lastly, on each iteration we don't want to lose the current elements in our powerset. So we add the entire list_of_lists to the start

list_of_lists + [lst + [value] for lst in list_of_lists]

Let's write this as a function with a *very * descriptive name

def add_value_to_every_list_in_list_of_lists(list_of_lists, value): """ Given a list_of_lists, return a list that contains the original list_of_lists and each list with value appended to it. e.g. # Add 3 to every list in [[], [1], [1, 2]] and return original # list of lists >>> add_value_to_every_list_in_list_of_lists([[], [1], [1, 2]], 3) [[], [1], [1, 2], [3], [1, 3], [1, 2, 3]] """ return list_of_lists + [lst + [value] for lst in list_of_lists]

To create a powerset we apply this function recursively to every value in my_set. We start with the empty set as the only element in our list of lists.

def powerset(my_set): """ Return the powerset of my_set """ # Initialise most basic powerset current_powerset = [[]] # Iterate over every value in my_set for value in my_set: # Recursively generate powerset. # Add next value to all elements of current_powerset, then # move onto next value current_powerset = add_value_to_every_list_in_list_of_lists(current_powerset, value) return current_powerset

Let's test it out:

>>> powerset({})[[]]>>> powerset({1})[[], [1]]>>> powerset({1, 2})[[], [1], [2], [1, 2]]# It works!>>> powerset({1, 2, 3})[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

Now we'll go back to the one-liner and show how it is equivalent to the functions above. We've replaced list_of_lists with LoL to save space:

# One-linerf = lambda l: reduce(lambda z, x: z + [y + [x] for y in z], l, [[]])# One-liner with the descriptive names we used abovef = lambda my_set: reduce(lambda LoL, value: LoL + [lst + [value] for lst in LoL], my_set, [[]])

- f = lambda my_set: is the same as def powerset(my_set):
- The first argument of reduce() is the same code as add_value_to_every_list_in_list_of_lists(LoL, value)
- The reduce() function iterates through my_set. It applies the lambda function (argument 1) to each element in my_set (argument 2) and adds the result to a list. We define the starting list as [[]] (argument 3).

**What are the Fibonacci numbers? ** They are the values of the Fibonacci Sequence. The Fibonacci Sequence was created by the Italian mathematician “Leonardo of Pisa”. It is very popular because it has many applications in math, art, and biology ( read more here ).

The sequence starts with the numbers 0 and 1. Each following element is the sum of the two previous elements.

# THE DATAn = 10 # THE ONE-LINERfib = lambda x: x if x<=1 else fib(x-1) + fib(x-2)# THE RESULTfor i in range(n): print(fib(i))"""0112358132134"""

The one-liner computes the Fibonacci series recursively. It defines an anonymous function with one parameter x to calculate the x-th Fibonacci element. The recursion base cases are x=0 and x=1 for which the Fibonacci numbers are 0 and 1, respectively. On top of that, the function calls itself to calculate the (x-1)-th and (x-2)-th Fibonacci numbers – and sums over both to calculate the x-th Fibonacci number.

While this is an intuitive way of defining the Fibonacci series, it is very inefficient because of highly redundant function calls. For example, the third Fibonacci number is calculated from scratch to find the fourth, the fifth, and the sixth Fibonacci number. A much better way would be to store the third (and each subsequent) Fibonacci number in a list rather than recomputing it again and again.

Therefore, the function is very slow at calculating even a dozen Fibonacci values. Still, this one-liner serves an educational purpose and is readable and concise.

If you don’t know the quicksort algorithm, have a look at this video:

https://youtu.be/ywWBy6J5gz8

Now, let’s create a one-liner that runs this algorithm!

# THE DATAunsorted = [33,2,3,45,6,54]# THE ONE-LINERqsort = lambda L: [] if L==[] else qsort([x for x in L[1:] if x< L[0]]) + L[0] + qsort([x for x in L[1:] if x>=L[0]])# THE RESULTprint(qsort(unsorted))# [2, 3, 6, 33, 45, 54]

The first thing the Quicksort algorithm ( source ) does is select a pivot element. In our code, it selects the first element, L[0], as the pivot. Then, the algorithm moves all elements smaller than the pivot to the left side. And it moves all elements larger or equal to the pivot to the right side. This is repeated recursively for the left and the right lists.

Let’s suppose you create a new list as follows. You put all elements that are smaller than the pivot to the left, the pivot in the center, and all elements that are larger or equal to the pivot to the right. You would consider the resulting list to be a bit more sorted, right? If the two sublists were already sorted, you would be finished. This is where the recursive call of qsort comes into play. It takes over the problem of sorting each sublist by applying the same scheme of pivoting and recursion as above.

Here is the one-liner written in pseudo-code to make it easier to understand:

if L == []: return []else: # Move all elements less than L[0] to the start return qsort([x for x in L[1:] if x < L[0]]) # Put L[0] after all elements smaller than it + L[0] # Move all elements greater than L[0] after L[0] + qsort([x for x in L[1:] if x >= L[0]])

The Sieve of Eratosthenes is an ancient algorithm that finds all the prime numbers below a specified number n

The idea is based on the fact that every number can be written as the product of prime numbers. This is called prime factorisation. For example, 30 = 2 x 3 x 5 and we say that the prime factors of 30 are 2, 3 and 5. A number is prime if its prime factors are 1 and itself. For example, the prime factors of 19 are 1 and 19. Thus 19 is prime.

You begin The Sieve of Eratosthenes by defining a number, n. You wish to find all prime numbers up to and including n. We do this by excluding all non-prime numbers below n. This leaves us with only prime numbers.

You start with all numbers unmarked. If you come to an unmarked number, it is prime. We start at 2 and see this is unmarked. Thus it is prime. We now mark all multiples of 2 as ‘non-prime’. So 4, 6, 8, 10… are all marked as ‘non-prime’. Next we see that 3 is unmarked. So it is also prime. We now mark all multiples of 3 as “non-prime”. But 6 has already been marked as non-prime because it’s a multiple of 2. So we start marking non-primes from 3 ** 2 = 9. In general, for an unmarked number x, you mark all its multiples as non-prime starting from x**2. Once you find an x where x**2 > n, you stop. The unmarked numbers you have left are all the primes up to n.

In our code we will ‘mark’ numbers by removing them from the set of all numbers from 2 to n. The ‘unmarked’ numbers are those that remain.

# THE ONE-LINERprimes = lambda n: reduce( (lambda r,x: r-set(range(x**2,n,x)) if (x in r) else r), range(2,int(n**0.5)), set(range(2,n)))# THE RESULTprint(primes(100)# {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}

This one-liner is based on a StackOverflow answer .

It’s very elegant but you need to invest some time to understand it. So let’s rewrite it as a function so that it’s easier to understand.

This one-liner is based on a StackOverflow answer .

def sieve_of_eratosthenes(n): """ Return the set of all prime numbers below n """ # We want all primes in the range 2 to n all_nums = set(range(2, n)) # We start marking non-primes from x**2 # So only need to check numbers up to the square root of n nums_to_check = range(2, int(n**0.5)) # Write as a for loop for x in nums_to_check: # if x in all_nums, it has not been marked and so is prime if x in all_nums: # keep x and remove all multiples of x from x**2 onwards all_nums = all_nums - set(range(x**2, n, x)) # if x not in all_nums, it has been marked/removed and so is # not prime. Move onto next iteration without any action else: continue # Numbers remaining are all primes up to n return all_nums

This is much easier to understand than the one-liner thanks to the use of descriptive variable names, a for loop and well-commented code. But does it work? Of course it does!

>>> sieve_of_eratosthenes(10){2, 3, 5, 7}>>> sieve_of_eratosthenes(37){2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}

We’ll now slowly turn this into the one-liner.

**One-liner broken down **

# THE ONE-LINERprimes = lambda n: reduce((lambda r,x: r-set(range(x**2,n,x)) if (x in r) else r), range(2,int(n**0.5)), set(range(2,n)))# Function nameprimes = # The input to our function is the number nlambda n: # We are iteratively removing elements one by one, so we use reducereduce((# same as the first if statement in our function# but written in different order because of reduce's syntaxlambda all_nums, x: all_nums - set(range(x**2, n, x)) if (x in all_nums)# Reduce must return something on each iteration# Returning the unmodified set has the same effect as# ‘else: continue’ in the for loopelse all_nums),# argument 2 of reduce (numbers to iterate over): nums_to_check range(2, int(n**0.5)),# argument 3 of reduce (starting set to modify): all_numsset(range(2, n)))

If you join all those lines together and replace all_nums with r, you get the original one-liner.

One-liners are wonderful. They are powerful and, at times, elegant solutions to problems. However, they can be hard to read. And, as The Zen of Python states, ‘Readability counts.’

Although readability is not important for computers, it is important for humans. Code is meant to be read and understood by humans first and computers second.

Thus it’s better to write more lines of code that are easy to understand rather than a one-liner that takes hours to decipher.

That being said, the one-liners in this article will improve your coding skills. And it will make reading explicit, well-commented code a breeze. We hope you enjoyed working through them as much as we did creating them.

FINXTER PREMIUM
###
Python One-Liners