Python: Six, ABC and functools.wraps

Python
Over the past week I went through some Python code in Stackstorm (hosted in GitHub), and by doing so I learned a few cool new things. Maybe ‘new’ is not the right term, but these things were new to me and I wanted to share them with you.

The code I am referring to resides in a few places in Stackstorm, the first item:

@six.add_metaclass(abc.ABCMeta)
class Access(object):
Two things here:

1. six.add_metaclass – makes Access a metaclass in a way which is compatible both with Python2 and Python3. In general, six package has a purpose of helping developers migrate their code from Python 2 to Python 3 by writing code which is compatible with both.
Hence the name: six = 2 * 3. To read more: https://pypi.python.org/pypi/six

2. abc.ABCMeta – abc is another builtin package, its purpose is to provide support for Abstract Base Classes. By making a class abstract you ensure that nobody can instantiate objects out of it.
This is useful for (at least) three purposes:
a. Maintain logic which is common to multiple classes in one place
b. force the users to implement methods which are annotated by @abc.abstractmethod (you can also force the inheriting classes to create specific class members by using the annotation @abc.abstractproperty). For more info in regards see the docs.
c. creating a singleton. I’m still not convinced about the usefulness of creating a singleton in Python (because we have modules), but assuming you want to do so, you can either create a metaclass and call its methods as ClassName.method() or extend a metaclass but not implement the abstractmethod or abstractproperty. By not implementing the abstract-methods/properties the inheriting class will become abstract as well. This technique is used in a few different places in StackStorm, if you want to see some examples, search for classes that inherit from Access.

3. This one took me a while to wrap my head around (requires a “functional programming” mind if you will):
def decorate(f):
        @functools.wraps(f)
        def callfunction(*args, **kwargs):
By calling @functools.wraps(f) we’re using a decorator inside a decorator. Not trivial…
To understand the main idea of ‘functools.wraps’, it’s easier to go through the following points/process (at least, easier for me…):
– When you decorate a function, you’re creating a new function that wraps the ‘original’ one, and return the wrapper
– The wrapper is used in order to be able to perform all kinds of tasks before/after the ‘original’ function is applied. Examples: preparing the arguments for the ‘original function’, timing how long does it take for the function to execute, add log printings before and after and etc.
– Say you have a wrapped function f, returned from a decorator, the user will then see the description (metadata) of the decorator instead of f, the original function.
– In order to avoid this confusion, @functools.wraps(f) comes into the picture: by annotating the wrapper function with @functools.wraps(f) – you’re making sure that the user of the wrapper will see the same description/metadata as the original function has. By ‘description’ I mean the function-signature (the name of the function and the names of the arguments it takes).

Hope this is helpful!
Advertisements
Python: Six, ABC and functools.wraps

A simple calculator in Python

A while back, I wrote a simple calculator in Ruby. A few minutes ago I ran into a similar question in Stackoverflow, and decided to implement the Python version:

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False


def calc(expr):
    if is_number(expr):
        return float(expr)    
    arr = expr.split('+')
    if len(arr) > 1:
        return sum(map(calc, arr))
    arr = expr.split('-')
    if len(arr) > 1:
        return reduce(lambda x,y: x-y, map(calc, arr))
    arr = expr.split('*')
    if len(arr) > 1:
        return reduce(lambda x,y: x*y, map(calc, arr), 1)
    arr = expr.split('/')
    if len(arr) > 1:
        return reduce(lambda x,y: x/y, map(calc, arr))


print calc("3+4-2 *2/ 2")    # 5

Nice, isn’t it?

A simple calculator in Python

foo.bar: Rusty calculator

Last week I wrote about the code challenge foo.bar: Peculiar balance. Today I’ll tease you with another one:

Rusty calculator
================

Lab minion Rusty works for Professor Boolean, a mad scientist. He’s been stuck in this dead-end job crunching numbers all day since 1969. And it’s not even the cool type of number-crunching – all he does is addition and multiplication. To make matters worse, under strict orders from Professor Boolean, the only kind of calculator minions are allowed to touch is the Unix dc utility, which uses reverse Polish notation.

Recall that reverse Polish calculators such as dc push numbers from the input onto a stack. When a binary operator (like “+” or “*”) is encountered, they pop the top two numbers, and then push the result of applying the operator to them.

For example:
2 3 * => 6
4 9 + 2 * 3 + => 13 2 * 3 + => 26 3 + => 29

Each day, Professor Boolean sends the minions some strings representing equations, which take the form of single digits separated by “+” or “*”, without parentheses. To make Rusty’s work easier, write function called answer(str) that takes such a string and returns the lexicographically largest string representing the same equation but in reverse Polish notation.

All numbers in the output must appear in the same order as they did in the input. So, even though “32+” is lexicographically larger than “23+”, the expected answer for “2+3″ is “23+”.

Note that all numbers are single-digit, so no spaces are required in the answer. Further, only the characters [0-9+*] are permitted in the input and output.

The number of digits in the input to answer will not exceed 100.

Test cases
==========

Inputs:
(string) str = “2+3*2″
Output:
(string) “232*+”

Inputs:
(string) str = “2*4*3+9*3+5″
Output:
(string) “243**93*5++”

The solution (in Python) is surprisingly short:

def answer(normal):
    res = ''
    muls = normal.split('+')
    for mul in muls:
        if len(mul) > 1:
            local_mul = mul.split('*')
            res += ''.join(local_mul)
            res += (len(local_mul) - 1) * '*'
        else:
            res += mul 
    res += (len(muls)-1) * '+'    
    return res

That’s definitely NOT the way you should write your code…
Can you figure it out?

Hint: a similar technique was used here

foo.bar: Rusty calculator

foo.bar: Peculiar balance

In the past few months Google pulled “Matrix” on many Python and Java engineers: if you google terms like “python lambda” you might get pulled into “The Matrix” as well…
To me it happened on a Saturday around midnight, I googled something related to Python and suddenly the search page collapsed and became dark. There was a single sentence that said something like:
“You speak our language”.
and then it invited me to participate in a series of code-challenges.
As a veteran member of CodeEval I gladly stepped into the Matrix and started solving different challenges. All was good until I ran into the following:

Peculiar balance
================
Can we save them? Beta Rabbit is trying to break into a lab that contains the only known zombie cure – but there’s an obstacle. The door will only open if a challenge is solved correctly. The future of the zombified rabbit population is at stake, so Beta reads the challenge: There is a scale with an object on the left-hand side, whose mass is given in some number of units. Predictably, the task is to balance the two sides. But there is a catch: You only have this peculiar weight set, having masses 1, 3, 9, 27, … units. That is, one for each power of 3. Being a brilliant mathematician, Beta Rabbit quickly discovers that any number of units of mass can be balanced exactly using this set.
To help Beta get into the room, write a method called answer(x), which outputs a list of strings representing where the weights should be placed, in order for the two sides to be balanced, assuming that weight on the left has mass x units.
The first element of the output list should correspond to the 1-unit weight, the second element to the 3-unit weight, and so on. Each string is one of:

“L” : put weight on left-hand side
“R” : put weight on right-hand side
“-” : do not use weight

To ensure that the output is the smallest possible, the last element of the list must not be “-“.
x will always be a positive integer, no larger than 1000000000.

Test cases
==========
Inputs:
(int) x = 2
Output:
(string list) [“L”, “R”]
Inputs:
(int) x = 8
Output:
(string list) [“L”, “-“, “R”]

After playing a bit with numbers, I figured out my strategy:
Given a number x I’ll try to find the minimal integer y >=x so that y can be presented as a sum of distinct powers of 3.
I say “distinct” because the coefficients for any power of 3 can be either 0 or 1.
Example:
A good solution: 12 = 0*3^0 + 1*3^1 + 1*3^2
A bad solution: 12 = 3*3^0 + 0*3^1 + 1*3^2
The second solution is bad because one of the coefficients is 3.
So I need to find the smallest y that respects this rule. Once I’ve found such a y, and since I already know how to represent it as a series of distinct powers of 3 – it’s a good candidate for ‘R’.
Now I still need to figure out how to represent the difference y-x as a “series of distinct powers of 3″ and I’m done.
As you might already started suspecting: we cannot find such a representation for any y-x. And indeed, in case we fail, we’ll have to look for the “next y” and on and forth.
The code looked like this:
from itertools import combinations

def answer(x):
    """
    Main logic:
    find the closest number y >= x so such that:
    1. y is a sum of powers of 3
    2. x + z = y where z is sum of numbers, each of which is a power of 3
    3. z will be decomposed to "L" and y will be decomposed to "R" in build_list()
    """
    next = find_next_sum_of_powers(x)        
    left = represent_as_powers(next - x)   
    while list(set(left)) != left: # meaning we have the same item more than once in "left"
        # look for the next "sum of powers" and try to use it instead
        next = find_next_sum_of_powers(next + 1)        
        left = represent_as_powers(next - x)   
    right = represent_as_powers(next)
    return build_list(left, right)


def find_next_sum_of_powers(x):
    """
    Given a number x, find the closest number y
    such that y >= x and y is one of the numbers 
    returned by get_list_of_sums() and there is no number z,
    which is also returned by get_list_of_sums() such that:
    y > z > x
    """
    next = find_next_power(x)
    if x == next:
        return x
    if x <= next // 2:
        sums = []
        for list_of_sums in get_list_of_sums(x):
            for sm in list_of_sums:
                if sm not in sums:                
                    if x == sm:
                        return x                
                    if sm - x in sums:
                        return sm
                    sums.append(sm)
    else:
        return next

def is_power_of_3(x):
    """
    Checks if x is a power of 3
    """
    n = 0
    b = 0
    while n < x:
        n = 3 ** b    
        b += 1
    return n == x


def get_list_of_sums(x):
    """
    Generates a list of sums of powers of 3:
    1, 3, 4, 9, 10, 12, 13...
    4 = 3 + 1; 10 = 9 + 1; 12 = 9 + 3; 13 = 9 + 3 + 1; ...
    """
    workset = []
    for i in gen_next_power():         
        if i > x:
            break        
        workset.append(i) 
    res = []
    for i in range(len(workset)+1):
        for j in combinations(workset, i):
            res.append(j)      
            yield map(sum, res[1:])

def gen_next_power():
    """
    Generator for powers of 3
    each iteration will get the next power
    """
    pow = 0
    while True:
        yield 3 ** pow
        pow += 1


def find_next_power(x):
    """
    Given a number x find the closest 
    power of 3, y such that y > x and there
    is no power of 3, z such that  x < z < y
    """
    if x == 1:
        return x
    t = 3
    while t < x:
        t *= 3
    return t

def represent_as_powers(num):
    res = []
    while num > 0:
        t = find_next_power(num)
        if t > num:
           t /= 3
        res.append(t)
        num -= t
    return res[::-1]

def build_list(left, right):
    stop = right[-1]
    res, i = [],  0
    while 3 ** i <= stop :
        if 3 ** i in left:
            res.append("L")
        elif 3 ** i in right:
            res.append("R")
        else:
            res.append("-")
        i += 1
    return res    
Now, this works. Only problem is – this approach of trying different y’s could get really expensive.
In fact, if I limited the number “retries” to a small number – I could get almost all the test-cases to pass – all but one…
But I wasn’t ready to give up 🙂
So I started googling and found this post which provided a good direction but to be honest – I didn’t understand:
1. how balanced ternary representation helps to solve our problem
2. how to translate from ternary representation to balanced ternary
These questions are coupled, and it took me some extra-reading to figure it out and what I’d like to do now – is save you the time that I spent, by trying to explain it a little better than that post, wikipedia and other resources I ran into during my investigation.
Let’s start with the second question which is pure technical, by comparing numbers in different bases, we’ll represent “balanced” with 1,0,-1:
decimal      1    2      3     4      5
ternary      1    2      10    11     12
balanced     1    1-1    10    11     1-1-1
Trying to follow up on the “balanced” gave me a headache… but after playing with it for a bit, and reading some search results like this answer (which explains how to add numbers in balanced-ternary form), made things much clearer:
in “balanced” we’re still using ternary-base, and the numbers 1/0/-1 are simply the coefficients of the respective power of 3. Lets demonstrate:
2 = 1*3^1 -1*3^0 = 1-1
3 = 1*3^1 + 0*3^0 = 10
4 = 1*3^1 + 1*3^0 = 11
5 = 1*3^2 1*3^1 1*3^0 = 9 – 3 – 1 = 1-1-1
etc
Once you realize the answer to #2 it’s only another small step to understand the connection to the code challenge:
Given that number x, once we find its balanced-ternary representation we can express it as a… sum of distinct powers of 3!
Now we have only a small obstacle: this sum has positive and negative elements – how do we handle it?
The challenge was to balance weights, which means that instead of “subtracting” the left side we can “add” the weight to the right side – like balancing equations!
The meaning of this is that the balanced-ternary form is actually the answer we’re looking for, lets take the number 5 from the previous example:
5 = 1*3^2 1*3^1 1*3^0 = 9 – 3 – 1 = 1-1-1
in the form of our answer – we’re looking at the result from right to left, which means that
1-1-1 = [-1, -1, 1] = 1*3^0 1*3^1 + 1*3^2
and the way it helps us solving the problem is by using ‘R’ as 1*3^2 and balancing it with ‘L’ by adding to ‘L’ 1*3^0 + 1*3^1 which ends up as simply replacing letters in the reversed version of the balanced-ternary form
from: [-1, -1, 1]
to:   [L,  L,  R]
 
Further, if we have a zero in the balanced-ternary form it means that neither L nor R should add the respective power of 3, so we can simply replace any zero with “-” (dash).
We’re now ready to look at the code:
def answer(x):
    return to_balanced(to_ternary(x))

def to_ternary(x):
    """
    convert decimal into ternary
    """
    res = ""
    while x > 0:
        res = str(x % 3) + res
        x /= 3
    return int(res) 

def to_balanced(t):
    """
    convert ternary number into balanced-ternary
    and then to L/-/R representation
    """
    res = map(int, str(t))[::-1] # iterating the digit from right --> left
    for i in range(0, len(res)):
        # zeros will remain zeros and ones remain ones
        # the only thing we need to convert is 2 into +- (== +1-1)
        if res[i] == 2:
            res[i] = -1
            res = inc(res, i + 1)

    # convert from (1,0,-1) representation to (R,-,L)
    return ['-' if x == 0 else ('R' if x == 1 else 'L') for x in res]


def inc(arr, ind):
    """
    calculate the carryover
    """
    if len(arr) == ind:
        arr += [1] # prepend the last carryover
    elif arr[ind] == -1:
        arr[ind] = 0
    elif arr[ind] == 0:   
        arr[ind] = 1
    elif arr[ind] == 1: 
        arr[ind] = -1
        arr = inc(arr, ind + 1)
    else: # arr[ind] == 2
        arr[ind] = 0
        arr = inc(arr, ind + 1)
    return arr

Needless to say that this solution worked perfectly.
foo.bar: Peculiar balance