Python类成员
Python类成员
我正在学习Python,之前有C的背景,如果我在两者之间有任何混淆/混合,请告诉我。
假设我有以下类:
class Node(object): def __init__(self, element): self.element = element self.left = self.right = None @classmethod def tree(cls, element, left, right): node = cls(element) node.left = left node.right = right return node
这是一个名为`Node`的类,它重载了构造函数,以便能够处理不同的参数(如果需要的话)。
在上述代码中,仅在`__init__`中定义`self.element`和下面这样做之间有什么区别:
class Node(object): element, left, right = None def __init__(self, element): self.element = element self.left = self.right = None
在`__init__`中的`self.element`和类定义的`element`变量不是相同的吗?这不只是将`element`从`None`覆盖为传递给`__init__`的`element`值吗?
Python中的类成员问题是由于构造函数(即__init__
方法)中的self.element
是一个实例变量(如果一个节点对象修改它的值,它只会对该对象生效),而第二个版本中的self.element
是一个类变量(因此,如果一个节点对象修改它的值,它将对所有节点对象生效)。
在C++中,类成员变量的类比是非静态成员变量与静态成员变量。
解决这个问题的方法是将self.element
声明为类变量,而不是实例变量。这可以通过将其放在类的__init__
方法外部来实现,或者在构造函数内使用ClassName.element
来引用它。
下面是解决这个问题的示例代码:
class Node: element = None def __init__(self, element): self.element = element
通过将element
放在类的外部,它成为了一个类变量,而不是实例变量。这样,无论节点对象如何修改它的值,它都会对所有节点对象生效。
在Python中,可以同时拥有相同名称的类变量和实例变量。它们在内存中是分开存储的,并且访问方式也有所不同。
在你的代码中:
class Node(object): element, left, right = None def __init__(self, element): self.element = element self.left = self.right = None
第一组变量(在`__init__`函数之外)被称为类变量。可以通过`Node.element`等方式访问这些变量。它们相当于C++中的静态成员变量,它们被所有类的实例共享。
第二组变量(在`__init__`函数之内)被称为实例变量。可以通过`self`对象访问,例如`self.element`,或者在类外部通过实例名称访问,例如`myNode.element`。
重要的是要注意,如果要在成员函数中访问这两者中的任何一个,必须使用`self.variable`或`Node.variable`的形式。如果只访问`variable`,则会尝试访问一个名为`variable`的局部变量。
Python类成员(Python Class Members)之间的区别在于一个是类属性,而另一个是实例属性。尽管它们在某些情况下看起来相似,但它们实际上是有密切关联的。
这与Python查找属性的方式有关。有一个层次结构。在简单的情况下,它可能是这样的:
实例(instance) -> 子类(Subclass) -> 父类(Superclass) -> 对象(object,内置类型)
当你像这样在实例上查找属性时...
instance.val
...实际上发生的是,首先Python会在实例本身中查找val。然后,如果它在实例中找不到val,它会在其类Subclass中查找。然后,如果它在Subclass中找不到val,它会在Subclass的父类Superclass中查找。
这意味着当你这样做...
class Foo():
foovar = 10
def __init__(self, val):
self.selfvar = val
...Foo的所有实例共享foovar,但它们各自拥有自己的selfvar。以下是一个简单的具体示例:
f = Foo(5)
f.foovar
10
Foo.foovar
10
如果我们不修改foovar,它对f和Foo来说是相同的。但是如果我们更改f.foovar...
f.foovar = 5
f.foovar
5
Foo.foovar
10
...我们添加了一个实例属性,它有效地屏蔽了Foo.foovar的值。现在,如果我们直接更改Foo.foovar,它不会影响我们的foo实例:
Foo.foovar = 7
f.foovar
5
但是它会影响一个新的foo实例:
Foo(5).foovar
7
还要记住,可变对象会添加另一层间接性。在这里,f.foovar引用的是与Foo.foovar相同的对象,因此当你修改对象时,更改会向上传播到层次结构的上层:
Foo.foovar = [1]
f = Foo(5)
f.foovar[0] = 99
Foo.foovar
[99]
很棒的答案!在Foo.foovar是一个可变对象(如列表)的情况下,这些情况甚至会变得更有趣。