我们说classes
在Python中是可变的,这意味着你可以使用引用我们可以改变将在对象中反映的值。例如,
>>> A = [1, 2, 3]
>>> B = A
>>> B[2] = 5
>>> A
[1, 2, 5]
在这里,我可以使用A
更改B
对象的值,因为list
是一个可变类型。我的问题是为什么我不能使用相同的概念更改下面的类的属性:
class C:
apple = 2
def __init__(self):
self.dangerous = 2
D = C # D is pointing to same class C
D().dangerous = 5 # changing the value of class attribute D
D().apple = 3 # changing the value of apple here
print D().apple
print D().dangerous
OUTPUT:
2
2
任何人都可以解释为什么输出是2
和2
而不是3
和5
,因为我们说这个类是mutable
类型。
更新:参考@ zxq9的答案,如果你看到D=C
时的下图,D
实际上是指向同一个类而不是你所描述的新对象。你能解释一下吗:
每次在类之后放置parens时,您都在构造类的新实例对象。因此,您打印的内容是新品牌,并没有反映您之前制作的短期作品。
这是一个示例(扩展为涵盖对C类的基础引用):
>>> class C:
... red = 2
... def __init__(self):
... self.blue = 2
...
>>> C.red
2
>>> C.blue
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'blue'
>>> C().red
2
>>> C().blue
2
>>> #OOOOH!
...
>>> z = C()
>>> z.red
2
>>> z.blue
2
>>> D = C
>>> D.red
2
>>> D().red
2
>>> D().red = "over 9000!"
>>> D.red
2
>>> D.red = "No, really over 9000!"
>>> D.red
'No, really over 9000!'
>>> C.red
'No, really over 9000!'
>>> #OOOOOOHHHH!
...
请注意,我在分配D.red = "No, really over 9000!"
时直接更改了类 - 因为它引用了类定义本身,而不是从它创建的实例化对象。另请注意,分配D(副本)的属性会更改C(原始)的属性,因为在许多(但不是全部)情况下,Python通过引用进行此类分配,这意味着D实际上是C的别名,而不是副本底层结构。阅读Python的deepcopy()方法,了解更多有关特别令人吃惊的细节的信息。
仔细阅读示例代码,注意引用ClassName
和调用ClassName()
之间的区别。第一个是通过变量名称引用到类定义的引用 - 用于生成带有构造函数__init__()
的实例对象的蓝图。第二个是__init__()
的调用,其返回值是定义它的类的实例对象。
这也是你可以做这样的事情的原因:
def some_fun(another_fun, value):
another_fun(value)
def foo(v):
return v + v
def bar(v):
return v * v
some_fun(foo, 5)
some_fun(bar, 5)
此功能为Python在构建功能抽象方面提供了高度的灵活性。 (现在,如果它只有尾声消除......)
这是一个有趣的例子。