I don’t understand how ‘static’ class data works in Python. I’m attempting to replicate a pattern I used often in Java where I create a central faux-database class, and give instances of it to any classes that need it.
However, here’s where I’m lost with Python, it seems like anytime I try to access the ‘static’ variable, I create an instance level one. It’s very confusing.
Here’s the code:
import csv
class Database(object):
data = list()
def __init__(self):
pass
def connect(self, location=None):
path = '.\\resources\\database\\'
with open(path + 'info.csv', 'rU') as f:
csv_data = csv.reader(f, delimiter=',')
for row in csv_data:
self.data.append(Entry(*row))
As you can see, pretty straight forward. I declare data outside of the init, which I believe makes it class level. I then loop through a csv file and append each row’s cell fields to the data list.
Here’s where I get confused. The first thing I wanted to do was slice of the first entry in the data list (this just had the column named from the excel sheet). But in doing this, it seems to create an instance level version of data rather than modifying the class level data that I want.
So, I modified the code as follows
import csv
class Database(object):
data = list()
def __init__(self):
pass
def connect(self, location=None):
path = '.\\resources\\database\\'
with open(path + 'info.csv', 'rU') as f:
csv_data = csv.reader(f, delimiter=',')
for row in csv_data:
self.data.append(Entry(*row))
self.data = self.data[1:] ## <--- NEW LINE HERE
As you can see, I tried to slice it as I would any other list, but in doing so, it makes (seemingly) a local version of data
In my main(), I have the following code to test out some of the attributes:
def main():
settings = Settings()
db = csv_loader.Database()
db.connect(settings.database)
print 'gui.py:', db.data[0].num
print 'gui.py:', db.data[0].name
print 'gui.py:', len(db.data)
This behaves just as expected. It has 143 entries, because of the previous slice operation.
Now, I have another class which has an instance of Database() but the slice had no effect.
self.db = Database()
self.settings = Settings()
print 'tabOne.py:', self.db.data[0].conf_num
print 'tabOne.py:', self.db.data[0].conf_name
print len(self.db.data)
The printout from this class shows that there are 144 elements in the list — so the slice operation didn’t actually modify the class level data.
What am I missing? Am I attempting to modify the class level variable incorrectly or something?
You’re basically right about how to go about doing this, however, list slicing creates a copy, so the line:
creates a copy of the data and then assigns it to a new instance attribute.
Here’s how all of this works: when an attribute is looked up for an instance, python first looks in the instances’s
__dict__for the attribute. If it isn’t in the instance’s__dict__, python next looks in the class’s__dict__and then parent class’s__dict__s (following the method resolution order). So, when you do first start accessingself.data(to append to it), you don’t finddatain the instance’s__dict__, so python is usingdatafrom the class’s__dict__. But, when you explicitly assign toself.dict, you’re all of a sudden adding an entry into the instance’s__dict__which will be used from that point on.There are a couple of workarounds:
would work just fine, or you could use slice assignment to modify the
self.datalist in place:Or any other method which modifies the list in place:
A final option, which is probably the most explicit of the bunch is to change
connectto aclassmethod:Now the first argument to the method is the class, not the instance and so we’ve changed the customary
selfto an equally customarycls(although I’ve seenklassas well). This method can be invoked from an instance or from the class itself:In the comments, there is mention of using a module for this sort of thing. A
moduleallows state to transported throughout your program very simply as well and they end up acting a lot like a singleton — in fact they’re often recommended for python instead of singletons:Now in another module you just:
etc. etc.