61

我刚刚学习 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,它重载了构造函数,以便在需要时能够处理不同的参数。

仅定义(如上所示)与执行以下操作有self.element什么区别:__init__

class Node(object):
    element, left, right = None
    def __init__(self, element):
        self.element = element
        self.left = self.right = None

是不是self.element和定义__init__的类的element变量一样?那不会覆盖elementfromNoneelement传入的值__init__吗?

4

7 回答 7

99

一个是类属性,另一个是实例属性。它们是不同的,但它们彼此密切相关,使它们有时看起来相同。

它与python查找属性的方式有关。有等级制度。在简单的情况下,它可能如下所示:

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

当你寻找instance这样的属性时......

`instance.val`

...实际发生的是,首先,Pythonval在实例本身中查找。然后,如果它没有找到val,它会在它的类中查找Subclass。然后,如果它在那里找不到val,它会在 , 的父级中Subclass查找Superclass。这意味着当你这样做时......

>>> class Foo():
    foovar = 10  
    def __init__(self, val):
        self.selfvar = val

Foo ... share 的所有实例foovar,但有自己独特selfvar的 s。这是一个简单而具体的例子,说明它是如何工作的:

>>> 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]
于 2012-09-13T15:39:02.040 回答
23

在 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.variableorNode.variable表单从成员函数中访问其中任何一个。只是访问variable将尝试访问一个名为variable.

于 2012-09-13T15:42:46.000 回答
2

self.element在构造函数(即__init__方法)内部是一个实例变量(如果一个节点对象修改了它的值,它只会改变这个对象),其中第二个版本中的一个是一个类变量(所以如果一个节点对象修改了它的值,它就会改变对于所有节点对象)。

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

于 2012-09-13T15:29:03.733 回答
1

重要的部分是 的self论点__init__。事实上,在任何实例方法中,这都是第一个参数。这是设计使然;在 Python 中,您真正可以访问实例的唯一时间是在方法调用期间,并且它与self参数一起显式显示。

当您在class定义中时,您还没有任何实例,因此您真正要修改的是类本身。因此,如果您在类级别定义属性,那么它们就真正成为类属性,而不是实例。

将其与 C++ 相比,您可能会说这些语言中的“类”基本上是它们所代表的对象的蓝图。“这些对象应具有foobar属性,此外,还应具有以下方法。” 然而,在 Python 中,类本身就是对象,它们的主要优势在于它们可以创建自身的副本(实例),这也恰好使用类的方法。因此,它更像是“它们应具有foobar作为类属性,此外,您还应使用以下方法来创建实例。”

因此,与其说是蓝图,不如说是一步一步的操作方法。

于 2012-09-13T15:34:15.623 回答
0

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

于 2012-09-13T15:35:25.973 回答
0

我想添加一个具有不同“视角”的答案,上述答案均未提及(并不是说它们不好!!)-> 对象的“大小”。

前言:在 Python 中,无论好坏,都可以在 RUN TIME 中为特定对象实例“创建”(添加)“新”(附加)INSTANCE 类成员,这意味着,例如,如果特定类有两个实例成员,然后可以在创建对象实例之后添加第三个,并且它只会添加到类的这个特定实例(即 - 类的“蓝图”不会改变)。请参阅下面示例代码中的#1)。

现在,如果这个“新”实例成员的名称恰好与“全局”类成员的名称相同 --> 那么对于这个特定的对象实例,将添加一个额外的实例成员,其名称与已经拥有的类成员(并且它与该类的所有其他实例共享)。请参阅下面示例代码中的#2)。

--> 因此,当您出于“设置目的”访问“全局”(类)成员时,通过特定实例对象而不是通过类名,意思是:my_class_obj2.class_data_member = some_value而不是MyClass.class_data_member根据下面的示例代码,那么会发生什么这个特定的实例,一个额外的实例成员被创建 - 因此它的 SIZE 也被改变了(如果你运行示例代码,你会看到两个不同的实例有两种不同的大小。我对所有必须做的事情都不是很好使用 Python 的大小“分配”,所以我期待看到 的大小my_class_obj2大于my_class_obj--> 但事实并非如此,我想有关 Python 中对象大小的更多信息可以在这个 Q&A中看到,从中我取了packageasizeof函数中的用法示例)。pympler

有关更完整的示例,请参见以下代码:

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()
于 2020-07-28T07:47:20.377 回答
0

当您尝试使用类“符号”访问变量时,它只会查看

cls.__dict__

但是当您尝试使用实例“表示法”访问变量时,它首先在

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}

对于细节类特殊属性

于 2017-07-30T07:36:54.597 回答