【问题标题】:Are class variables in the scope of instance methods or class methods?类变量是在实例方法还是类方法的范围内?
【发布时间】:2014-03-12 11:36:30
【问题描述】:
  • 类变量是否在为 Python 类定义的方法的范围内?
  • 如何访问方法中的类变量(类方法或实例方法或静态方法)并可能更改它?
  • 最好的方法是什么?例如,我可能想计算已经创建了多少个类的实例。

我读到here 说类变量在方法参数列表的范围内。要在那里使用类变量,我应该将其称为counter 而不是MyClass.counter,因为该类正在定义中,但是我可以在__init__ 方法中以MyClass.counter 的形式访问它。这是否意味着在 __init__ 方法中时该类已完全定义?

编辑:我也想知道为什么counter在方法的参数列表范围内,但不在方法体中?

class MyClass(object):
    counter = 0

    def __init__(self): # counter is in scope here. can be used as default argument
        counter += 1  # Counter not in scope. Throws UnboundLocalError

    def printCount(self):
        print counter  # Counter not in scope. Throws UnboundLocalError

【问题讨论】:

  • 我问这里是为了解释为什么会这样。
  • 你应该得到 UnboundLocalError 错误而不是全局错误。
  • UnboundLocalError: local variable 'counter' referenced before assignment.
  • 因此,如果您阅读链接到的答案,您会注意到回答者从未在方法主体内引用 counter... 所以不,除非您在课堂上提及它,否则它不在范围内名字。
  • 当类主体完成执行后,变量counter已转换为Myclass.counter,因此要在方法中访问该变量,您需要使用类名或self。跨度>

标签: python class methods scope


【解决方案1】:

在方法中,您可以通过两种不同的方式访问类属性:

  1. 通过引用类,然后是属性:

    MyClass.counter
    
  2. 通过访问实例上的属性,self:

    self.counter
    

    只有在没有实例属性掩蔽counter时才有效。

问题是当你在实例上设置属性时,self上的类属性不再可以访问:

self.counter += 1

将在您第一次运行时设置实例属性,并且将不再访问类属性。

如果你想设置一个类属性,那么你必须使用第一种方法来解决它:

class MyClass(object):
    counter = 0

    def __init__(self): 
        MyClass.counter += 1

    def printCount(self):
        # these both work:
        print self.counter
        print MyClass.counter

您不能在方法中将counter 称为本地。在类定义主体之外,它们不是本地或全局名称。只有在class body 内才能做到这一点:

类的套件然后在一个新的执行框架[...]中执行,使用一个新创建的本地命名空间和原来的全局命名空间。 (通常,套件只包含函数定义。)当类的套件完成执行时,它的执行帧被丢弃,但它的本地命名空间被保存。 [...] 然后使用基类的继承列表和属性字典的保存的本地命名空间创建一个类对象。

换句话说,当 Python 执行 class 主体时,counter__init__printCount 是本地名称,在执行过程中您可以将这些名称称为本地名称。因此,您可以使用counter 为方法参数定义默认值,或基于它进行计算。但是一旦构造了类,该命名空间就消失了,您将不得不引用MyClass.<name>

【讨论】:

  • 您能否解释一下我在编辑中的问题,这是我真正想问的。谢谢:)
  • @ajay:定义类时,class 语句中的所有内容都像函数一样执行。 counter 是该“函数”中的局部变量,函数定义也是如此。该函数的本地命名空间成为类主体。所以counter 是本地的,__init__ 是另一个,等等。
  • @ajay:所以当时你可以参考counter作为本地名称。但是当类完成定义时,那些本地名称就消失了,它们现在是类的一部分。您必须转而参考MyClass.<name>
  • 请在答案中添加这些。它将完成。
【解决方案2】:

这里有另一个简单的解释:

1.类变量是否在为 Python 类定义的方法的范围内?

下面是类变量说明:

  • 一个类的所有实例共享的变量。
  • 它在一个类中定义,但在该类的任何方法之外。
  • 可以通过使用类名点在变量名上来访问类变量。

注意:类变量存储在class_name.__dict__ 中,即共享给所有实例。

注意:实例变量存储在instance.__dict__ 中,即仅存储到当前实例。

注意:使用类名访问类变量总是一个好主意,以减少混淆,不要用实例变量覆盖。

这里是 sn-p:

class ClassA(object):
    _counter = 0
    def __init__(self, val):
       self._counter = val

    def show_me(self):
        print 'Class variable: %d' % ClassA._counter
        print 'Instance variable: %d' % self._counter

obj = ClassA(1000)

obj.show_me()
print ClassA.__dict__['_counter']
print obj.__dict__['_counter']

输出:

Class variable: 0
Instance variable: 1000
0
1000

2。如何访问方法中的类变量(类方法或实例方法或静态方法)并可能更改它?

您可以使用 class_name 点变量名 ie 访问类变量

ClassA._counter

3.最好的方法是什么?例如,我可能想计算已创建的类实例的数量。

据我所知,覆盖__new__ 方法会更好,因为__new__ 会调用每个对象来创建和更新您的类变量_counter,如下所示:

#!/usr/bin/python

class ClassA(object):
    # this _counter is in the class.__dict__ which shared across all instance
    _counter = 0
    def __new__(cls, *args, **kw):
        ob = super(ClassA, cls).__new__(cls, *args, **kw)
        ClassA._counter += 1
        return ob

    def __init__(self, val):
        # this _counter is in the instance dictionary which is not shared
        self._counter = val*val

    def total_instance(self):
        return ClassA._counter

# Lets create three instance
for i in range(1,4):
    obj = ClassA(i)
    print 'class variable _counter : %d' % obj.total_instance()
    print 'instance variable _counter : %d' % obj._counter

输出:

class variable _counter : 1
instance variable _counter : 1
class variable _counter : 2
instance variable _counter : 4
class variable _counter : 3
instance variable _counter : 9

注意:在上面的示例中,我试图向您展示实例变量 _counter 和类变量 _counter 的区别。

如果有帮助,请告诉我。

【讨论】:

  • 类变量也可以在参数列表中访问,而不用类名作为前缀。请看我的编辑。谢谢。
  • 好的,如果您从参数列表中访问类变量,那么您可以增加它吗?计算实例数。可以,但需要编写更多代码。
猜你喜欢
  • 2015-11-15
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
  • 2023-03-19
  • 1970-01-01
  • 2011-05-19
  • 2011-08-22
  • 1970-01-01
相关资源
最近更新 更多