【问题标题】:Python Class MembersPython 类成员
【发布时间】:2012-09-06 17:52:29
【问题描述】:

我刚刚学习 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 变量定义的一样吗?那不是将elementNone 覆盖到传递给__init__element 值吗?

【问题讨论】:

    标签: python class member-variables


    【解决方案1】:

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

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

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

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

    `instance.val`
    

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

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

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

    >>> 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.foovarFoo.foovar 指的是同一个对象,因此当您更改对象时,更改会向上传播:

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

    【讨论】:

    • 优秀的答案!在Foo.foovar 是可变对象(例如list)的情况下,这些事情会变得更加有趣。
    【解决方案2】:

    在 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 的局部变量。

    【讨论】:

      【解决方案3】:

      self.element 在构造函数中(即 - __init__ 方法)是一个 instance 变量(如果节点对象修改了它的值,它只会改变这个对象),第二个版本中的那个是一个类变量(因此,如果一个节点对象修改了它的值,它将为所有节点对象更改)。

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

      【讨论】:

        【解决方案4】:

        重要的部分是 __init__self 参数。事实上,在 any 实例方法中,这将是第一个参数。这是设计使然;在 Python 中,您真正可以访问实例的唯一时间是在方法调用期间,它通过 self 参数显式显示。

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

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

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

        【讨论】:

          【解决方案5】:
          __init__ 中的

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

          【讨论】:

            【解决方案6】:

            我想添加一个具有不同“视角”的答案,上述答案都没有提到(并不是说它们不好!!)-->“size”的对象。

            前言:在 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中对象的大小可以在this Q&A中看到,我从中取了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()
            

            【讨论】:

              【解决方案7】:

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

              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}
              

              详情class special attribute

              【讨论】:

                猜你喜欢
                • 2011-03-31
                • 1970-01-01
                • 1970-01-01
                • 2019-09-03
                • 2023-04-08
                • 2017-12-28
                • 1970-01-01
                • 2017-01-25
                • 2013-02-20
                相关资源
                最近更新 更多