基本继承(在python中)

时间:2011-02-24 12:02:14

标签: python oop inheritance

用户,

我有一个关于继承的基本问题(在python中)。我有两个类,其中一个继承自另一个类

class   p:
    def __init__(self,name):
        self.pname  = name

class   c(p):
    def __init__(self,name):
        self.cname  = name

我是否有可能创建父对象和几个引用SAME父对象的子对象?它应该像父对象包含几个变量那样工作,每当我从子进入相应的变量时,我实际上从父进程访问变量。即如果我为一个孩子更改它,它也会被更改为所有其他childes,并且数据仅存储在内存中一次(并且不会为每个孩子复制...)

提前谢谢。

这是一个可行的解决方法,我不认为这么好

class P:
    def __init__(self, name):
        self.pname = name

class C:
    def __init__(self, name,pobject):
        self.pobject = pobject
        self.cname = name

这真的是最先进的还是存在其他概念?

塞巴斯蒂安

谢谢大家帮助我,还有名称约定:)但我仍然不太满意。也许我会举一个更高级的例子来强调我真正想做的事情。

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

现在我可以做以下

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'

到目前为止,这正是我想要的。每个子节点都有一个不同的变量名,父类访问各个变量。正常继承。 然后是来自父类的可变数据,每个子进程都访问它。但是,现在以下内容不再适用

In [39]: c1.data = "tst"

In [40]: c2.data
Out[40]: 'shareddata'

In [41]: c1.data
Out[41]: 'tst'

我希望c1.data中的更改也影响c2.data,因为我希望共享变量,不知何故是这个父类的全局变量。

还有更多。我还想创建P的不同实例,每个实例都有自己的数据变量。当我创建一个新的C对象时,我想指定应该从哪个P对象数据中获取,即共享....

更新:

评论@eyquem的评论:谢谢你,它正朝着我想要的方向发展。但是,现在__class__.pvar在类的所有对象之间共享。我想要的是P的几个实例可能有不同的pvar。让我们假设P1有pvar = 1而P2有pvar = 2。然后我想创建与P1相关的子C1a,C1b,C1c,即如果我说C1a.pvar它应该从P1获得pvar。然后我创建C2a,C2b,C2c,如果我访问,即C2b.pvar,我想从P2访问pvar。由于C类从P类继承pvar,因此C的已知。我天真的想法是,如果我创建CI的新实例应该能够指定哪个(现有的)P对象应该用作父对象而不是创建一个全新的P对象,因为它在C P.__init__内调用__init__时完成...对我来说听起来很简单,也许我忘记了......

更新:

所以我发现this discussion这几乎是我的问题

有什么建议吗?

更新:

方法。 ._ 子类 _似乎不再存在..

更新:

这是另一个链接:

link to discussion

通过复制解决了问题。但我不想复制父类,因为我希望它只存在一次......

更新:

很抱歉昨天离开讨论,我有点不舒服...谢谢你的帖子!我现在将通读它们。我考虑了一下,这是我找到的可能的解决方案

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)

这有点麻烦,我希望有一种更简单的方法来实现这一目标。但它的特点是pvar只在P类中提到,C类不知道pvar,因为它应该根据我对继承的理解。然而,当我创建一个新的C实例时,我可以指定一个现有的P实例,它将存储在变量pobject中。当访问变量pvar时,实际访问存储在该变量中的P实例的pvar ......

输出由

给出
3078326816
3078326816
3078326816
3074996544
3074996544
3074996544
3078326816
3074996544
156582944
156583040
156583200
156583232
156583296
156583360

我现在将通过你的上一篇评论阅读,

最好的,塞巴斯蒂安

更新:

我认为最优雅的方式是以下(不起作用)

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

我认为python应该允许这个......

更新:

好的,由于eyquem的解释,现在我找到了实现这个目标的方法。但由于这真的是一个黑客应该有一个正式版本...

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)

输出与上面相同

3077745184
3077745184
3077745184
3074414912
3074414912
3074414912
3077745184
3074414912
144028416
144028448
144028480
144028512
144028544
144028576

更新:即使id似乎是正确的,最后一个代码也无法正常运行

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

它有输出

newpvar1
1
1
2
2
2
1
2

然而,我首先发布的版本有效:

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

输出

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

有人知道为什么会这样吗?我可能不明白python使用这个__dict__的内部方式......

8 个答案:

答案 0 :(得分:5)

  

它应该像父对象包含几个变量一样工作,每当我从子进入相应的变量时,我实际上从父进程访问变量。即如果我为一个孩子更改它,它也会被更改为所有其他childes,并且数据仅存储在内存中一次(并且不会为每个孩子复制...)

这不是继承。

这是一个完全不同的概念。

您的“共享变量”只是可以变异的对象,并且在其他对象中具有引用。没什么有趣的。

继承与此完全不同。

答案 1 :(得分:1)

我认为你的解决方法是可行的;您可以使用属性更轻松地访问P的属性:

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')

c1c2共享相同的pobject

print(c1.pvar)
# 1
c1.pvar=2

请注意,将pvar更改为c1会更改c2.pvar

print(c2.pvar)
# 2

c3有不同的pobject

c3=C('3',P())
print(c3.pvar)
# 1

关于心理学实验的OOP设计(在评论中提到):

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

Picture肯定不是Person,反之亦然。 Trial也是如此。所以这些类都不应该相互继承。

这些类中的每一个都有一个公共(也许是私有)接口。 公共方法和属性应该可以从其他类中自由访问。 因此,在Trial实例t的情况下,应该可以通过t.picture.image访问该图像。只要您只访问公共属性和方法,那么一切都应该没问题。

为方便起见,您可以使用properties将属性链接到组件属性。例如:

class Trial(object):
    ...
    @property
    def image(self):
        return self.picture.image

但是,通过使Trial成为Picture的子类来违反基本的OOP设计原则。

答案 2 :(得分:1)

另一个答案是对的,你的问题更多是关于命名空间和引用而不是关于继承。

Python中的所有变量都是引用,所有对象实例都是命名空间。所以你可以这样做:

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

更新:

但请注意,很容易出错:

>>> 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'
>>> 

我认为最先进的技术是使用properties隐藏私有实例变量中的__shared_instance - 所以你可以使用c1.x而不是c1.x.value来避免拼写错误就像上面的例子一样。

答案 3 :(得分:0)

我迷失在所有这些不同的答案中。

但我认为您需要的内容在以下代码中表达:

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)

结果

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

我的意思是你必须知道的是,通过直接暗示实例名称的指令(而不是通过仅暗示父类名称的更改)来改变类属性 pvar 它继续被所有 P 的实例共享,你必须写

c2.__class__.pvar = something 

而不是

c2.pvar =something

请注意,C是一个有效继承父类P

的类

答案 4 :(得分:0)

您必须知道的一件事是作为理解类和实例的功能的基础:

  

类实例具有命名空间   实现为字典   属性的第一个位置   搜索引用

     

当在那里找不到属性时,   而实例的类有一个   属性由该名称,搜索   继续使用类属性。

     

http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

在第二句中,我并不完全理解“通过该名称”的含义,但我理解全局,首先在实例的命名空间中搜索属性,然后在其类型的命名空间中搜索。

在以下代码中:

  • 对于课程P和实例c

名称'dataclass'和对象dataclass确实属于P类的命名空间,只有APPARENTLY属于c实例的命名空间:when {{通过上述搜索过程调用1}},实际上是c.dataclass

  • 但是在类c.__class__.dataclass的实例cc中,分配了名称'data',它属于PP类的命名空间(绑定)通过P中出现的定义到__init__()实例名称空间中创建的新data对象。

因此,获得该类c值的唯一解决方案是通过其真实引用(dataPP.data)来调用它。

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

注意:

  

<强> DIR([对象])

     

用一个参数,尝试返回一个   该有效属性的列表   对象

     

如果对象没有提供    dir (),该函数尽力从中收集信息   对象的 dict 属性,如果   定义,并从其类型对象。

     

默认的dir()机制表现   不同类型的   对象,因为它试图产生   最相关的,而不是完整的,   信息:

     

•如果对象是类型或类   对象,该列表包含的名称   它的属性,以及递归的   其基础的属性。

     

•否则,列表中包含   对象的属性名称,名称   其类的属性,和   递归地说它的属性   class的基类。

     

http://docs.python.org/library/functions.html#dir

因此,使用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 来显示对象dir(ob)的属性是一个陷阱,因为它显示的属性多于严格属于对象的属性。

换句话说, __ dict __ 是真实的,而 dir()在某种意义上提供了一个信息中心。

答案 5 :(得分:0)

r6d9,请在编写更新时,将日期和小时改为UPDATE。它开始被要求遵循所有这些

关于你的这段代码:

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

它可以被这个替换:

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

但似乎很简单

答案 6 :(得分:0)

最后,我找到了一种方法。

关键是放弃使用真实 pvar 字段获取实例 c 的目标,因为这是不可能的:

因为它是相同的 _ init _()函数(在P类中的那个)处理以创建对象 pvar ,无法在 c 的实例中创建 pvar ,这将指向实例 p 中的 pvar 反映它的价值,每次 c 时,也可​​以改变 p pvar 的这个值> pvar 的价值会发生变化。这使得过于矛盾的条件无法验证。

因此,由于实例 c 不能有真正的 pvar 字段,最好是设置一个控制创建的机制(使用 _ setattr _ )并访问(使用 _ getattr _ )这些 c 的看似< strong> pvar 对象给出了它们存在的错觉。

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)

结果

--- 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)

说明:

  1. 必须在指令

    之前定义属性名称

    P.的初始化(个体,无,pobject.cat +富)

    因为执行该指令调用

    __setattr__(self,'pvar',"1")执行指令

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

    时 例如,执行

    c1a = C("c1a",p1,'sea')

    AnHence此调用需要 self.name 作为dic的关键。

  2. 我将 foo cat 介绍给

    证明需要编写指令

    P.的初始化(个体,无,pobject.cat +富)

    否则,由于 pvar 实际上没有定义到 c 的实例中,因此该实施将无用。

  3. 有两种情况 调用__setattr__:创建实例,以及修改现有实例的属性。创建实例时,实例 p pvar 值必须不受指令C.dic[self.name].pvar = None的影响。因此条件elif self.name in C.dic:

    为了使此条件得出正确的结果,指令C.dic[name] = pobject必须跟随对P.__init__(self,None,pobject.cat+foo)的调用

  4. 编辑1

    我认为最好写

    def __setattr__(self,xn,val):
        if xn=='pvar':
            self.__class__.dic[self.name].pvar = val
    

    def __setattr__(self,xn,val):
        if xn=='pvar':
            C.dic[self.name].pvar = val
    

    在第一种情况下,口译员必须搜索对自我的类 C 的引用(也就是说,名称'_自我命名空间中的 class _')。

    在第二种情况下,解释器必须在类 P 的级别的名称空间中搜索相同的引用(但名称'C'), C 已定义。

    第二个命名空间可能比实例的有限命名空间更大。在第一种情况下,名称'_ class _'在实现self命名空间的字典中被查找为一个键。 在第二个中,名称'C'是在包含 P C 类的级别字典中搜索的键。

    可以使用 id()

    功能验证这两个对象的身份

    编辑2

    dic对象还有另一种可能性:它可以在类 C的外部范围内定义,而不是使其成为类 C 的类属性。 即可。如果此外层是一个模块,则 dic 是一个全局对象。

    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 = {}
    

    结果完全一样

    这样做, dic 失去了它的OOish性质。

    编辑3

    最后,还有另一种方法:在函数__setattr__和{{1}的帮助下,为每个实例 c 创建一个虚幻属性 pvar ,而不是根据我的说法,使用字典 dic 作为默认参数并将替换它们的函数更好。

    __getattribute__

    结果是一样的,只使用c.pvar()略有不同:

    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())
    

    请注意,在最后一个代码中 P 的实例不能是 C '实例的值,因为 pobject 传递给< strong> c.pvar()方法永远不会被视为值。

答案 7 :(得分:0)

好的,我想你可能想把你的问题改为:

如何扩展Python的OOP以使继承在对象层而不是类上工作?

首先关闭 - 不要弄乱词语: 如果您只是将父dict的条目复制到child-dict,则这适用于实例化,但任何dicts中的更改都不会自动更新所有其他dicts中的条目。为属性分配新值只会创建一个新引用,因此该属性不再指向同一个对象。

解决方案是告诉python使用Python魔术在正确的位置寻找属性......

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)

(参见python reference on attribute access

只需将ProxyMixin添加到您的子类中,您就可以了。

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)

现在,您可以使用简单的赋值随时动态重新连接父对象:

c.parent = newparent