Home About Puzzles Math Programming Origami Japanese MSPaint

Don't worry, I'm not selling anything. Just giving a shoutout to one of my open-source side projects:

I created a programming language for beginners and am looking for people to test it out. If you have a spare moment (particularly if you haven't programmed before) and you're interested in making 2D games, please check it out.

Home > Programming > Writing Long Python Scripts in One Line

# Writing Long Python Scripts in One Line

Disclaimer: this post has no practical application. It's just a fun way of thinking about coding differently.

One thing that separates Python from most modern widespread programming languages is that whitespace does matter. There are no curly braces to denote where the blocks are. This is done with indention instead. There are no semicolons to denote where the end of each command is. This is done with newlines.

Sometimes this can be frustrating, but it does enforce good code-writing style.

It also creates an opportunity for perverse fun: attempting to get a Python program down to 1 line of code.

Python is pretty simple to learn, but there are some weird hooks in it that allow you to twist it in ways it wasn't meant to be used. For example, earlier I posted a bit on how to "spoof" a switch statement in Python using a lists hack. Let's raise this kind of idea to the power of steroids.

Consider the following program that takes in a string and a list of numbers. It will prepend the string to each of the numbers and return a string of this new list separated by commas. For example, foo('\$', range(5)) would return '\$0, \$1, \$2, \$3, \$4'. This is how we would normally write something like that...

def foo(string, numbers):
output = ''
for i in range(len(numbers)):
if i > 0:
output += ', '
output += string + str(numbers[i])
return output

This can be taken down to one line as follows:

def foo(string, numbers): return ', '.join(map(lambda s,n:s+str(n), [string for i in numbers], numbers))

There's a function called map, that takes in a function pointer and a series of lists of equal size. An item from each list is passed as a parameter to a call of the function pointer you passed in as the first parameter. Suppose you passed in a function called 'bar' and 2 lists with 10 items in it. Map would return a list with 10 items in it where the first item is bar invoked with the first item from the first list as its first parameter and the first item of the second list as its second parameter. The 2nd item in the returned list would be the 2nd item of the first list and the 2nd item of the second list and so on.

Since all lists passed to map have to be the same size, we'd need a list of the string we passed in n times, where n is the size of the numbers list. To do this, there's a wacky implementation of the for loop that is rarely used...

strings = [string for i in range(len(numbers))]

If '\$' was the string, we now have ['\$', '\$', '\$', ...] with as many '\$'s as there are items in the numbers list.

Strings also have a method called .join that takes in a list of strings. This will return a single string that is the list you passed in, but concatenated together with the original string as the glue.

Python supports lambda's. Lambdas are nameless functions. This is ideal for map, since you don't really have to name the function anything to pass its pointer to map. Once map is done, you won't care about this function ever again. Lambda's one-time-deal-ness is perfect. The lambda in this exampl takes 2 parameters: s and n (which correspond to the string list and the numbers list). It adds the string version of n to the end of s.

And last but not least, if a colon is followed by 1 line of indented code, then you can put those two lines of code on the same line. However, this rule can only be applied once. Suppose I have a 3-line program where the first line is a def, the 2nd line is a for loop declaration, and the 3rd line is a normal statement. You can put the 3rd line on the same line as the 2nd line, but even though this is now technically "one line" below the first colon, you can't put this new line on the same line as the first line.

## Optional Parameters

If you need to start out with some variables, you can always rely on optional parameters. Simply put all the initial values you need in the parameter list, but follow them with an equals sign with the value you want to start them out as.

def foo(a, b=3, c=open('whatever.txt''rt')):
...

In this example, you'll have a as the first and only parameter you passed in to the function call, but you'll also have b starting out as 3 and c starting out as a file read stream thingy.

## More Python Abuse: Recursion

If you're dealing with something where you must assign a value to a variable and use that reference later on, then recurions is the way to go if optional parameters won't cut it.

Consider the following program that simply takes in a number and creates a text file with that number as its name and contents:

def foo(a):
con = open(str(a)+'.txt''wt')
con.write(str(a))
con.close()

Now we have a problem. We must create a file write stream that we must reference twice in order to write to it and close it. Also, we can't use optional parameters to open the stream because the filename is in terms of one of the parameters passed in.

Let's rewrite the program with recursion and if statements. Just for the hell of it...

def foo(a, counter=0, con=None):

if counter == 0:
foo(a, 1, open(a + '.txt''wt'))

if counter == 1:
con.write(str(a))
con.close()

Excellent. I like to call this Blake O'Hare Normal Form (simply because I have no idea what the hell to call this state and I like seeing my name everywhere). When you get a program in Blake O'Hare normal form, it can be guaranteed that there is a way to condense it to one line. I don't know if this has a real name already, so for now, I'll just keep calling it Blake O'Hare Normal Form.

## Blake O'Hare Normal Form:

• All statements that require a previous statement to be executed are separated by if statements.
• The function has a counter as a parameter that is modified to determine which if statement will be ran on the next invocation of the function.
• The program uses recursive calls to change the counter.

So to get rid of the if statements, we convert each line of code into an item in a giant list. If foo returns anything, then the list ends with [counter] to indicate which commands to return.

def foo(a, counter=0, con=None):

(
foo(a, 1, open(a + '.txt''wt')),

(
con.write(str(a)),
con.close()
)
)

Before, items under false if statements were not executed, but now they are. Therefore this doesn't immediately work. We need to do some odd tinkering with the function pointers and methods...

def foo(
a,
counter=0
con=None
close=lambda x:x.close(),
write=lambda x,s:x.write(s),
n=lambda a=0,b=0,c=0:0
):

(
(foo,n)[counter](a, 1, (open,n)[counter](a + '.txt''wt')),

(
(n,write)[counter](con, str(a)),
(n,close)[counter](con)
)
)