Skip to content Skip to sidebar Skip to footer

Class Attribute Changing Value For No Reason

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 d

Solution 1:

Ok after reading this article: http://martyalchin.com/2007/nov/24/python-descriptors-part-2-of-2/ I have seen the error of my ways. In the aforementioned article, the following snippet is provided which illustrates the correct way to utilize a descriptor for this scenario:

classSimpleDescriptor(object):def__init__(self, name):
        self.name = name

    def__get__(self, instance, owner):
        ifself.name notin instance.__dict__:
            raise AttributeError, self.name
        return instance.__dict__[self.name]

    def__set__(self, instance, value):
        instance.__dict__[self.name] = value

So my descriptor should be this:

classNumberise(object):def__init__(self, value=0, base=16):
        self.base = base
        self.value = value

    def__get__(self, obj, objtype):
        return obj.value
    def__set__(self, obj, val):
        #self.value = self.extract_number(val)
        obj.value = val
        print 'set value to:', self.value

I made the mistake by using the following class as a reference:

classRevealAccess(object):
    """A data descriptor that sets and returns values
       normally and prints a message logging their access.
    """def__init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def__get__(self, obj, objtype):
        print'Retrieving', self.name
        return self.val

    def__set__(self, obj, val):
        print'Updating', self.name
        self.val = val

>>> classMyClass(object):
    x = RevealAccess(10, 'var "x"')
    y = 5>>> m = MyClass()
>>> m.x
Retrieving var "x"10>>> m.x = 20
Updating var "x">>> m.x
Retrieving var "x"20>>> m.y
5

The above class was taken from the documentation: http://docs.python.org/2/howto/descriptor.html Whilst the example is not wrong and it does what it should, it does not apply in this case obviously which was my mistake.

Solution 2:

Note: this answer is similar to the OP's answer, but with a few differences worth noting.

After reading the article linked from another relevant SO question, I've come to the following code:

#!/usr/bin/env pythonimport inspect

classNumberise(object):
    def__init__(self, name):
        self.name = name

    def__get__(self, instance, owner):
        if self.name notin instance.__dict__:
            raise (AttributeError, self.name)
        return'%o'%(instance.__dict__[self.name])

    def__set__(self, instance, value):
        print ('setting value to: %d'%value)
        instance.__dict__[self.name] = value

classRegister(object):
    width = Numberise("truewidth")
    def__init__(self, width=16, name='unNamed'):
        super(Register, self).__init__()
        tuple_args = inspect.getargvalues(inspect.currentframe()) #shorthandfor arg in tuple_args[0]:
            setattr(self, arg, tuple_args[3][arg])

if __name__ == "__main__":

    new_regs = [Register(width=i) for i inrange(10)]
    for i,reg inenumerate(new_regs):
        reg.width = i

    for R in new_regs:
        print ('In extract(). Id:%s name:%s, width:%s, truewidth:%d'%(id(R), R.name, R.width, R.truewidth))

This program produces the output which I think is what's desired:

setting value to:0setting value to:1setting value to:2setting value to:3setting value to:4setting value to:5setting value to:6setting value to:7setting value to:8setting value to:9setting value to:0setting value to:1setting value to:2setting value to:3setting value to:4setting value to:5setting value to:6setting value to:7setting value to:8setting value to:9Inextract().Id:35542384name:unNamed,width:0,truewidth:0Inextract().Id:35543152name:unNamed,width:1,truewidth:1Inextract().Id:35537776name:unNamed,width:2,truewidth:2Inextract().Id:36072560name:unNamed,width:3,truewidth:3Inextract().Id:36070384name:unNamed,width:4,truewidth:4Inextract().Id:36073040name:unNamed,width:5,truewidth:5Inextract().Id:36073072name:unNamed,width:6,truewidth:6Inextract().Id:36073104name:unNamed,width:7,truewidth:7Inextract().Id:36073136name:unNamed,width:10,truewidth:8Inextract().Id:36073168name:unNamed,width:11,truewidth:9

Here's an explanation of what happens. In the line width = Numberise("truewidth") of Register class, we introduce the descriptor. It is one per class, not one per instance, so no value is stored in Numberise itself: we got to store the actual values in the instances. The descriptor as it is defined allows us to access the member variable self.truewidth of an instance of Register class. For the purpose of illustration, the __get__ method returns not the truewidth (which would be return instance.__dict__[self.name]), but its string representation as an octal number. Printing R.width is accessing it via descriptor. Printing R.truewidth is accessing it directly.

We could have called the member variable width, the same as the descriptor, and there would be no naming conflict: the descriptor is a part of the class namespace, and the member variable is a part of each instance's namespace. So, truewidth is used only for clarity, to better distinguish the two entities. In real code, perhaps naming it width is better, so that the actual data is hidden behind the descriptor, and you can't access it by accident.

Also, the program was made both Python2 and Python3 friendly, just by adding parentheses to the lines with raise and print.

Post a Comment for "Class Attribute Changing Value For No Reason"