Python objects within an object - help
I'm having trouble making an object in python within another object that is not shared with other main objects. Example:
class HEAD:
nose = 4
class HUMAN:
head = HEAD()
height = 4
x = HUMAN()
x.head.nose
>>> 4
x.head.nose = 20
x.head.nose
>>> 20
y = HUMAN()
y.head.nose
>>> 20
How do I change the code so that y.head.nose retains its original value of 4?
Right now, "nose","head" and "height" are class attributes. They belong to the classes HEAD and HUMAN respectively, not to instances of those classes(objects). When you do "y.head" or "x.head", you actually access the class attribute "HUMAN.head" both times. What you actually need is data attributes:
class HEAD: def __init__(self): self.nose=4class HUMAN: def __init__(self): self.head=HEAD() self.height=4
This should do the trick:
EDIT: beaten to it
class HEAD: def __init__(self): self.nose = 4class HUMAN: def __init__(self): self.head = HEAD() self.height = 4
EDIT: beaten to it
Quote:Original post by seanmusgrave
How do I change the code so that y.head.nose retains its original value of 4?
Assign it to an instance of the class and not to the class as a whole:
class Head: def __get_nose(self): return self.__nose def __set_nose(self, value): self.__nose = value def __init__(self): self.__nose = 4 nose = property(__get_nose, __set_nose) # nose can not be deletedclass Human: def __get_head(self): return self.__head def __get_height(self): return self.__height def __set_height(self, value): if value < 0: raise ValueError("A Human can not have a negative height") self.__height = value def __init__(self): self.__height = 4 self.__head = Head() head = property(__get_head) # head can not be set or deleted height = property(__get_height, __set_height)
In addition to making nose an instance member (by assigning it to the dictionary of self in __init__), I made it a property to show you how you can control access to a member. Try the following:
x = Human()x.height = -5 # raises ValueErrorh = Head()x.head = h # raises AttributeError, IIRC
Hmm. My property-fied version does not raise exceptions as expected. I might have to write the objects out as full descriptors, which is a chore...
Fixed: properties silently fail on classic classes. Your classes must be derived from object, like so:
Hope that helped someone!
class Head(object): def __get_nose(self): return self.__nose def __set_nose(self, value): if value < 0: raise ValueError("A Head can not have a negatively sized nose!") self.__nose = value def __init__(self): self.__nose = 4 nose = property(__get_nose, __set_nose) # nose can not be deletedclass Human(object): def __get_head(self): return self.__head def __set_head(self, value): raise AttributeError("A Human Head can not be replaced!") def __get_height(self): return self.__height def __set_height(self, value): if value < 0: raise ValueError("A Human can not have a negative height!") self.__height = value def __init__(self): self.__height = 4 self.__head = Head() head = property(__get_head, __set_head) # head can not be set or deleted height = property(__get_height, __set_height)
Hope that helped someone!
Quote:Original post by mikeman
Right now, "nose","head" and "height" are class attributes. They belong to the classes HEAD and HUMAN respectively, not to instances of those classes(objects). When you do "y.head" or "x.head", you actually access the class attribute "HUMAN.head" both times. What you actually need is data attributes:
*** Source Snippet Removed ***
mucho gracis for the reply, but "x.height" and "y.height" are still independtly variable, unlike x.head.nose and y.head.nose. are you sure they're all class attribuites?
Quote:Original post by seanmusgrave
mucho gracis for the reply, but "x.height" and "y.height" are still independtly variable, unlike x.head.nose and y.head.nose. are you sure they're all class attribuites?
Numbers are immutable in Python, so x.height and y.height are the same instance initially, but become different instances when y.height is replaced.
Quote:Original post by seanmusgraveQuote:Original post by mikeman
Right now, "nose","head" and "height" are class attributes. They belong to the classes HEAD and HUMAN respectively, not to instances of those classes(objects). When you do "y.head" or "x.head", you actually access the class attribute "HUMAN.head" both times. What you actually need is data attributes:
*** Source Snippet Removed ***
mucho gracis for the reply, but "x.height" and "y.height" are still independtly variable, unlike x.head.nose and y.head.nose. are you sure they're all class attribuites?
Yes, they're all class attributes. The thing is, when you do "x.height=42", it doesn't set the class attribute "height" to 42, it sets/creates a data attribute "height" for instance "x". Remember that in Python you can add new attributes on the fly. Try this:
class Foo: x=1a=Foo()b=Foo()print a.x,b.xprint "a:",a.__dict__,"b:",b.__dict__print "Foo:",Foo.__dict__a.x=42print a.x,b.xprint "a:",a.__dict__,"b:",b.__dict__print "Foo:",Foo.__dict__
You'll see that, after "a.x=42", the class attribute "x" is the same, but instance "a" has a new attribute x:42, which "overshadows" the class attribute with the same name, because it is looked up first.
OTOH, the command "x.head.nose" first retrieves the attribute "x.head"(which is a class attribute), and then sets the attribute "nose". So it works as expected.
Generally, try to remember that classes are little more than dictionaries. If "foo" is an instance of Foo, then the following rules apply:
(1)Extracting an attribute "x": "print foo.x" or "foo.x.y=42"(remember:this doesn't write to the "x" attribute, it reads the "x" attribute and writes to the "y" attribute of the retrieved object).
First, "x" is looked up in the instance's dictionary(data attribute). If it's not there, it's looked up in the class' dictionary(class attribute). If it's still not there, an error occurs.
(2)Setting an attribute "x": foo.x=42
An entry in the instance's dictionary is created(if it doesn't already exists), and it's set to reference the object "42".
So you see, you can read class attributes through instances, but you cannot set them. If you want to alter a class attribute, you have to do Foo.x=42 directy.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement