我正在尝试构建一些从父类继承的类,其中包含从其他父类继承的子类。但是当我在任何子级中更改子类中的属性时,更改会影响所有子类。我希望避免创建实例,因为我稍后使用该功能。
以下代码归结为问题。最后一行显示了意想不到的结果。
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"!
答案 0 :(得分:9)
您所看到的行为正是您应该期待的。定义类
时>>> class Foo(object): pass
...
您可以修改该类 - 不是它的实例,类本身 - 因为该类只是另一个对象,存储在变量Foo中。因此,例如,您可以获取并设置类的属性:
>>> Foo.a = 1
>>> Foo.a
1
换句话说,class
关键字创建一种新类型的对象,并将指定的名称绑定到该对象。
现在,如果在里面定义一个类另一个类(顺便说一句,这是一件奇怪的事情),这相当于在类体中定义一个局部变量。而且你知道在类体内定义局部变量的作用是什么:它将它们设置为类属性。换句话说,本地定义的变量存储在类对象中,而不是存储在单个实例上。因此,
>>> class Foo(object):
... class Bar(object): pass
...
定义了一个具有一个类属性Foo
的类Bar
,它本身就是一个类。但是这里没有子类化 - 类Foo
和Bar
完全独立。 (您已经实现的行为可以如下复制:
>>> class Foo(object):
... class Bar(object): pass
...
>>> class Foo(object): pass
...
>>> class Bar(object): pass
...
>>> Foo.Bar = Bar
)
所以你总是修改同一个变量!当然,你会改变你看到的价值观;你自己改变了它们!
您的问题似乎是您在实例和类属性之间有些混淆,这些属性完全不同。
class 属性是在整个类上定义的变量。也就是说,该类的任何实例都将共享相同的变量。例如,大多数方法都是类属性,因为您希望在每个实例上调用相同的方法(通常)。您还可以将类属性用于全局计数器(您实例化此类的次数?)以及应在实例之间共享的其他属性。
实例属性是类实例特有的变量。也就是说,每个实例都有一个不同的变量副本,可能含有不同的内容。这是您将数据存储在类中的位置 - 例如,如果您有Page
类,您希望每个实例存储contents
属性,因为不同的Page
将会当然需要不同的contents
。
在您的示例中,您希望Child1.Subcls.a
和Child2.Subcls.a
是不同的变量。当然,他们应该依赖于实例!
这可能是一种信念的飞跃,但您是否尝试在Python中实现Java风格的界面?换句话说,您是否尝试指定类应该具有哪些属性和方法,而不实际定义这些属性?
这曾经被认为是非Pythonic要做的事情,因为普遍的共识是你应该允许这些类做任何他们想做的事情并抓住他们没有定义所需属性时出现的异常或方法。然而,最近人们已经意识到接口实际上有时是一件好事,并且Python中添加了新功能以允许这样做:abstract base classes。
答案 1 :(得分:3)
试试这个
class SubclsParent(object):
def __init__(self):
self.a = "Hello"
直接在类上定义SubclsParent.a时,您将其定义为static。
答案 2 :(得分:1)
当您使用Child1.Subcls时,python看到没有Child1.Subcls,因此检查Parent找到它并返回它。 Child2.Subcls也发生了同样的事情。结果,这两个表达式都引用了同一个类。 Child1和Child2没有得到它们自己的子类,而是可以访问原始的。
* 我希望避免创建实例,因为我稍后会使用该功能。 *
我不明白你的意思。
答案 3 :(得分:1)
您的问题是,当您访问属性时,您正在访问在父类中创建的继承例程,这些例程都引用同一个变量。您可以创建这些实例变量,也可以在子类中创建属性以获取独立属性。
答案 4 :(得分:0)
您真正想要的可能是metaclasses。