Sinisterly
Python Code Golfing Tips! - Printable Version

+- Sinisterly (https://sinister.ly)
+-- Forum: Coding (https://sinister.ly/Forum-Coding)
+--- Forum: Python (https://sinister.ly/Forum-Python)
+--- Thread: Python Code Golfing Tips! (/Thread-Python-Code-Golfing-Tips)



Python Code Golfing Tips! - Shebang - 08-31-2015

Code Golfing Tips
For Python




Introduction
As someone who is very involved in the code-golf community, I thought I should make a thread summarizing a
few of the most useful tips I've learned over the past few months for those looking to start doing it now. Some of
you reading this might not understand the point, but it's just like any other friendly competition. It doesn't exactly
sharpen your programming ability in the sense of making usable code, but it does help in the sense that you need
to check out multiple angles of approach to figure out which one will be the shortest. This also means you may
need to do your own research to find shortcuts that aren't necessarily presented with the question.




Using Repr String Conversion

This is a really quick tip, but insanely useful in Python 2, specifically. Backticks in Python are the same as using the
repr() function, which for most purposes is exactly the same as using str() on a list, integer, etc. This means you
can save 3 bytes off of every string conversion, and you can remove spaces in some instances! Be wary, however,
as this will append an L to any number too large to be stored in an int, whereas str() will remove this.

Code:
print str(12345) print`12345`



Using Negation/Complement

Negation - and complement ~ are two of the unary operators available in Python to perform. More specifically,
~ is the bitwise complement of a number, i.e. where you switch every 1 with a 0, and 0 with a 1. This is equivalent
to performing -n - 1. Negation should be obvious, but just in case, it is simply multiplying the value by -1.

Now, consider a situation where you need to multiply some value (let's use 4) by a number + 1. Normally, due to
operator precedence, you would have to use 4*(n+1) = 4n + 4. If you did not, you would wind up with 4*n+1,
which you can see is not equivalent. However, we can abuse the negation and complement unary operators
to get around this. Remember that ~n is equal to -n - 1, and -n is equal to -1*n. Now, consider when we first
take the complement, then negate that number. We would have -1*(-n - 1) = n + 1. We've just managed to
increment the variable using those two operators, and we can save two bytes due to operator precedence!
Similarly, you can use negation then complement to get -(-n) - 1 = n - 1. So, in the example I started with, we
could change this to 4*-~n. The only situation in which this would not work is with exponentiation,
which is a more binding operator.



Using Truthy/Falsey Values

One of the great things about Python is that everything generally has a truthy or falsey value. What this means
is that even values that are not booleans can be compared as a boolean. There are much more truthy values
in Python than falsey values. Some examples of falsey values are 0, 0.0, [], (,), {} and "". To be clear, only the
integer 0, float 0.0, empty string "", empty list [], empty tuple (,), and empty dictionary {} are considered falsey
(of the basic data types, in general all empty collections are falsy as well). Any number not equal exactly to 0 is truthy,
and any other non-empty data type is truthy as well.

This can be utilized in a number of situations. For example, if you wanted to print a list of items, starting with the
last and ending with the first, you could check the length on each iteration to make sure it's not zero:

Code:
while len(lst)!=0: print lst.pop()

However, because the list will be falsey when it is empty, you could shorten this a lot by changing it to follow this:

Code:
while lst: print lst.pop()

To be clear, a falsey value does not necessarily mean it is equal to false. You can test this by using [] == False
in the Python REPL environment, and you will clearly see that this is not equal. The ONLY values that are equal to
true and false are the integers 0 and 1 respectively.



Using List Indexing

One of the first tricks I learned is abusing list indexing to avoid having a long if-statement. The general idea is
because True == 1 and False == 0, you can make a list of length 2 and place whatever you want when a
value is False to occur in the 0th element, and whatever you want when a value is True to occur in the 1st element.
You can even chain these together, nesting them the whole way through. Let's say you're taking a number input
that, when greater than 10, prints "A", when between 10 and 0 prints "B", and prints "C" otherwise,
you can use this clunky if-statement:

Code:
n = input() if n > 10: print "A" elif n < 0: print "C" else: print "B"

You could just make this a simple two-liner using list indexing, like so:

Code:
n = input() print [["B","C"][n<0],"A"][n>10]

One issue you need to account for is that none of the conditions can fail, or give an error. This is because this is
not short-circuiting. What this means is that every single condition and action applied must be valid regardless of
what data is being given, otherwise this will not work.



Using String Interleaving

This case does not come about very often, but when it does it's great fun! Especially because people not familiar
with Python will not have much of a clue what's going on. What we're going to be using here is the list slicing ability
of Python. Most of you will know that with a slice value of -1, you can reverse a string. But, you can also use it by
mixing together two strings, so that if a certain value is there, a certain string will print out. For example, if you
needed to take input, and print "One" if a 1 is given, or print "Two" if a 2 is given, you could have this:

Code:
n = input() if n == 1: print "One" else: print "Two"

Or, you could use list indexing:

Code:
print ["One","Two"][input()==1]

However, an even shorter way is with this slicing technique. It's fairly easy how to figure out how to interleave strings.
You simply take the first letter of one of the strings, then the first letter of the next until all the first letters are there.
Then, you continue in the same order for the rest of the strings. If a string ends before the others, any time it
comes up in the order you should place a space as a placeholder. The only issue with uneven string lengths is it
can sometimes be longer than just indexing, so keep trying stuff out! In this example, since we have two strings,
our slice value is 2, and we will subtract 1 from the input value to get the starting position.

Code:
print "OTnweo"[input()-1::2]



Using Python's Exec

Python has a very interesting function called exec. What it does is execute Python code, which means you could
make a Python program that can interpret any other Python program by using one function! This has some very
powerful applications when it comes to avoiding using costly loops. An example question that appeared on
Anarchy Golf was the shortest code to print out the first power of two that started with the numbers 1 through
100. So, the general process for each number i would to continually multiply some variable starting with 1 by
2 until it started with i. Then you would print this, and continue with the rest of the sequence. This is a reference
implementation of the problem that does exactly that:

Code:
for i in range(1,101): v=1 while not str(v).startswith(str(i)):v*=2 print v

Note that startswith takes a lot of bytes, so we can use another function: find. This will return the index of the
string found, and will be 0 when the string starts with the other string. Since 0 is falsey, we can use this to
shorten it and get out of the loop.

Code:
for i in range(1,101): v=1 while str(v).find(str(i)):v*=2 print v

Also, remember we can use backticks to convert a number to a string:

Code:
for i in range(1,101): v=1 while`v`.find(`i`):v*=2 print v

Now, this is fairly short, but it can be improved. In fact, the ideal solution is only 57 bytes long, and fits on one line!
Note that the for loop is very expensive to use. Also note that anything executed by exec can reference variables
that were made outside of the exec. Here is the solution that I submitted to the website, which is ideal:

Code:
i=0;exec'i+=1;x=1\nwhile`x`.find(`i`):x*=2\nprint x;'*100

Note that not much has changed! Just instead of using a for-loop, I created a variable i that starts at zero,
and multiplied the string I wanted executed by 100 to make sure I got through all the numbers. The newlines are
in there because they have to be, and the semicolons are there when a newline isn't needed to keep everything
separate. This is because you can still experience indenting and syntax errors in exec, just the same as you
would in a regular program.



If you need any help understanding some of this, or you would like more examples, post below and ask! Smile
Thanks for reading.