users,
I have a basic question concerning inheritance (in python). I have two classes and one of them is inherited from the other like
class p:
def __init__(self,name):
self.pname = name
class c(p):
def __init__(self,name):
self.cname = name
Is there any possibility that I can create a parent object and several child objects which refer to the SAME parent object? It should work like that that the parent object contains several variables and whenever I access the corresponding variables from a child I actually access the variable form the parent. I.e. if I change it for one child it is changed also for all other childes and the data are only stored once in memory (and not copied for each child...)
Thank you in advance.
Here is a possible workaround which I do not consider as so nice
class P:
def __init__(self, name):
self.pname = name
class C:
def __init__(self, name,pobject):
self.pobject = pobject
self.cname = name
Is this really the state of the art or do there exist other concepts?
Sebastian
Thank you all for helping me, also with the name conventions :) But I am still not very satisfied. Maybe I give a more advanced example to stress what I really want to do.
class P:
data = "shareddata"
def __init__(self,newdata):
self.data = newdata
def printname(self):
print self.name
class C(P):
def __init__(self,name):
self.name = name
Now I can do the following
In [33]: c1 = test.C("name1")
In [34]: c2 = test.C("name2")
In [35]: c1.printname()
name1
In [36]: c2.printname()
name2
In [37]: c1.data
Out[37]: 'shareddata'
In [38]: c2.data
Out[38]: 'shareddata'
And this is so far exactly what I want. There is a variable name which is different for every child and the parent class accesses the individual variables. Normal inheritance. Then there is the variable data which comes from the parent class and every child access it. However, now the following does not work any more
In [39]: c1.data = "tst"
In [40]: c2.data
Out[40]: 'shareddata'
In [41]: c1.data
Out[41]: 'tst'
I want the change in c1.data to affect also c2.data since I want the variable to be shared, somehow a global variable of this parent class.
And more than that. I also want to create different instances of P, each having its own data variable. And when I create a new C object I want to specify from which P object data should be inhetited i.e. shared....
UPDATE:
remark to the comment of @eyquem: Thanks for this, it is going into the direction I want. However, now the __class__.pvar
is shared among all objects of the class. What I want is that several instances of P may have a different pvar. Lets assume P1 has pvar=1 and P2 has pvar=2. Then I want to create children C1a, C1b, C1c which are related to P1, i.e. if I say C1a.pvar it should acess pvar from P1. Then I create C2a, C2b, C2c and if I access i.e. C2b.pvar I want to access pvar from P2. Since the class C inherits pvar from the class P pvar is known to C. My naive idea is that if I create a new instance of C I should be able to specify which (existing) P object should be used as the parent object and not to create a completely new P object as it is done when calling P.__init__
inside of the __init__
of C... It sounds simple to me, maybe I forget something...
UPDATE:
So I found this discussion which is pretty much my question
Any suggestions?
UPDATE:
The method .class._subclasses_ seems to be not existing any more..
UPDATE:
Here is onother link:
link to discussion
There it is solved by copying. But I do not want to copy the parent class since I would like that it exists only once...
UPDATE:
Sorry for leaving the discussion yesterday, I am a bit ill... And thank you for the posts! I will now read through them. I thought about it a bit more and here is a possible solution I found
class P(object):
def __init__(self,pvar):
self.pobject = None
self._pvar = pvar
开发者_JAVA技巧 @property
def pvar(self):
if self.pobject != None:
return self.pobject.pvar
else:
return self._pvar
@pvar.setter
def pvar(self,val):
if self.pobject != None:
self.pobject.pvar = val
else:
self._pvar=val
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject): #<-- The same default `P()` is
# used for all instances of `C`,
# unless pobject is explicitly defined.
P.__init__(self,None)
self.name = name
self.pobject = pobject
p1 = P("1")
p2 = P("2")
c1a = C("c1a",p1)
c1b = C("c1b",p1)
c1c = C("c1c",p1)
c2a = C("c2a",p2)
c2b = C("c2b",p2)
c2c = C("c2c",p2)
print id(c1a.pvar)
print id(c1b.pvar)
print id(c1c.pvar)
print id(c2a.pvar)
print id(c2b.pvar)
print id(c2c.pvar)
print id(p1.pvar)
print id(p2.pvar)
print id(c1a.name)
print id(c1b.name)
print id(c1c.name)
print id(c2a.name)
print id(c2b.name)
print id(c2c.name)
It is a bit cumbersome and I hope that there is a simpler way to achieve this. But it has the feature that pvar is only mentioned in the class P and the class C does not know about pvar as it should be according to my understanding of inheritance. Nevertheless when I create a new instance of C I can specify an existing instance of P which will be stored in the variable pobject. When the variable pvar is accessed actually pvar of the P-instance stored in this variable is accessed...
The output is given by
3078326816
3078326816
3078326816
3074996544
3074996544
3074996544
3078326816
3074996544
156582944
156583040
156583200
156583232
156583296
156583360
I will read now through your last comments,
all the best, Sebastian
UPDATE:
I think the most elegant way would be the following (which DOES NOT work)
class P(object):
def __init__(self,pvar):
self.pvar = pvar
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject):
P = pobject
self.name = name
I think python should allow for this...
UPDATE:
Ok, now I found a way to achieve this, due to the explanations by eyquem. But Since this is really a hack there should be an official version for the same...
def replaceinstance(parent,child):
for item in parent.__dict__.items():
child.__dict__.__setitem__(item[0],item[1])
print item
class P(object):
def __init__(self,pvar):
self.pvar = pvar
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject):
P.__init__(self,None)
replaceinstance(pobject,self)
self.name = name
p1 = P("1")
p2 = P("2")
c1a = C("c1a",p1)
c1b = C("c1b",p1)
c1c = C("c1c",p1)
c2a = C("c2a",p2)
c2b = C("c2b",p2)
c2c = C("c2c",p2)
print id(c1a.pvar)
print id(c1b.pvar)
print id(c1c.pvar)
print id(c2a.pvar)
print id(c2b.pvar)
print id(c2c.pvar)
print id(p1.pvar)
print id(p2.pvar)
print id(c1a.name)
print id(c1b.name)
print id(c1c.name)
print id(c2a.name)
print id(c2b.name)
print id(c2c.name)
the output is the same as above
3077745184
3077745184
3077745184
3074414912
3074414912
3074414912
3077745184
3074414912
144028416
144028448
144028480
144028512
144028544
144028576
UPDATE: Even if the id's seem to be right, the last code does not work as is clear from this test
c1a.pvar = "newpvar1"
print c1a.pvar
print c1b.pvar
print c1c.pvar
print c2a.pvar
print c2b.pvar
print c2c.pvar
print p1.pvar
print p2.pvar
it has the output
newpvar1
1
1
2
2
2
1
2
However the version I posted first works:
class P(object):
def __init__(self,pvar):
self.pobject = None
self._pvar = pvar
@property
def pvar(self):
if self.pobject != None:
return self.pobject.pvar
else:
return self._pvar
@pvar.setter
def pvar(self,val):
if self.pobject != None:
self.pobject.pvar = val
else:
self._pvar=val
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject): #<-- The same default `P()` is
# used for all instances of `C`,
# unless pobject is explicitly defined.
P.__init__(self,None)
self.name = name
self.pobject = pobject
p1 = P("1")
p2 = P("2")
c1a = C("c1a",p1)
c1b = C("c1b",p1)
c1c = C("c1c",p1)
c2a = C("c2a",p2)
c2b = C("c2b",p2)
c2c = C("c2c",p2)
print id(c1a.pvar)
print id(c1b.pvar)
print id(c1c.pvar)
print id(c2a.pvar)
print id(c2b.pvar)
print id(c2c.pvar)
print id(p1.pvar)
print id(p2.pvar)
print id(c1a.name)
print id(c1b.name)
print id(c1c.name)
print id(c2a.name)
print id(c2b.name)
print id(c2c.name)
print "testing\n"
c1a.printname()
c1b.printname()
c1c.printname()
c2a.printname()
c2b.printname()
c2c.printname()
print "\n"
c1a.name = "c1anewname"
c2b.name = "c2bnewname"
c1a.printname()
c1b.printname()
c1c.printname()
c2a.printname()
c2b.printname()
c2c.printname()
print "pvar\n"
print c1a.pvar
print c1b.pvar
print c1c.pvar
print c2a.pvar
print c2b.pvar
print c2c.pvar
print p1.pvar
print p2.pvar
print "\n"
c1a.pvar = "newpvar1"
print c1a.pvar
print c1b.pvar
print c1c.pvar
print c2a.pvar
print c2b.pvar
print c2c.pvar
print p1.pvar
print p2.pvar
print "\n"
c2c.pvar = "newpvar2"
print c1a.pvar
print c1b.pvar
print c1c.pvar
print c2a.pvar
print c2b.pvar
print c2c.pvar
print p1.pvar
print p2.pvar
with the output
3077745184
3077745184
3077745184
3074414912
3074414912
3074414912
3077745184
3074414912
144028416
144028448
144028480
144028512
144028544
144028576
testing
c1a
c1b
c1c
c2a
c2b
c2c
c1anewname
c1b
c1c
c2a
c2bnewname
c2c
pvar
1
1
1
2
2
2
1
2
newpvar1
newpvar1
newpvar1
2
2
2
newpvar1
2
newpvar1
newpvar1
newpvar1
newpvar2
newpvar2
newpvar2
newpvar1
newpvar2
Does anybody know why it is like that? I probably do not understand the internal way python works with this __dict__
so well...
It should work like that that the parent object contains several variables and whenever I access the corresponding variables from a child I actually access the variable form the parent. I.e. if I change it for one child it is changed also for all other childes and the data are only stored once in memory (and not copied for each child...)
That's not inheritance.
That's a completely different concept.
Your "shared variables" are simply objects that can be mutated and have references in other objects. Nothing interesting.
Inheritance is completely different from this.
The other answer is right, your question is more about namespaces and references than about inheritance.
All variables in Python are references, and all object instance is a namespace. So you can do:
class C():
def __init__(self, x):
self.x = x
class Shared(object):
def __init__(self, value):
self.value = value
# instances:
>>> shared1 = Shared(1)
>>> shared2 = Shared(2)
>>> c1 = C(shared1)
>>> c2 = C(shared1)
>>> c3 = C(shared2)
>>> c4 = C(shared2)
# c1 and c2 sharing a reference to shared1
>>> c1.x.value
1
>>> c2.x.value
1
# change c2.x will reflect on c1
>>> c2.x.value = 3
>>> c1.x.value
3
# but not on c3, because shared1 and shared2 are distinct namespaces
>>> c3.x.value
2
UPDATE:
But watch out, it is easy to make a mistake:
>>> c4.x = 4
>>> c3.x.value
2
>>> c4.x.value
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: 'int' object has no attribute 'value'
>>>
I think the state of the art would be using properties to hide the __shared_instance
in a private instance variable - so you can use c1.x instead of c1.x.value and avoid a typo like the example above.
I think your workaround is doable; You could use properties to make access to P
's attributes easier:
class P(object):
def __init__(self,name='default',pvar=1):
self.pname = name
self.pvar=pvar
class C(object):
def __init__(self,name,pobject=P()): #<-- The same default `P()` is
# used for all instances of `C`,
# unless pobject is explicitly defined.
self.cname = name
self.pobject=pobject
@property
def pvar(self):
return self.pobject.pvar
@pvar.setter
def pvar(self,val):
self.pobject.pvar=val
c1=C('1')
c2=C('2')
c1
and c2
share the same pobject
:
print(c1.pvar)
# 1
c1.pvar=2
Notice that changing pvar
through c1
changes c2.pvar
:
print(c2.pvar)
# 2
c3
has a different pobject
:
c3=C('3',P())
print(c3.pvar)
# 1
Regarding OOP design for the psychology experiment (mentioned in the comments):
import Image
class Picture(object):
def __init__(self,filename):
self.filename = filename
self.image=Image.open(filename)
class Person(object):
def __init__(self,name):
self.name=name
# other vital statistics associated with people as individuals here
class Trial(object):
# A trial is composed of one person, one picture, and the places they look
def __init__(self,person,picture,locations):
self.person=person
self.picture=picture
self.locations = locations
# put methods for analyzing where the person looked here
A Picture
is certainly not a Person
, nor vice versa. And the same goes for Trial
s. So none of these classes should inherit from each other.
Each of these classes have a public (and maybe a private) interface.
Public methods and attributes should be freely accessible from other classes.
So given a Trial
instance, t
, the image should be accessible through t.picture.image
. As long as you are only accessing public attributes and methods, then everything should be fine.
For convenience, you can use properties
to link attributes to component-attributes. For example:
class Trial(object):
...
@property
def image(self):
return self.picture.image
But to short-cut this by making, say, Trial
a subclass of Picture
would be contrary to fundamental OOP design principles.
I am lost in all these diverse answers.
But I think that what you need is expressed in the following code:
class P:
pvar=1 # <--- class attribute
def __init__(self,name):
self.cname = name
class C(P):
def __init__(self,name):
self.cname = name
c1=C('1')
c2=C('2')
print
print "C.pvar ==",C.pvar,' id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,' id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,' id(c2.pvar)==',id(c2.pvar)
print
C.pvar = [1,2]
print "instruction C.pvar = [1,2] executed"
print "C.pvar ==",C.pvar,' id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,' id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,' id(c2.pvar)==',id(c2.pvar)
print
c2.__class__.pvar = 'sun'
print "instruction c2.__class__.pvar = 'sun' executed"
print "C.pvar ==",C.pvar,' id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,' id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,' id(c2.pvar)==',id(c2.pvar)
print
c2.pvar = 145
print "instruction c2.pvar = 145 executed"
print "C.pvar ==",C.pvar,' id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,' id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,' id(c2.pvar)==',id(c2.pvar)
result
C.pvar == 1 id(C.pvar) == 10021768
c1.pvar== 1 id(c1.pvar)== 10021768
c2.pvar== 1 id(c2.pvar)== 10021768
instruction C.pvar = [1,2] executed
C.pvar == [1, 2] id(C.pvar) == 18729640
c1.pvar== [1, 2] id(c1.pvar)== 18729640
c2.pvar== [1, 2] id(c2.pvar)== 18729640
instruction c2.__class__.pvar = 'sun' executed
C.pvar == sun id(C.pvar) == 18579136
c1.pvar== sun id(c1.pvar)== 18579136
c2.pvar== sun id(c2.pvar)== 18579136
instruction c2.pvar = 145 executed
C.pvar == sun id(C.pvar) == 18579136
c1.pvar== sun id(c1.pvar)== 18579136
c2.pvar== 145 id(c2.pvar)== 10022024
I mean that what you must know is that to change , through an instruction implying directly the name of an instance (and not through a change implying only the parent class's name) the class attribute pvar while it continues to be shared by all the P 's instances, you must write
c2.__class__.pvar = something
and not
c2.pvar =something
Note that C is a class effectively inheriting from a parent class P
One thing you must know as a base of the understanding of functionning of classes and instances:
A class instance has a namespace implemented as a dictionary which is the first place in which attribute references are searched.
When an attribute is not found there, and the instance’s class has an attribute by that name, the search continues with the class attributes.
http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy
In the second sentence, I don't exactly understand the meaning of "by that name" , but I understand of the global that an attribute is searched first in the namespace of an instance and then in the namespace of its type.
In the following code :
- For class
P
and instancec
:
the name 'dataclass' and object dataclass
really belong to the P
class's namespace and only APPARENTLY belong to the c
instance's namespace: when c.dataclass
is called , that's in fact c.__class__.dataclass
that is attained, by the course of search described above.
- But in an instance
cc
of the classPP
, the name 'data' , which belongs to theP
class's namespace, is assigned (binded) by the definition occuring in__init__()
to a newdata
object created in thec
instance's namespace.
Hence, the only solution to obtain the class's data
's value is to call it by its real reference, either PP.data
or cc.__class__.data
.
class P:
dataclass = "shareddata"
def __init__(self,newdata):
self.data = newdata
def printname(self):
print self.name
c = P(1)
print 'P.__dict__.keys()==',P.__dict__.keys()
print 'c.__dict__.keys()==',c.__dict__.keys()
print
print 'c.data==',c.data
print 'c.dataclass==',c.dataclass
print
class PP:
data = "shareddata"
def __init__(self,newdata):
self.data = newdata
def printname(self):
print self.name
cc = PP(2)
print 'PP.__dict__.keys()==',PP.__dict__.keys()
print 'cc.__dict__.keys()==',cc.__dict__.keys()
print
print 'cc.data==',cc.data
print 'PP.data==',PP.data
print 'cc.__class__.data==',cc.__class__.data
result
P.__dict__.keys()== ['dataclass', '__module__', 'printname', '__init__', '__doc__']
c.__dict__.keys()== ['data']
c.data== 1
c.dataclass== shareddata
PP.__dict__.keys()== ['__module__', 'data', 'printname', '__init__', '__doc__']
cc.__dict__.keys()== ['data']
cc.data== 2
PP.data== shareddata
cc.__class__.data== shareddata
.
Note:
dir([object])
With an argument, attempt to return a list of valid attributes for that object.
If the object does not provide dir(), the function tries its best to gather information from the object’s dict attribute, if defined, and from its type object.
The default dir() mechanism behaves differently with different types of objects, as it attempts to produce the most relevant, rather than complete, information:
•If the object is a type or class object, the list contains the names of its attributes, and recursively of the attributes of its bases.
•Otherwise, the list contains the object’s attributes’ names, the names of its class’s attributes, and recursively of the attributes of its class’s base classes.
http://docs.python.org/library/functions.html#dir
.
Hence, the use of dir(ob)
to display the attributes of the object ob
is a trap because it display more attributes than the ones belonging strictly to the object.
In other words, __dict__ is the real thing, while dir() gives a dashboard, in a sense.
r6d9, please, when you write an update, you should put the date and hour by the word UPDATE. It begins to be complcated to follow all that
.
.
Concerning this code of you:
def replaceinstance(parent,child):
for item in parent.__dict__.items():
child.__dict__.__setitem__(item[0],item[1])
print item
class P(object):
def __init__(self,pvar):
self.pvar = pvar
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject):
P.__init__(self,None)
replaceinstance(pobject,self)
self.name = name
it can be replaced by this one:
class P(object):
def __init__(self,pvar):
self.pvar = pvar
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject):
P.__init__(self,None)
self.pvar = pobject.pvar
self.name = name
but it seems to simple
Okay, I think you might want to rephrase your question as:
How can I extend Python's OOP to make inheritance work on the level of objects rather than classes?
First off - don't mess with the dicts: If you are just copying the entries of the parent dict over to the child-dict, this works for instantiation, but changes in any of the dicts will not automagically update entries in all the other dicts. Assigning a new value to an attribute will simply create a new reference, so the attribute will not point to the same object any more.
The solution is to tell python to look for the attribute in the right place using some Python magic ...
class ProxyMixin (object):
def __init__(self, parent):
self.parent = parent
def __getattribute__(self, name):
if name != 'parent' and hasattr(self.parent, name):
return getattr(self.parent, name)
else:
return object.__getattribute__(self, name)
def __setattr__(self, name, val):
if name != 'parent' and hasattr(self.parent, name):
setattr(self.parent, name)
else:
object.__setattr__(self, name, val)
(see the python reference on attribute access)
Just add the ProxyMixin to your child class, and you will be fine.
class P:
data = "shared data"
def __init__(self, name):
self.name = name
def printname(self):
print self.name
class C(P, ProxyMixin):
def __init__(self, parent=None):
if parent:
ProxyMixin.__init__(self, parent)
Now youn can dynamically rewire the parent object at any time using a simple assignment:
c.parent = newparent
Finally, I found a way to do it.
The key point is to abandon the aim to obtain instances c with real pvar field, because it is impossible:
Since it is the same _init_() function (the one being in class P) that processes to create the objects pvar, it isn't possible to create pvar in instances c that will points to the pvar in an instance p to mirror its value and that will also give the possibility to change this value of a p's pvar each time a c's pvar's value will change. That makes too much contradictory conditions to verify.
Consequently, since the instances c can't have a real pvar field, the best is to set up a mechanism controling the creation of ( with _setattr_ ) and access to ( with _getattr_ ) these c's seemingly pvar objects to give the illusion that they exist.
class P(object):
def __init__(self,pvar_arg,foo="^^^ "):
self.pvar = pvar_arg
self.cat = foo
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject,foo=''):
self.__dict__['name'] = name
P.__init__(self,None,pobject.cat+foo)
C.dic[name] = pobject
def __setattr__(self,xn,val):
if xn!='pvar':
self.__dict__[xn] = val
elif self.name in C.dic:
# During the creation of an instance c,
# this condition is False because the instruction
# C.dic[name] = pobject is written after
# P.__init__(self,None,pobject.cat+foo).
# Hence the value of pobject.pvar is preserved,
# not changed with the value val being None
# due to P.__init__(self,None,pobject.cat+foo)
# that provokes self.pvar = pvar_arg and
# consequently a call __setattr__(self,'pvar',None)
C.dic[self.name].pvar = val
def __getattribute__(self,xn):
if xn=='pvar':
return object.__getattribute__(C.dic[self.name],'pvar')
else:
return object.__getattribute__(self,xn)
dic = {}
p1 = P("1")
p2 = P("2","QZX ")
print '--- p1 = P("1") and p2 = P("2","QZX ") executed ---'
print "p1.__dict__ ==",p1.__dict__
print "p2.__dict__ ==",p2.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
c1a = C("c1a",p1,'sea')
c1b = C("c1b",p1,'mountain')
c1c = C("c1c",p1,'desert')
c2a = C("c2a",p2,'banana')
c2b = C("c2b",p2)
c2c = C("c2c",p2,'pear')
print '\n--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---'
print "p1.__dict__ ==",p1.__dict__
print "p2.__dict__ ==",p2.__dict__
print "c1a.__dict__ ==",c1a.__dict__
print "c1b.__dict__ ==",c1b.__dict__
print "c1c.__dict__ ==",c1c.__dict__
print "c2a.__dict__ ==",c2a.__dict__
print "c2b.__dict__ ==",c2b.__dict__
print "c2c.__dict__ ==",c2c.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar, c1b.pvar, c1c.pvar)==',(c1a.pvar,c1b.pvar,c1c.pvar)
print '(c2a.pvar, c2b.pvar, c2c.pvar)==',(c2a.pvar,c2b.pvar,c2c.pvar)
c1a.pvar = "newpvar1"
print '\n--- c1a.pvar = "newpvar1" executed ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar, c1b.pvar, c1c.pvar)==',(c1a.pvar,c1b.pvar,c1c.pvar)
print '(c2a.pvar, c2b.pvar, c2c.pvar)==',(c2a.pvar,c2b.pvar,c2c.pvar)
c2c.pvar = 45789
print '\n--- c2c.pvar = 45789 executed ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar, c1b.pvar, c1c.pvar)==',(c1a.pvar,c1b.pvar,c1c.pvar)
print '(c2a.pvar, c2b.pvar, c2c.pvar)==',(c2a.pvar,c2b.pvar,c2c.pvar)
result
--- p1 = P("1") and p2 = P("2","QZX ") executed ---
p1.__dict__ == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__ == {'cat': 'QZX ', 'pvar': '2'}
p1.pvar== 1
p2.pvar== 2
--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---
p1.__dict__ == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__ == {'cat': 'QZX ', 'pvar': '2'}
c1a.__dict__ == {'name': 'c1a', 'cat': '^^^ sea'}
c1b.__dict__ == {'name': 'c1b', 'cat': '^^^ mountain'}
c1c.__dict__ == {'name': 'c1c', 'cat': '^^^ desert'}
c2a.__dict__ == {'name': 'c2a', 'cat': 'QZX banana'}
c2b.__dict__ == {'name': 'c2b', 'cat': 'QZX '}
c2c.__dict__ == {'name': 'c2c', 'cat': 'QZX pear'}
p1.pvar== 1
p2.pvar== 2
(c1a.pvar, c1b.pvar, c1c.pvar)== ('1', '1', '1')
(c2a.pvar, c2b.pvar, c2c.pvar)== ('2', '2', '2')
--- c1a.pvar = "newpvar1" executed ---
p1.pvar== newpvar1
p2.pvar== 2
(c1a.pvar, c1b.pvar, c1c.pvar)== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar, c2b.pvar, c2c.pvar)== ('2', '2', '2')
--- c2c.pvar = 45789 executed ---
p1.pvar== newpvar1
p2.pvar== 45789
(c1a.pvar, c1b.pvar, c1c.pvar)== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar, c2b.pvar, c2c.pvar)== (45789, 45789, 45789)
Remarks:
the attribute name must be defined before the instruction
P.init(self,None,pobject.cat+foo)
because the execution of this instruction calls
__setattr__(self,'pvar',"1")
that executes itself the instructionC.dic[self.name].pvar = "1"
whenc1a = C("c1a",p1,'sea')
is executed, for example.AnHence this call needs self.name as key for dic.
I introduced foo and cat to
justify the need to write the instruction
P.init(self,None,pobject.cat+foo)
otherwise, as no pvar is in fact defined into the instances c , this instuction wouldn't be useful.
There are two situations in which
__setattr__
is called: at the creation of an instance, and at the modifications of the attributes of an existing instance. When an instance is created, the value of pvar of the instance p must remain unaffected by the instructionC.dic[self.name].pvar = None
. Hence the conditionelif self.name in C.dic:
In order that this condition gives a correct result, the instruction
C.dic[name] = pobject
must follow the call toP.__init__(self,None,pobject.cat+foo)
.
EDIT 1
I think it's better to write
def __setattr__(self,xn,val):
if xn=='pvar':
self.__class__.dic[self.name].pvar = val
than
def __setattr__(self,xn,val):
if xn=='pvar':
C.dic[self.name].pvar = val
In the first case, the interpreter has to search for the reference to the self's class C ( that is to say under the name '_class_' ) in the namespace of self.
In the second case, the interpreter must search for the same reference ( but under the name 'C') in the namespace of the level in which classes P and C are defined.
This second namespace may be more more big than the limited namespace of an instance. In the first case, the name '_class_' is looked for as a key in the dictionary implementing the self's namespace. In the second, the name 'C' is the key searched for in the dictionary of the level inclosing the classes P and C.
The identity of these two objects can be verified with the function id()
.
.
EDIT 2
There is another possibility for the dic
object: instead of making it a class attribute of the class C, it can be defined in the outer scope of the class C. If this outer level is a module, then dic is a global object.
class P(object):
def __init__(self,pvar,foo="^^^ "):
self.pvar = pvar
self.cat = foo
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject,foo=''):
self.__dict__['name'] = name
P.__init__(self,None,pobject.cat+foo)
dic[name] = pobject
def __setattr__(self,xn,val):
if xn!='pvar':
self.__dict__[xn] = val
elif self.name in dic:
# During the creation of an instance c,
# this condition is False because the instruction
# dic[name] = pobject is written after
# P.__init__(self,None,pobject.cat+foo).
# Hence the value of pobject.pvar is preserved,
# not changed with the value val being None
# due to P.__init__(self,None,pobject.cat+foo)
# that provokes self.pvar = pvar_arg and
# consequently a call __setattr__(self,'pvar',None)
dic[self.name].pvar = val
def __getattribute__(self,xn):
if xn=='pvar':
return object.__getattribute__(dic[self.name],'pvar')
else:
return object.__getattribute__(self,xn)
dic = {}
The result is exactly the same
Doing so, dic looses its OOish nature.
.
.
EDIT 3
At last, there is still another way: instead of creating an illusory attribute pvar for each instance c with help of functions __setattr__
and __getattribute__
, it is better, according to me, to use a function with the dictionary dic as a default argument and that will replace them.
class P(object):
def __init__(self,pvar,foo="^^^ "):
self.pvar = pvar
self.cat = foo
def printname(self):
print self.name
class C(P):
def __init__(self,name,pobject,foo=''):
P.__init__(self,None,pobject.cat+foo)
self.__dict__['name'] = name
del self.pvar
self.pvar(pobject)
def pvar(self,x = None,dic = {}):
if x.__class__==P: # a pobject
dic[self.name] = x
elif x: # a value
dic[self.name].pvar = x
else: # to return value
return dic[self.name].pvar
p1 = P("1")
p2 = P("2","QZX ")
print '--- p1 = P("1") and p2 = P("2","QZX ") executed ---'
print "p1.__dict__ ==",p1.__dict__
print "p2.__dict__ ==",p2.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
c1a = C("c1a",p1,'sea')
c1b = C("c1b",p1,'mountain')
c1c = C("c1c",p1,'desert')
c2a = C("c2a",p2,'banana')
c2b = C("c2b",p2)
c2c = C("c2c",p2,'pear')
print '\n--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---'
print "p1.__dict__ ==",p1.__dict__
print "p2.__dict__ ==",p2.__dict__
print "c1a.__dict__ ==",c1a.__dict__
print "c1b.__dict__ ==",c1b.__dict__
print "c1c.__dict__ ==",c1c.__dict__
print "c2a.__dict__ ==",c2a.__dict__
print "c2b.__dict__ ==",c2b.__dict__
print "c2c.__dict__ ==",c2c.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar(),c1b.pvar(),c1c.pvar())==',(c1a.pvar(),c1b.pvar(),c1c.pvar())
print '(c2a.pvar(),c2b.pvar(),c2c.pvar())==',(c2a.pvar(),c2b.pvar(),c2c.pvar())
c1a.pvar("newpvar1")
print '\n--- c1a.pvar("newpvar1") executed ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar(),c1b.pvar(),c1c.pvar())==',(c1a.pvar(),c1b.pvar(),c1c.pvar())
print '(c2a.pvar(),c2b.pvar(),c2c.pvar())==',(c2a.pvar(),c2b.pvar(),c2c.pvar())
c2c.pvar(45789)
print '\n--- c2c.pvar(45789) ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar(),c1b.pvar(),c1c.pvar())==',(c1a.pvar(),c1b.pvar(),c1c.pvar())
print '(c2a.pvar(),c2b.pvar(),c2c.pvar())==',(c2a.pvar(),c2b.pvar(),c2c.pvar())
Results are the same, only use of c.pvar() is slightly different:
--- p1 = P("1") and p2 = P("2","QZX ") executed ---
p1.__dict__ == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__ == {'cat': 'QZX ', 'pvar': '2'}
p1.pvar== 1
p2.pvar== 2
--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---
p1.__dict__ == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__ == {'cat': 'QZX ', 'pvar': '2'}
c1a.__dict__ == {'cat': '^^^ sea', 'name': 'c1a'}
c1b.__dict__ == {'cat': '^^^ mountain', 'name': 'c1b'}
c1c.__dict__ == {'cat': '^^^ desert', 'name': 'c1c'}
c2a.__dict__ == {'cat': 'QZX banana', 'name': 'c2a'}
c2b.__dict__ == {'cat': 'QZX ', 'name': 'c2b'}
c2c.__dict__ == {'cat': 'QZX pear', 'name': 'c2c'}
p1.pvar== 1
p2.pvar== 2
(c1a.pvar(),c1b.pvar(),c1c.pvar())== ('1', '1', '1')
(c2a.pvar(),c2b.pvar(),c2c.pvar())== ('2', '2', '2')
--- c1a.pvar("newpvar1") executed ---
p1.pvar== newpvar1
p2.pvar== 2
(c1a.pvar(),c1b.pvar(),c1c.pvar())== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar(),c2b.pvar(),c2c.pvar())== ('2', '2', '2')
--- c2c.pvar(45789) ---
p1.pvar== newpvar1
p2.pvar== 45789
(c1a.pvar(),c1b.pvar(),c1c.pvar())== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar(),c2b.pvar(),c2c.pvar())== (45789, 45789, 45789)
Note that in this last code P's instances can't be values of C'instances because a pobject passed to a c.pvar() method will never be considered as a value.
精彩评论