开发者

basic inheritance (in python)

开发者 https://www.devze.com 2023-02-12 23:16 出处:网络
users, I have a basic question concerning inheritance (in python). I have two classes and one of them is inherited from the other like

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 Trials. 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 instance c :

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 class PP , the name 'data' , which belongs to the P class's namespace, is assigned (binded) by the definition occuring in __init__() to a new data object created in the c 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:

  1. 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 instruction

    C.dic[self.name].pvar = "1" when

    c1a = C("c1a",p1,'sea') is executed, for example.

    AnHence this call needs self.name as key for dic.

  2. 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.

  3. 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 instruction C.dic[self.name].pvar = None . Hence the condition elif self.name in C.dic:

    In order that this condition gives a correct result, the instruction C.dic[name] = pobject must follow the call to P.__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.

0

精彩评论

暂无评论...
验证码 换一张
取 消