Python nested objects unable to retrieve value in function - python

I am trying to build a tree structure that represents a parsed configuration file (the configuration file has a hierarchical structure to it). I represented this as:
class configContainer():
treeDict = {}
instances = {}
class configObject():
def __init__(self, name, configLine, parent):
self.name = name # Assign to the line number to make unique
self.parent = parent
self.children = []
self.configLine = configLine # Actual line of the configuration
configContainer.instances[name] = self
The configContainer contains a set of objects. configContainer.instances uses a key of "line#" to map to the object. treeDict does a similar mapping, but with a different key (I create treeDict after the entire container is created).
I then try to reference two objects inside two different configContainers. This works fine from __main__. But when I pass the two configContainers to a function, instances always returns objects from configContainer2
if __name__ == "__main__":
f1 = open('rfile', 'r')
configFile1 = f1.read()
f1.close()
configTree1 = parseConfig(configFile1)
configTree1.treeDict = createTreeDict(configTree1)
zObject1 = configTree1.instances["line10"]
f2 = open('sfile', 'r')
configFile2 = f2.read()
f2.close()
configTree2 = parseConfig(configFile2)
configTree2.treeDict = createTreeDict(configTree2)
zObject2 = configTree2.instances["line10"]
print "\n\nFrom __main__"
print "###########################"
print configTree1
print configTree2
print zObject1
print zObject2
compareConfigurations(configTree1, configTree2)
def compareConfigurations(tmpTree1, tmpTree2):
print "\n\nFrom compareConfigurations"
print "###########################"
print tmpTree1
print tmpTree2
zObject1 = tmpTree1.instances["line10"]
zObject2 = tmpTree2.instances["line10"]
print zObject1
print zObject2
Result is:
### From __main__
<__main__.configContainer instance at 0xb77a34ec>
<__main__.configContainer instance at 0xb740a68c>
<__main__.configObject instance at 0xb740e3ac>
<__main__.configObject instance at 0xb7414bcc>
### From compareConfigurations
<__main__.configContainer instance at 0xb77a34ec>
<__main__.configContainer instance at 0xb740a68c>
<__main__.configObject instance at 0xb7414bcc>
<__main__.configObject instance at 0xb7414bcc>
I can't figure out why I am always getting back the 0xb7414bcc object from inside compareConfigurations?

configContainer.instances is a class attribute, so if you modify it for any instance of a class it will change for all instances of that class. With your current code any time you create a new configObject with the same name it will overwrite the entry in configContainer.instances for that name. You should either make instances an instance attribute of configContainer or make sure your configObjects have different names.
class configContainer():
def __init__(self):
self.instances = {}
...
Here is a quick example of what is happening:
>>> cc1 = configContainer()
>>> cc2 = configContainer()
>>> cc1.instances["line10"] = "foo"
>>> configContainer.instances
{'line10': 'foo'}
>>> cc2.instances["line10"] = "bar"
>>> configContainer.instances
{'line10': 'bar'}
>>> cc1.instances
{'line10': 'bar'}

You are aware that configContainer.instances doesn’t access a instance variable, right?
if you want to refer to the wrapping class, you will have to do something like this:
class configContainer(object):
treeDict = {}
instances = {}
def configObject(self, name, configLine, parent):
return _configObject(self, name, configLine, parent)
class _configObject(object):
def __init__(self, container, name, configLine, parent):
self.name = name # Assign to the line number to make unique
self.parent = parent
self.children = []
self.configLine = configLine # Actual line of the configuration
container.instances[name] = self

Something either in parseConfig or createTreeDict is corrupting your instances dictionary.
Notice that in main you get both zObject1 and zObject2 from configTree1:
zObject1 = configTree1.instances["line10"]
#...
zObject2 = configTree1.instances["line10"]
#
print zObject1
print zObject2
Which you said produces:
<__main__.configObject instance at 0xb740e3ac>
<__main__.configObject instance at 0xb7414bcc>
If you posted the source to parseConfig and createTreeDict we could get to the root of it.

Related

Unexpected change of list values

This is my class:
class variable(object):
def __init__(self, name, name_alias, parents,values,table):
#name of the variable
self.name = ""
This is the function with problems:
f is a .txt file (opened in main function),
def read_problem(f):
list_of_variables=[]
entry=0;
for line in f:
words = line.split()
#enters only if it's not a comment
if (words[0]!='#'):
if (words[0]=="VAR"):
x=variable;
elif (words[0]=="name"):
x.name=words[1]
list_of_variables.append(x)
for i in range(len(list_of_variables)):
print(list_of_variables[i].name)
return
My .txt file is:
VAR
name MaryCalls
VAR
name JohnCalls
VAR
name Burglary
VAR
name Earthquake
VAR
name Alarm
What I get in that print(and thus, the list) is:
Alarm
Alarm
Alarm
Alarm
Alarm
But I wanted to have:
MaryCalls
JohnCalls
Burglary
Earthquake
Alarm
What's wrong? Why are all the previous entries of the list changing?
The line x=variable makes the x refer to the class variable. You never create any instances of that class, instead you repeatedly modify a class-level variable name.
At the end of the program, when you print, variable.name of course has the last value assigned to it, in this case 'Alarm'.
You'll see this if you do print(list_of_variables):
[<class '__main__.variable'>, <class '__main__.variable'>,
etc.
Change x = variable to x = variable(), and you'll see (for example):
[<__main__.variable object at 0x6ffffee65d0>, <__main__.variable object at 0x6ffffee6610>
etc.
1) If you want to initialize the name in the constructor of variable, you have to indent the assignment further.
def __init__(self, name, name_alias, parents,values,table):
#name of the variable
self.name = ""
2) Your main problem is, that you want to create a new instance of variable with this line:
x=variable;
You have to write:
x = variable();
The primary problem is when the code detects a line starting with "VAR" it assigns the class variable to x, not an instance of the class. To do that you need to call the class which ends up invoking the class' __init__() method if it has one. Yours does, but it expects 5 arguments, none of which are known at that time of creation.
The easiest thing to do in such a case is to assign each argument a default value that means "doesn't have a value yet", and assign those to the instance being created (self).
Here's what I mean:
class Variable(object):
def __init__(self, name="", name_alias="", parents=None, values=None,
table=None):
self.name = name
self.name_alias = name_alias
self.parents = parents
self.values = values
self.table = table
def read_problem(f):
list_of_Variables=[]
for line in f:
words = line.split()
# if not a comment
if words[0] != '#':
if words[0] == "VAR":
x = Variable() # construct empty Variable
elif words[0] == "name":
x.name = words[1]
list_of_Variables.append(x)
for var in list_of_Variables:
print(var.name)
return
def main():
filename = 'variables.txt'
with open(filename) as f:
read_problem(f)
main()

Creating several classes in Python dynamically

I will appreciate any effort to clarify the following: is there a way in Python to dynamically create one object per class, where several classes are declared? My silly guess can be described as following:
...
suppose we have some data from db
props = dict_cur.fetchall()
classes_names = []
data = []
for i in props:
classes_names.append(i['client_name'].title())
classes = []
data = []
for i in props:
data.append(dict(i))
for i, d in zip(classes_names, data):
classes.append(type(i, (object,), dict(**d)))
print classes
#printing list of classes
objects = []
for obj in classes:
objects.append(obj())
for obj in objects:
print obj.client_name, obj.client_id
This is very naive approach and it never lets inherit from created classes in a regular way, just like this:
class ClientProcess(Someclient): #Someclient is the name of the created class before
def __init__(self):
print "Someclient stuff"
The goal is pretty simple: create the objects of several classes, preferably with the properties that are stored in the tables, but at the same time have class declaration for every client which will have specific method implemented that will very from class to class. The initial script that works well and uses Python version of Factory method is not sufficient because it only can process one class(client) a time (based on command-line argument which is client id).
If I understand you correctly, you can use the following ways to subclass dynamically created classes:
In : classes = []
In : cls_name = 'BaseCls1'
In : classes.append(type(cls_name, (object, ), {'x': 1}))
In : classes[0].x
Out: 1
In : classes[0].__bases__
Out: (object,)
# two ways to create subclass out of BaseCls1
In : class SubCls1(classes[0]):
: x = 2
:
In : SubCls1.x
Out: 2
In : SubCls1.__bases__
Out: (__main__.BaseCls1,)
In : SubCls2 = type('SubCls2', (classes[0],), {'x': 2})
In : SubCls2.x
Out: 2
In : SubCls2.__bases__
Out: (__main__.BaseCls1,)
class GetConfig(object):
def __init__(self, client_id):
self.client_id = client_id
#construct the query here to get the clients data ...where client_id = self.client_id
d = {'logfile': 'some_long_path', 'contact_name': 'some_name'}
class FirstClient(object):
def __init__(self):
client_id = '111111111'
props = GetConfig(client_id)
#print props.d
def check_source(self):
print "Checking FirstClient source"
return "Something"
#print props.d
def check_downl(self):
print "Checking FirstClient downloaded"
class SecondClient(object):
def __init__(self):
client_id = "222222"
props = GetConfig(client_id)
def check_source(self):
print "Checking SecondClient source"
def check_downl(self):
print "Checking SecondClient downloaded"
myfactory = {
"firstclient" : FirstClient,
"secondclient" : SecondClient,
}
for i in myfactory.values():
i().check_source()
i().check_downl()
collections.namedtuple. done.
Edit: to elaborate,
from collections import namedtuple
rows = dict_cur.fetchall()
# creates the class Row which is a tuple, but each position argument
# corresponds to the column name in that position
# Row can be instantiated as a tuple and then its elements can be accessed
# by name class attributes
Row = namedtuple("Row", zip(*dict_cur.description)[0])
objects = [Row(row) for row in rows]
for o in objects:
print o.client_name, ' is ' , o

Interfering objects (or something less sinister?)

I have created a word object, which consists of just two methods, and takes just two parameters. In spite of this apparent simplicity it is behaving in a way that's beyond my comprehension: if I create two instances of the same object, with the same first argument ("dissembling" in this case) the second instance somehow interferes with the first. Printing the instances reveals that they are indeed separate, so why are the interacting in this way?
# Example tested with Python 2.7.3
from collections import namedtuple
DefinitionTuple = namedtuple("Definition", "word word_id text pos")
class Word(object):
def __init__(self, word, defs=None):
""""""
self.definitions = []
self.word = word
if defs != None:
for each in defs:
try:
each.pos
if each.word.lower() == self.word.lower():
self.definitions.append(each)
except AttributeError:
raise AttributeError("Definitions must be named tuples")
self.orderDefinitions()
def orderDefinitions(self):
""""""
ordered = sorted(self.definitions, key=lambda definition: definition.pos)
for i,each in enumerate(ordered):
each.pos = (i+1)
self.definitions = ordered
class Definition(object):
""""""
def __init__(self, definition):
"""Incoming arg is a single namedtuple"""
self.word = definition.word
self.word_id = definition.word_id
self.text = definition.text
self.pos = definition.pos
if __name__ == "__main__":
nt1 = DefinitionTuple("dissemble", 5, "text_string_a", 1)
nt2 = DefinitionTuple("dissemble", 5, "text_string_b)", 2)
nt3 = DefinitionTuple("dissemble", 5, "text_string_c", 3)
# Definiton objects
def_1 = Definition(nt1)
def_2 = Definition(nt2)
def_3 = Definition(nt3)
dissemble = Word("dissemble", [def_1, def_2, def_3])
print "first printing: "
for each in dissemble.definitions:
print each.pos, each.text
# create a new instance of Word ...
a_separate_instance = Word("dissemble", [def_3])
# ... and now the 'pos' ordering of my first instance is messed up!
print "\nnow note how numbers differ compared with first printing:"
for each in dissemble.definitions:
print each.pos, each.text
You create a new instance of Word, but you reuse the same instance of def_3:
a_separate_instance = Word("dissemble", [def_3])
which is stateful. If we look inside using vars:
print vars(def_3)
# create a new instance of Word ...
a_separate_instance = Word("dissemble", [def_3])
print vars(def_3)
We see
{'text': 'text_string_c', 'word': 'dissemble', 'pos': 3, 'word_id': 5}
{'text': 'text_string_c', 'word': 'dissemble', 'pos': 1, 'word_id': 5}
due to orderDefinitions.
In your orderDefinitions method, you are modifying the pos attribute of your Definition objects:
each.pos = (i+1)
So when you call orderDefinitions a second time, you will be doing def_3.pos = 1.
But, dissemble holds a reference to this def_3 object, whose pos attribute has now changed, hence your issue.

Class design: Last Modified

My class:
class ManagementReview:
"""Class describing ManagementReview Object.
"""
# Class attributes
id = 0
Title = 'New Management Review Object'
fiscal_year = ''
region = ''
review_date = ''
date_completed = ''
prepared_by = ''
__goals = [] # List of <ManagementReviewGoals>.
__objectives = [] # List of <ManagementReviewObjetives>.
__actions = [] # List of <ManagementReviewActions>.
__deliverables = [] # List of <ManagementReviewDeliverable>.
__issues = [] # List of <ManagementReviewIssue>.
__created = ''
__created_by = ''
__modified = ''
__modified_by = ''
The __modified attribute is a datetime string in isoformat. I want that attribute to be automatically to be upated to datetime.now().isoformat() every time one of the other attributes is updated. For each of the other attributes I have a setter like:
def setObjectives(self,objectives):
mro = ManagementReviewObjective(args)
self.__objectives.append(mro)
So, is there an easier way to than to add a line like:
self.__modified = datetime.now().isoformat()
to every setter?
Thanks! :)
To update __modified when instance attributes are modified (as in your example of self.__objectives), you could override __setattr__.
For example, you could add this to your class:
def __setattr__(self, name, value):
# set the value like usual and then update the modified attribute too
self.__dict__[name] = value
self.__dict__['__modified'] = datetime.now().isoformat()
Perhaps adding a decorator before each setter?
If you have a method that commits the changes made to these attributes to a database (like a save() method or update_record() method. Something like that), you could just append the
self.__modified = datetime.now().isoformat()
just before its all committed, since thats the only time it really matters anyway.

python unbound method again

This gets me into difficult time (sorry, i am still very new to python)
Thank you for any kind of help.
The error
print Student.MostFrequent() TypeError: unbound method
MostFrequent() must be called with
Student instance as first argument
(got nothing instead)
This Student.MostFrequent() is called all the way in the end (last line) and the def is last def in the class
EDITED - Naming convention
My long code
import csv
class Student:
sports = []
ftopics = []
stopics = []
choice_list = []
choice_dict = {}
def __init__(self, row):
self.lname, self.fname, self.ID, self.gender, self.sport, self.movie, self.movieyr, self.country, self.ftopic, self.stopic = row
self.sports.append(self.sport)
self.ftopics.append(self.ftopic)
self.stopics.append(self.stopic)
def print_information(self):
return (self.lname, self.fname, self.ID, self.gender)
def print_first(self):
return (self.lname, self.fname, self.sport)
def print_second(self):
return (self.lname, self.fname, self.movie, self.movieyr)
def print_third(self):
return (self.lname, self.fname, self.country)
def print_fourth(self):
return (self.lname, self.fname, self.ftopic, self.stopic)
def most_frequent(self):
for choice in self.choice_list:
self.choice_dict[choice] = self.choice_dict.get(choice, 0) + 1
self.mostFrequent = sorted([(v, k) for k, v in self.choice_dict.items()], reverse=True)
print self.mostFrequent
reader = csv.reader(open('new_mondy_csc_data_revise.csv'), delimiter=',', quotechar='"')
header = tuple(reader.next())
print "%-17s|%-10s|%-6s|%s" %header[:4]
print "-" * 45
students = list(map(Student, reader)) # read all remaining lines
for student in students:
print "%-17s|%-10s|%-6s|%3s" % student.print_information()
print "%-17s|%-10s|%s" %(header[0],header[1],header[4])
print "-" * 45
for student in students:
print "%-17s|%-10s|%s" %student.print_first()
print "%-17s|%-10s|%-16s|%s" %(header[0],header[1],header[5],header[6])
print "-" * 45
for student in students:
print "%-17s|%-10s|%-16s|%s" % student.print_second()
print "%-17s|%-10s|%s" %(header[0],header[1],header[7])
print "-" * 45
for student in students:
print "%-17s|%-10s|%s" %student.print_third()
print "%-17s|%-10s|%-15s|%s" %(header[0],header[1],header[8],header[9])
print "-" * 45
for student in students:
print "%-17s|%-10s|%-16s|%s" % student.print_fourth()
k = len(students)
# Printing all sports that are specified by students
for s in set(Student.sports): # class attribute
print s, Student.sports.count(s), round(((float(Student.sports.count(s)) / k) *100),1)
# Printing sports that are not picked
allsports = ['Basketball','Football','Other','Baseball','Handball','Soccer','Volleyball','I do not like sport']
allsports.sort()
for s in set(allsports) - set(Student.sports):
print s, 0, '0%'
Student.choice_list = Student.sports
X = Student()
X.most_frequent()
#class Search(Student):
# def __init__(self):
# Student.__init__
first read PEP-8 on naming conventions:
Method Names and Instance Variables
Use the function naming rules: lowercase with words separated by
underscores as necessary to improve readability.
second you are calling mostFrequest on the class Student, not an instance of it. Use the method on an instance instead:
student = Student(row)
student.MostFrequent()
use Student().MostFrequent()
edit:
beware that you use class attributes and this is dangerous. here an example:
>>> class Person:
... name = None
... hobbies = []
... def __init__(self, name):
... self.name = name
...
>>> a = Person('marco')
>>> b = Person('francesco')
>>> a.hobbies.append('football')
>>> b.hobbies
['football']
>>> a.name
'marco'
>>> b.name
'francesco'
>>> a.name = 'mario'
>>> b.name
'francesco'
>>> a.name
'mario'
>>>
as you can see i modify marco's hobbies and francesco's hobbies are modified consequentially.
What you probably want is to define most_frequent as a classmethod:
#classmethod
def most_frequent(cls):
for choice in cls.choice_list:
cls.choice_dict[choice] = cls.choice_dict.get(choice, 0) + 1
cls.mostFrequent = sorted([(v, k) for k, v in cls.choice_dict.items()], reverse=True)
return cls.mostFrequent
First, I recommend making function names lower case only.
The error you get results from the usage of MostFrequent as a static method. For this to work, you need to explicitly pass an instance of Student as first argument.
If called directly on an instance of Student, the instance will implicitly be passed as first argument.
Consider using the staticmethod decorator for static usage of functions.
You only rarely call methods on a class definition (Student)
Almost always, you create an instance of the class
someStudent = Student(someRow)
Then you call the method on the instance ("object"), someStudent.
someStudent.MostFrequent()
Student.MostFrequent means You're trying to use static method, not instance method. So You must first create instance by calling Student() and then call MostFrequent() on it.
P.S.: If this is not part of some arcane project, I urge you to follow PEP 8 and use most_frequent as method name.
in your class def, the method definition
def MostFrequent(self,mostFrequent):
has the extra variable mostFrequent that you probably don't want there. Try changing to :
def MostFrequent(self):

Categories