Failing In So Many Ways

Icon

Liang Nuren – Failing In So Many Ways

Primitive Class Variables in Python

I recently ran across something peculiar in my Python development.  I was writing some builders for complex JSON objects and decided to move away from random.randint and simply use a class variable.  I had some code that looked something like this:

class FooBuilder(object):
    def __init__(self, **kwargs):
        options = {
            "obj_id" : random.randint(1, 10000000),
        }

        options.update(kwargs)

I know, it’s not a great design and I could expect some failures due to random number collisions. It was also a bit slower than I really wanted, so I modified it to look like this:

class FooBuilder(object):
    next_obj_id = 0
    def __init__(self, **kwargs):
        options = {
            "obj_id" : self.next_obj_id,
        }

        options.update(kwargs)    
        self.next_obj_id += 1

However, it had a peculiar property: all my tests failed because it appeared that the class variable never updated. I did some experimenting and found lists, dictionaries, and pretty much everything but ‘native’ types worked exactly as expected. It turns out that what’s happening is that you’re assigning the incremented primitive int to the instance because it’s literally a new object. In order to assign it back to the class you have to take some special precautions – type(self).next_obj_id += 1. Here’s some sample code that demonstrates what I’m talking about:

import random

class Foo(object):
    def __init__(self, **kwargs):
        options = {
            "obj_id" : random.randint(1, 10000)
        }

        self.data = options
        print "Foo." + str(self.data)

class Bar(object):
    next_obj_id = 0

    def __init__(self, **kwargs):
        options = {
            "obj_id" : self.next_obj_id
        }

        self.next_obj_id += 1

        self.data = options
        print "Bar." + str(self.data)

class Working(object):
    next_obj_id = 0

    def __init__(self, **kwargs):
        options = {
            "obj_id" : self.next_obj_id
        }

        type(self).next_obj_id += 1

        self.data = options
        print "Working." + str(self.data)

Foo()
Foo()
Foo()

Bar()
Bar()
Bar()

Working()
Working()
Working()

It outputs:

Foo.{‘obj_id’: 1234}
Foo.{‘obj_id’: 40}
Foo.{‘obj_id’: 2770}
Bar.{‘obj_id’: 0}
Bar.{‘obj_id’: 0}
Bar.{‘obj_id’: 0}
Working.{‘obj_id’: 0}
Working.{‘obj_id’: 1}
Working.{‘obj_id’: 2}

tl;dr:
type(self).class_variable_name or self.__class__.class_variable_name to modify class variables seems to be a better choice than self.class_variable.

Advertisements

Filed under: Software Development, ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: