I have a problem with a program I am writing and i cannot for the life of me figure out what I am doing wrong. Ok so basically I am writing a program to extract data from an XML document and manipulate with class representations of the data.
Now, I have some added complexity in my program in that I am trying to be smart and use a descriptor (I am learning about them and thought I would try integrating them into my code)
Note: I shortened the problem to a self contained python script which can be run as is:
#!/usr/bin/env python
import inspect
class Numberise(object):
def __init__(self, value=0, base=16):
self.base = base
self.value = value
def __get__(self, obj, objtype):
return self.value
def __set__(self, obj, val):
#self.value = self.extract_number(val)
self.value = val
print 'set value to:', self.value
class Register(object):
width = Numberise(base=10)
def __init__(self, width=16, name='unNamed'):
super(Register, self).__init__()
tuple_args = inspect.getargvalues(inspect.currentframe()) #shorthand
for arg in tuple_args[0]:
setattr(self, arg, tuple_args[3][arg])
if __name__ == "__main__":
new_regs = [Register(width=i) for i in range(10)]
for i,reg in enumerate(new_regs):
reg.width = i
for R in new_regs:
print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
When I run the script I get the following output:
C:\Users\gkuhn\Desktop>python test.py
set value to: 0
set value to: 1
set value to: 2
set value to: 3
set value to: 4
set value to: 5
set value to: 6
set value to: 7
set value to: 8
set value to: 9
set value to: 0
set value to: 1
set value to: 2
set value to: 3
set value to: 4
set value to: 5
set value to: 6
set value to: 7
set value to: 8
set value to: 9
In extract(). Id:48851280 name:unNamed, width:9
In extract(). Id:48852080 name:unNamed, width:9
In extract(). Id:48879472 name:unNamed, width:9
In extract(). Id:49285200 name:unNamed, width:9
In extract(). Id:49291504 name:unNamed, width:9
In extract(). Id:49291984 name:unNamed, width:9
In extract(). Id:49292016 name:unNamed, width:9
In extract(). Id:49292048 name:unNamed, width:9
In extract(). Id:49292080 name:unNamed, width:9
In extract(). Id:49292112 name:unNamed, width:9
What I'd like is for the width value to be individual to each register object that I have made. It looks like it is being shared. Should they not be individual?!
Below was my original question, you do not need to read it but I have left it here anyway. The actual issue is already discussed.
So in the snippet below, I am basically grabbing my newly created Register objects and adding them to an already made list.
self.regs = []
temps = []
for register in self.ip_root:
unrolled_regs = UnrollRegister(register)
new_regs = unrolled_regs.convert()
for R in new_regs:
#print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
if 'extended' in R.name.lower():
print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
temps.append(R)
#print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
a = copy.deepcopy(R)
#print type(R).__dict__
#print temps
#self.regs.extend(new_regs)
self.regs += new_regs
#self.regs.extend(unrolled_regs.convert())
for n in temps:
print '\tIn loop. Id:%s name:%s, width:%d'%(id(n), n.name, n.width)
#print type(n).__dict__
Excuse the prints, I have been trying to figure this out!
The definition for the class Register is:
class Register(Base):
width = Numberise(base=10)
address = Numberise(base=10)
def __init__(self, name='unNamed', width=16, description='No description provided',
access='RW', address=0, visibility='Public', reset='async',
documentation=''):
super(Register, self).__init__()
tuple_args = inspect.getargvalues(inspect.currentframe()) #shorthand
for arg in tuple_args[0]:
setattr(self, arg, tuple_args[3][arg])
self.bitfields = []
As mentioned, I am using a data descriptor for the width and address attributes. The definition for the Numberise descriptor is:
class Numberise(Base):
def __init__(self, value=0, base=16):
self.base = base
self.value = self.extract_number(value)
def __get__(self, obj, objtype):
return self.value
def __set__(self, obj, val):
self.value = self.extract_number(val)
def extract_number(self,input):
"try and get the value being represented"
if type(input) == int: #its already a number
return input
else: #its a string
RE = re.compile(r"\d?'([hHdDbB])(\w+)") #of the form 'h10 (verilog)
result = RE.search(input)
if result is not None:
radix, string_num = result.groups()
return int(string_num, {'h':16, 'd':10, 'b':2}[radix.lower()])
else:
return int(input, self.base)
Base does not include much and I've included it here for clarity:
class Base(object):
def __init__(self):
self._parent_spacer = ''
self._spacer = '\t'
@property
def parent_spacer(self):
return self._parent_spacer
@parent_spacer.setter
def parent_spacer(self, value):
self._parent_spacer = value
@property
def spacer(self):
return self.parent_spacer+'\t'
The idea behind this descriptor is to ensure that no matter what we initialise the width and address attributes to be, the saved values will always be integers as opposed to strings.
Now the all important output after running the code:
In extract(). Id:239825680 name:ASIC_ADC_RESULTS_EXTENDED_READ, width:64
In extract(). Id:239779088 name:ASIC_HART_EXTENDED_RECEIVE_BUFFER, width:64
In loop. Id:239825680 name:ASIC_ADC_RESULTS_EXTENDED_READ, width:16
In loop. Id:239779088 name:ASIC_HART_EXTENDED_RECEIVE_BUFFER, width:16
Can someone save my sanity and explain this behavior to me?!