2011-02-16 247 views
3

我想构建一些继承父类的类,它包含从其他父类继承的子类。但是,当我更改任何子项中的子类中的属性时,该更改会影响所有子类。我期望避免创建实例,因为我稍后使用该功能。Python子类继承

下面的代码归结了这个问题。最后一行显示了意想不到的结果。

class SubclsParent(object): 
    a = "Hello" 

class Parent(object): 
    class Subcls(SubclsParent): 
     pass 

class Child1(Parent): 
    pass 

class Child2(Parent): 
    pass 

Child1.Subcls.a # Returns "Hello" 
Child2.Subcls.a # Returns "Hello" 
Child1.Subcls.a = "Goodbye" 
Child1.Subcls.a # Returns "Goodbye" 
Child2.Subcls.a # Returns "Goodbye"/Should still return "Hello"! 
+1

你正在处理类,而不是类的实例。那是你要的吗? – robert

+5

“我期望避免创建实例,因为我稍后使用该功能。”严重的错误观念。请找一个关于面向对象设计的更好的教程。 –

回答

8

你所看到的行为正是你应该期待的。当你定义一个类

>>> class Foo(object): pass 
... 

可以修改类 - 而不是它的实例,类本身 - 因为类是另一个对象,存储在变量foo中。所以,举例来说,你可以获取和设置类的属性:

>>> Foo.a = 1 
>>> Foo.a 
1 

换句话说,在class关键字创建一个新类型的对象和绑定指定名称到该对象。现在


,如果你定义一个类里面另一个类(这是做的,顺便说一个奇怪的事情),即相当于定义的类体中的局部变量。而且你知道在一个类的内部定义局部变量是什么:它将它们设置为类属性。换句话说,本地定义的变量存储在类对象中,而不是存储在单个实例中。因此,

>>> class Foo(object): 
...  class Bar(object): pass 
... 

定义了一类Foo与一种类属性,Bar,这恰好本身是一类。尽管如此,这里没有任何子类化 - 类别FooBar是完全独立的。(你已经实现可复制的行为如下:

>>> class Foo(object): 
...  class Bar(object): pass 
... 
>>> class Foo(object): pass 
... 
>>> class Bar(object): pass 
... 
>>> Foo.Bar = Bar 

。)

所以你总是修改同一个变量!当然,你会改变你看到的价值;你已经改变了他们自己!


你的问题似乎是你在实例和类属性之间有些混淆,它们根本不是一回事。

A 属性是一个定义在整个类别上的变量。也就是说,该类的任何实例都将共享相同的变量。例如,大多数方法都是类属性,因为您希望在每个实例(通常)上调用相同的方法。您还可以使用类属性来处理全局计数器(您已实例化此类的次数?)以及应该在实例之间共享的其他属性。

一个实例属性是一个类的实例特有的变量。也就是说,每个实例都有一个不同的变量副本,可能有不同的内容。这是您将数据存储在类中的位置 - 如果您有Page类,那么您希望contents属性按每个实例存储,因为不同的Page s当然需要不同的contents

在你的例子中,你想要Child1.Subcls.aChild2.Subcls.a不同的变量。自然,那么,他们应该依赖于实例!


这可能有点信仰的飞跃,但是你想在Python中实现Java风格的接口?换句话说,你是否试图指定所具有的属性和方法,而不实际定义这些属性?

这被认为是非pythonic的事情,因为普遍的共识是你应该允许类做任何他们想做的事,并且捕获当它们没有定义需要的属性时产生的异常或方法。然而,最近人们已经意识到接口实际上有时是一件好事,并且新的功能被添加到Python以允许:abstract base classes

3

试试这个

class SubclsParent(object): 
    def __init__(self): 
     self.a = "Hello" 

当你直接在类中定义SubclsParent.a,你将其定义为静态的。

+0

+1简洁明了的答案。一般信息:Python调用“静态字段”“类属性”,一个方法也是类属性,它恰好是可调用的。 – Skurmedel

+0

问题在于它需要我创建一个Subcls实例才能访问'a' – Conor

+2

问题是,当python解释器遍历代码并创建类时,变量a是一个引用到一个对象,一个“你好”字符串。当你创建子类时,它们都只是继承了相同的静态引用。内存中只有一个“hello”对象。你将不得不在每个子类上定义一个新的字符串对象或重写这个对象。或者,如果你想成为邪恶的方式,你真的,真的,不应该这样做,导入复制模块,并且当你定义子类时,定义a = copy.deepcopy(SubclsParent.a) –

1

当您使用Child1.Subcls时,python会看到没有Child1.Subcls,因此会检查Parent并找到它并将其返回。 Child2.Subcls发生同样的事情。因此,这两个表达式都指向同一个类。 Child1和Child2不会获得它们自己的子类,而是可以访问原始类。

* 我正在寻找避免必须创建实例,因为我稍后使用该功能。 *

我不明白你在这里的意思。

0

这可能是你真正想要的是metaclasses

+0

这不是元类的候选人。 OP甚至不理解类和类实例。 – Keith

+0

@Keith:我对这个问题的阅读表明,他们确实从“稍后使用该功能”位理解了类实例。也许我错了。 – nmichaels

+0

不幸的是,孩子的子类的元类将在孩子完全定义之前运行。我在大型方案中试图做的是在我开始创建这些子类的实例之前,使用子类的元类来定义其子类的属性(例如'a')。 (对不起,如果我的问题的措辞是低于标准) – Conor

1

你的问题是,当你访问属性时,你正在访问父类中创建的所有引用相同变量的继承例程。您可以创建这些实例变量,也可以在子类中创建属性以获取独立属性。