Single-Statement Python: The Complete Guide

0. Introduction

This guide was originally written by Pietu1998 in 2015. It is licensed under the CC BY-SA 4.0 license.

Example output markup

This guide contains example Python programs with their respective outputs. All normal output is displayed as black. Example user input supplied is displayed as bold blue. Any errors or other text written to STDERR are shown in red.

1. What is Single-Statement Python?

To understand Single-Statement Python, let's first take a look at Single-Line Python.

Python is a very powerful language. It has one problem, though: it takes many lines to write programs, since many language constructs require new lines, such as functions and loops. Each statement is also supposed to be on its own line, but luckily Python provides the semicolon as a separator to fit multiple statements on a line. Some tricks also exist that circumvent these limitations. Python programs written on just one line are called Single-Line Python.

Python is a procedural language, which leads to most actions taking multiple statements. Like everything, this also raised the question: could this basic property be contradicted?

It seems that practically every Python program can be written in one statement. This involves many tricks to change code, and basically makes the code highly functional. These programs written in one statement are called Single-Statement Python, which is a subset of Single-Line Python.

2. Simple statements

Most ordinary programs in Python consist of multiple statements. Let's take a look at this program:

print("Hello world!") name = "Programmer" print("My name is " + name)

This program can be very easily reduced to a single statement. The first thing to do is to eliminate the useless variable:

print("Hello world!") print("My name is Programmer")

After that, you just combine the prints with a newline.

print("Hello world!\nMy name is Programmer")

Cheating: semicolons

Straight up I'd like to emphasize one thing: semicolons don't combine statements! Semicolons allow certain statements to be placed on the same line, but it does not make them one.

Here's two statements:

print("a") print("b")

Here's still two statements:

print("a");print("b")

Basic I/O

Ideally, the program would only print one thing during execution, since prints can easily take multiple statements. Sometimes, though, user input is required. In that case, you need to first display a prompt, then process the input, and finally print the result.

Here's a simple calculator program.

print("Simple calculator") print("Multiply number by 2") print("The result is " + str(2 * int(input("Enter number: ")))) Simple calculator Multiply number by 2 Enter number: 5 The result is 10

Just combining the prints like before wouldn't work, for obvious reasons.

print("Simple calculator\nMultiply number by 2\nThe result is " + str(2 * int(input("Enter number: ")))) Enter number: 5 Simple calculator Multiply number by 2 The result is 10

However, you can take advantage of the input function. It displays a prompt, so just put the other text there.

print("The result is " + str(2 * int(input("Simple calculator\nMultiply number by 2\nEnter number: "))))

Now, the behavior matches the original.

Non-combinable calls

What would happen if the program had to also output a message to STDERR? (You can ignore the import for now. We'll come back to it later.)

import sys print("Print to STDOUT") sys.stderr.write("Print to STDERR") Print to STDOUT Print to STDERR

There's no way to combine these two statements like explained before. However, if you call print it always returns None. None is a falsy value, meaning it becomes False if you convert it to a Boolean.

Python's logical operators, or and and are short-circuiting. That means they only evaluate as much of the expression as necessary. That also means they evaluate their operands left to right. Accurately, or only evaluates its right operand if the left operand is falsy, in which case the truthfulness of the expression can't be decided without evaluating both sides. Likewise, and only evaluates its right operand if the left one is truthy.

This feature can be (ab)used to combine the statements. Since print always returns a falsy value, just put the or operator between it and the other expression. The interpreter will first evaluate the print, then decide it needs to also evaluate the next thing and do it.

import sys print("Print to STDOUT") or sys.stderr.write("Print to STDERR")

These can even be chained. Since the write method returns the number of bytes written, which in this case is always nonzero (truthy), so use and.

import sys print("Print to STDOUT") or sys.stderr.write("Print to STDERR") and print("Print to STDOUT")

One thing to beware of here, though, is operator precedence. and is evaluated before or, so you will probably want to walk through your code and add parenthesis where necessary.

Splitting lines

When you start writing your single-statement program, you'll soon find that your lines are getting really long. That may be a problem as the lines stop fitting on your screen.

Many languages not based on newline-separated statements allow extraneous whitespace almost anywhere. Python, however, is a whole different story.

var = "string that " + "continues here" print(var) File "program.py", line 1 var = "string that " + ^ SyntaxError: invalid syntax

Luckily, Python has a wonderful feature. If you open parenthesis (basically, any of ([{, the interpreter will stop parsing statements until that element is parsed. This allows for great constructions.

var = ("string that " + "continues here") print(var) string that continues here

You can basically wrap anything in there. Since prints and a lot of other things are expressions, you can just add two characters and debug your programs freely.

3. The basic tool: lambdas

Probably the most important thing to do before you can write proper Single-Statement Python is to learn about lambdas.

Lambda functions

A regular Python function is defined as such:

def function(argument1, argument2): statements return return_value

A lambda function or a lambda is simply a function created with the lambda keyword.

function = lambda argument1, argument2: return_value

However, there are some very fundamental differences.

Lambdas as expressions

Lambdas are expressions. Consider this function that computes the square of a number:

def square(number): return number * number print(square(int(input())))

Here's the problem: you can't put anything else on that line with the function! Even with a semicolon in between the other statements in the end would become part of the function. In the start they would just be invalid syntax.

A lambda solves this problem. Since a lambda is an expression, not a specific language construct, you can use it inside other expressions. You can just assign it to a variable and carry on:

square = lambda number: number * number; print(square(int(input())))

But it doesn't end there. You can put the lambda inside parenthesis, and call it immediately:

print((lambda number: number * number)(int(input())))

And there you have it! You've successfully converted a program with a function to a single statement. You couldn't just use the number twice (since it is taken as input), but you had to use some lambda magic instead.

4. Variables

Variables are ugly. Setting a variable can be done in a multitude of ways.

a = 1 a, b = 1, 2 a += 3

However, setting a variable takes up a statement. Variable assignments are not expressions like in many other languages. Therefore you should get rid of variables.

Simple elimination

The most simple way of elimination was already shown before. You just take the value of the variable and replace any usages of it with that variable. Here's the program to work with:

num = 5 square = num * num user = int(input()) user_square = user * user pi = 3.14159265 print("The number is " + str(num)) print("The square is " + str(square) + " (square of " + str(num) + ")") print("User input is " + str(user) + " and its square is " + str(user_square)) print("Pi = " + str(pi) + ", pi = " + str(pi) + ", pi = " + str(pi))

Here, num, square and user_square can easily be eliminated.

user = int(input()) pi = 3.14159265 print("The number is " + str(5)) print("The square is " + str(5 * 5) + " (square of " + str(5) + ")") print("User input is " + str(user) + " and its square is " + str(user * user)) print("Pi = " + str(pi) + ", pi = " + str(pi) + ", pi = " + str(pi))

It seems like the others are going to take a little more effort.

Variables to arguments

There are some things one can't just copy-paste everywhere. There are three reasons for this.

Lambda arguments work just like variables inside the lambdas. Therefore the variables can be converted to arguments.

  1. Find the expression to use the variable. This should usually be the smallest expression containing all the occurrences.
  2. Wrap the expression in a lambda with an argument with the same name as the variable.
  3. Put the lambda inside parenthesis, add another pair of parenthesis immediately after it.
  4. Finally, put the expression inside the variable in the last parenthesis.

Here's what the code looks like after this transformation:

print("The number is " + str(5)) print("The square is " + str(5 * 5) + " (square of " + str(5) + ")") print((lambda user: "User input is " + str(user) + " and its square is " + str(user * user))(int(input()))) print((lambda pi: "Pi = " + str(pi) + ", pi = " + str(pi) + ", pi = " + str(pi))(3.14159265))

Let's break down the last line to clarify a bit.

print( (lambda pi: # start lambda "Pi = " + str(pi) + ", pi = " + str(pi) + ", pi = " + str(pi) # original expression ) # end lambda (3.14159265) # call lambda with value )

You can also put multiple variables independent of each other in the same lambda.

variable1 = 1 variable2 = 2 print(variable1, variable2) (lambda variable1, variable2: print(variable1, variable2))(1, 2)

Modifying sequences

The trick shown above doesn't help with this piece of code.

array[index] = value print(array)

However, Python's feature called slicing does the job. You simply take the parts of the sequence before and after the desired index and insert a new value in between.

(lambda array: print(array))(array[:index] + [value] + array[index + 1:])

This should work for most sequences, may require a bit of adjusting depending on the type of array (for example, if it's a tuple then replace [value] with (value,)).

You can also do this with slice indices.

array[start:end] = data print(array) (lambda array: print(array))(array[:start] + data + [end:])

And there you have it. You can now eliminate most, if not all, variables from code.

Cheating: vars(), __setitem__() and others

Python has a nice little function called vars(). It returns an object that contains all variables. You can then put items in there using the __setitem__() method, which in turn results in variables being changed. The same goes for changing arrays with the __setitem__() method.

The trick is cool and all, but violates the "no variables" principle. The only exception I consider acceptable is the case where your code must modify variables for other code written separately. In that case, assignments or __setitem__() may be used, but still without multiple statements.

5. Conditionals

Conditionals in Python and many other languages are written using the if statement.

if condition: # do stuff if condition: # do stuff else: # do other stuff

These seem tricky to one-line. All code so far has only had one execution path, but conditionals allow for more. Let's see what we can do to them.

Logical operators

Do you still remember the or/and trick for combining two statements from earlier? That also doubles as a conditional.

Since the operators short-circuit, if the first argument is enough to determine the result, the other will not be executed. The following two code segments are equivalent:

if condition: stuff() condition and stuff()

If stuff() consists of multiple statements, you'll just combine them to one expression recursively.

Using the or operator instead of and inverts the condition, so these code segments are also equivalent:

if not condition: stuff() condition or stuff()

You can also do if/else conditionals with the logical operators. To use an else, the truthiness of the result of stuff() must be same as that of the condition if stuff() gets executed. If stuff() returns a truthy value, else becomes or; otherwise it becomes and.

if condition: # here condition is always truthy stuff_returning_truthy() else: other_stuff() condition and stuff_returning_truthy() or other_stuff() if not condition: # here condition is always falsy stuff_returning_falsy() else: other_stuff() (condition or stuff_returning_falsy()) and other_stuff()

If your condition is truthy and stuff() returns falsy (or vice versa), you can either

You can now do conditionals, but there's a better way too.

The ternary operator

Python's ternary operator works a lot like the if/else statement. It looks at condition and evaluates first_stuff() if it's truthy, and second_stuff() otherwise.

first_stuff() if condition else second_stuff()

This allows for much easier conditionals. This ternary is equivalent to the following code:

if condition: first_stuff() else: second_stuff()

If there's no else, just put a dummy value in there.

if condition: stuff() stuff() if condition else 0

6. Imports

Imports are used to acquire new modules for using libraries or making multi-file programs. They seem impossible to one-line at first, but are quite simple after all.

Python provides a wonderful function called __import__. It imports a module and returns it instead of saving it in a variable.

Let's say you write this:

import module import another_module as am from third_module import func from fourth_module import another_func as af module.somefunc(am.otherfunc(), func(), af())

What Python is really doing is something like this:

module = __import__("module") am = __import__("another_module") func = __import__("third_module").func af = __import__("fourth_module").another_func module.somefunc(am.otherfunc(), func(), af())

These look much nicer. Now we just apply the transformations from earlier.

(lambda module, am, func, af: module.somefunc(am.otherfunc(), func(), af()) )( __import__("module"), __import__("another_module"), __import__("third_module").func, __import__("fourth_module").another_func )