Python类成员

68

我正在学习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

self.element__init__中不是与类的定义的element变量相同吗?这难道不会仅仅将elementNone覆盖为传入到__init__中的element值吗?

7个回答

111

一个是类属性,另一个是实例属性。它们不同,但它们在某些方面密切相关,有时看起来相同。

这与Python查找属性的方式有关。存在一个层次结构。在简单情况下,它可能如下所示:

instance -> Subclass -> Superclass -> object (built-in type)

当你像这样在 instance 上查找属性时...

`instance.val`

实际发生的是,首先,Python会在实例本身中查找val。然后,如果没有在实例中找到val,它会在其类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,那么fFoo都是相同的。但是如果我们改变了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

另外请记住,可变对象添加了另一层间接性(正如mgilson提醒我的)。在这里,f.foovar 指的是与 Foo.foovar 相同的对象,因此当您更改对象时,更改会向上传播到层次结构中:

>>> Foo.foovar = [1]
>>> f = Foo(5)
>>> f.foovar[0] = 99
>>> Foo.foovar
[99]

5
非常好的答案!在Foo.foovar是可变对象(例如list)的情况下,这些事情甚至可以变得更加有趣。 - mgilson

30
在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.variableNode.variable形式从成员函数中访问它们。仅使用variable将尝试访问名为variable的局部变量。


2

__init__方法中的self.element是一个实例变量(如果节点对象修改其值,则仅对该对象更改),而第二个版本中的变量是类变量(因此,如果一个节点对象修改其值,则所有节点对象都会更改)。

C++中的类比是你的类中的非静态成员变量与静态成员变量。


1
重要的部分是__init__中的self参数。实际上,在任何实例方法中,这将是第一个参数。这是有意设计的;在Python中,您实际上只有在方法调用期间才能访问实例,并且使用self参数明确显示它。
当您在class定义内部时,还没有任何实例,因此您真正修改的是类本身。因此,如果您在类级别定义属性,则它们实际上成为类属性,而不是实例属性。
将其与C++进行比较,您可能可以说这些语言中的“类”基本上是它们所代表的对象的蓝图。 “这些对象应具有foobar属性,并且还具有以下方法。”然而,在Python中,类本身也是对象,它们的主要优势在于它们可以创建自己的副本(实例),这些副本也恰好使用类的方法。因此,更像是“它们应该具有foobar作为类属性,并且还应该具有以下方法,你可以使用它来创建实例。”
所以,它不是蓝图,而更像是一步一步的操作指南。

0

当您尝试使用类“notation”访问变量时,它只会查找

cls.__dict__

但是当您尝试使用实例“notation”访问变量时,它首先查找

self.__dict__ 

如果找到了所需的成员,则返回该成员,如果找不到,则还会查找。
cls.__dict__

这里的 cls 是类

class Test:
    temp_1=10
    temp_2=20

    def __init__(self):
        self.test_1=10
        self.test_2=20

    @classmethod
    def c_test(cls):
        pass

    def t_method(self):
        pass


print Test.__dict__
print Test().__dict__

输出:

{'c_test': <classmethod object at 0x7fede8f35a60>, '__module__': '__main__', 't_method': <function t_method at 0x7fede8f336e0>, 'temp_1': 10, '__doc__': None, '__init__': <function __init__ at 0x7fede8f335f0>, 'temp_2': 20}

{'test_2': 20, 'test_1': 10}

详细信息请参见class special attribute


0

self.element__init__ 中是一个实例变量,您可以通过键入 self.element 在任何其他成员函数中获取/设置它。在类中声明的 element 是类变量,您可以通过键入 Node.element 来获取/设置它。


0

我想通过一个不同的“视角”来回答这个问题,上面没有任何一篇回答提到过(并不是说他们不好!!),即对象的“大小”。

前言:在Python中,无论好坏,都可以在运行时向特定对象实例“创建”(添加)一个“新”的(附加的)INSTANCE类成员。也就是说,如果一个特定类有两个实例成员,那么在对象实例创建后可以添加第三个实例成员,它将仅添加到该类的此特定实例中(即类的“蓝图”不会改变)。请参见下面示例代码中的#1)。

现在,如果这个“新”的INSTANCE成员的名称恰好与“全局”类成员的名称相同——则对于这个特定对象实例,将添加一个附加的实例成员,其名称与已经具有(并与该类的所有其他实例共享)的类成员的名称相同。请参见下面示例代码中的#2)。

--> 当您通过特定实例对象而不是类名访问“全局”(类)成员以进行“设置目的”时,即:my_class_obj2.class_data_member = some_value 而不是 MyClass.class_data_member,根据下面的示例代码,那么会为此特定实例创建一个额外的实例成员 - 因此它的大小也被改变了(如果您运行示例代码,您将看到两个不同的实例具有两个不同的大小。我对Python的大小“分配”并不是很擅长,所以我期望看到my_class_obj2的大小大于my_class_obj - 但事实并非如此,我想在this Q&A中可以看到有关Python对象大小的更多信息,我从中提取了pympler包的asizeof函数的使用示例)。

请参见下面的代码以获取更完整的示例:

import sys
from pympler import asizeof

class MyClass:

    class_data_member = 15

    def __init__(self, num):
        self.num = num
        self.string = ""

    def define_some_class_member(self):
        pass

    def print_me(self):
        self.x = 17
        print("MyClass::print_me - num is:" + str(self.num) + ", string is:" + self.string)
        print("MyClass::print_me - self.x is:" + str(self.x))

    def foo(self):
        print("Hi there form " + __class__.__name__ + "::foo")


def classes_and_object_example():
    func_name = "classes_and_object_example - "
    print(func_name + "creating MyClass object")
    my_class_obj = MyClass(12)
    my_class_obj.print_me()

    print(func_name + "creating another MyClass object")
    my_class_obj2 = MyClass(17)
    my_class_obj2.print_me()

    # 1)
    my_class_obj.some_new_instance_member = 90
    print(func_name + "the new additional instance member is:" + str(my_class_obj.some_new_instance_member))
    # Pay attention that the "new instance member" was added to my_class_obj and NOT to my_class_obj2 so the below statement is illegal
    # print("the new additional instance member is:" + str(my_class_obj2.some_new_instance_member))

    print(func_name + "the \"global\" data member seen by my_class_obj.class_data_member"
      + " is:" + str(my_class_obj.class_data_member) + " and the \"global\" data member seen by my_class_obj2.class_data_member"
      + " is (also):" + str(my_class_obj2.class_data_member))

    # 2)
    my_class_obj2.class_data_member = 99
    print(func_name + "the \"global\" data member seen by my_class_obj2.class_data_member"
      + " after intentionally modifying it is:" + str(my_class_obj2.class_data_member) + ", while on the other hand it is STILL seen by my_class_obj.class_data_member"
      + " as:" + str(MyClass.class_data_member))

    MyClass.class_data_member = 67
    print(func_name + "after setting the \"global (static)\" data member that is shared among all MyClass instances"
      + " using the assignemt:MyClass.class_data_member = 67, its value is:" + str(MyClass.class_data_member) + ", while on the other hand my_class_obj2 STILL has its \"own\" INSTANCE member with the same name and value of:" 
      + str(my_class_obj2.class_data_member))

    size_of_my_class_orig_object = asizeof.asizeof(my_class_obj)
    print(func_name + "the size of a MyClass object instance is:" + str(size_of_my_class_orig_object))

    size_of_my_class_modified_object = asizeof.asizeof(my_class_obj2)
    print(func_name + "the size of a MyClass object instance after \"adding\" to it an additional instance member is:" + str(size_of_my_class_modified_object))

# run the sample code for illustration
classes_and_object_example()

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