Jubbles
Jubbles

Reputation: 4570

Python - class object initialization within function

The code below works (it will eventually pull records from a database via SQL), but I am having trouble understanding why the assert statement in the code below works.

def build_row(table, cols):
    """Build a class that creates instances of specific rows"""
    class DataRow:
        """Generic data row class, specialized by surrounding function"""
        def __init__(self, data):
            """Uses data and column names to inject attributes"""
            assert len(data)==len(self.cols)
            for colname, dat in zip(self.cols, data):
                setattr(self, colname, dat)
        def __repr__(self):
            return "{0}_record({1})".format(self.table, ", ".join(["{0!r}".format(getattr(self, c)) for c in self.cols]))
    DataRow.table = table
    DataRow.cols = cols.split()
    return DataRow

When is the length of 'data' determined and how is it guaranteed to be the same length as 'self.cols'? Will someone please explain this behavior?

Upvotes: 0

Views: 2638

Answers (3)

PaulMcG
PaulMcG

Reputation: 63782

This may make more sense to you if you replace the references to self.cols and self.table with self.__class__.cols and self.__class__.table. This confusion is caused by accessing class attributes through the self object as if they were instance attributes, and why I don't particularly like this coding idiom. When looking at the code in __init__, where instance attributes are typically assigned their values, it is jarring to immediately see a statement that reads the value of self.cols - where the heck did self.cols get initialized?! On the other hand, if you change this assert to:

assert len(data)==len(self.__class__.cols)

then it becomes clearer that the list of data values is expected to be the same length as the defined list of columns for this instance of DataRow. The class attribute cols gets initialized right after the class definition, in this statement:

DataRow.cols = cols.split()

long before any instance gets created - that's why this assert works.

The writer of this code might consider converting to the more current namedtuple construct.

Upvotes: 1

JBernardo
JBernardo

Reputation: 33407

When you defined DataRow, the column names are set in the variable self.cols. So, when you're instantiating the class, you're supposed to fill every column.

That's why you need both lists having the same length. Otherwise you may not have all attributes.

The way it's done is by setting an attribute with the name of the column which isn't be the best option because if you have a column named cols or table or even __repr__, it may break your code.

Upvotes: 0

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799510

data is given a value when you instantiate the class returned by the build_row() function.

It is not guaranteed to be the same length as self.cols; perhaps you never instantiate the class, and so never see an error.

Upvotes: 1

Related Questions