Python疯狂的可变性

3
这段代码应该打印什么?
class Parent():
    class Meta(object):
        classattr = "Hello"

class Child(Parent):
    pass


Child.Meta.classattr = "world"


ch = Child()
pr = Parent()

ch.Meta.classattr = "Oppa"

print Parent.Meta.classattr
print Child.Meta.classattr

print pr.Meta.classattr
print ch.Meta.classattr

我期望的情况是:

Hello
world
Hello
Oppa

但是得到了

Oppa
Oppa
Oppa
Oppa

所以...我可以通过修改子类实例来修改父类(不是实例!)的内容,这正常吗?
3个回答

6

Child.Meta实际上就是Parent.Meta——Child类没有自己的Meta类属性,所以属性查找规则会解析为Parent.Meta。另外,由于它是一个类属性,对Parent的任何实例(包括Child的实例)进行查找都将解析为相同的类对象。


2
可以通过运行 ch.Meta is pr.Meta 轻松验证。 - Hannes Ovrén
好的,但我已经尝试使用深拷贝,但什么也没有改变:import copy class Parent(object): class IntClass: intclassattr = ["Hello"] Child = copy.deepcopy(Parent) Child.IntClass.intclassattr.append("world") print Parent.IntClass.intclassattr结果是 ['Hello', 'world']。但是深拷贝应该递归地复制对象的所有字段,如何从 Parent 继承并更改类字段而不更改 Parent 实例中的字段? - Makc
1
copy 模块的文档中可以看到:"此版本不会复制模块、类、函数、方法等类型...". 在执行 Child = copy.deepcopy(Parent) 后,你可以检查 Child is Parent 的值为 True。 - bruno desthuilliers

1

我会尝试解释这里发生了什么:

class Parent():
    class Meta(object):
        classattr = "Hello"

class Child(Parent):
    pass


Child.Meta.classattr = "world"

在这里,你正在将“world”分配给Meta的classattr。无论Meta最初是否在Parent内定义,Child和Parent共享相同的Meta。
ch = Child()
pr = Parent()

ch.Meta.classattr = "Oppa"

在这里,您正在将“world”分配给Meta的classattr。无论ch是否是实例,Meta始终是相同的对象。
print Parent.Meta.classattr
print Child.Meta.classattr

print pr.Meta.classattr
print ch.Meta.classattr

在这里,您正在打印同一元数据的classattr

0

我认为最直接的解释是元类只是一个普通的对象(类型为type),被Parent引用,并且子类化和实例化并不会复制元类,而只是创建了对其的新引用(或者严格来说,甚至不是这样——这只是属性查找的工作方式)。因此,每当你将新值存储到classattr中时,实际上是修改相同对象的相同属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接