为什么方法不能访问类变量?

时间:2018-06-30 18:12:11

标签: python

我试图理解Python中的变量作用域,除了我不理解为什么无法从其方法访问类变量的那一部分之外,大多数事情对我来说都是清楚的。
在下面的示例中,mydef1()无法访问a,但是如果a在全局作用域(外部类定义)中声明,则可以访问。

class MyClass1:
    a = 25
    def mydef1(self):
        print(a)
ins1 = MyClass1()
ins1.mydef1()

输出

Traceback (most recent call last):
  File "E:\dev\Python\scope_test2.py", line 6, in <module>
    ins1.mydef1()
  File "E:\dev\Python\scope_test2.py", line 4, in mydef1
    print(a)
NameError: name 'a' is not defined

5 个答案:

答案 0 :(得分:2)

简短的回答:这是Python的范围规则。 Python中的嵌套函数具有词法范围,但是不适用于嵌套在类中的东西。

class Foo:
    a = 25
    print(a)
    class Bar:
        print(a)

第一个打印,而第二个打印NameError


更长的答案:

一个用于类级变量的函数闭包,但它们都封装在__class__中。 (此方法的主要用途是在super()魔术中,这就是为什么它不再需要Python 3中的参数的原因。)

class MyClass1:
    a = 25
    def mydef1(self):
        print(__class__.a)
ins1 = MyClass1()
ins1.mydef1()  # 25

通常,您可以通过self参数访问此类内容,以允许子类覆盖它们,但是__class__甚至适用于静态方法,该方法几乎没有self,也{ {1}}。

cls

从技术上讲,直到 类声明完成执行后,类对象才存在,这就是为什么不能这样做

class MyClass1:
    a = 25
    @staticmethod
    def mydef1():
        print(__class__.a)
ins1 = MyClass1()
ins1.mydef1()  # 25

甚至不,

class Foo:
    a = 25
    class Bar:
        # NameError: free variable '__class__' referenced before assignment
        print(__class__.a)

但是,在此之前,您可以访问class Foo: a = 25 def bar(): print(__class__.a) # NameError: free variable '__class__' referenced before assignment in enclosing scope bar() 字典。

locals()

这可行。

class Foo:
    a = 21
    locals()['a'] *= 2

Foo.a  # 42

答案 1 :(得分:1)

重要的是要理解这些评论中的一些不是等同的。 MyClass.a是类本身的成员,self.a是类实例的成员。

使用self.a时,它将从类中返回a,因为实例上没有a。如果还有一个a是实例的成员,它将返回该实例。通常,实例a是使用__init__构造函数设置的。两者可以同时存在。

class MyClass1:
    a = 25

    def __init__(self):
        self.a = 100

    def instance_a(self):
        print(self.a)

    def change_instance_a(self):
        self.a = 5

    def class_a(self):
        print(MyClass1.a)

    def change_class_a(self):
        MyClass1.a = 10


# Create two instances
ins1 = MyClass1()
ins2 = MyClass1()

# Both instances have the same Class member a, and the same instance member a
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()

# Now lets change instance a on one of our instances
ins1.change_instance_a()

# Print again, see that class a values remain the same, but instance a has
# changed on one instance only
print()
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()

# Lets change the class member a on just one instance
ins1.change_class_a()

# Both instances now report that new value for the class member a
print()
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
            self.a = 100

        def instance_a(self):
            print(self.a)

        def change_instance_a(self):
            self.a = 5

        def class_a(self):
            print(MyClass1.a)

        def change_class_a(self):
            MyClass1.a = 10

答案 2 :(得分:1)

请考虑以下课程:

class ScopeTest(object):
    classvariable = 0
    number_of_objects_created = 0

    def __init__(self, value=1):
        self.instancevariable = value
        ScopeTest.number_of_objects_created += 1

    def number_of_brothers(self):
        print(ScopeTest.number_of_objects_created)

    def confusion(self, value=2):
        self.classvariable = value
        print (f"class: {ScopeTest.classvariable}, self:{self.classvariable}")

让我们看看当您玩转它时会发生什么:

>>> a = ScopeTest()
>>> a.instancevariable
1
>>> a.classvariable
0
>>> ScopeTest.classvariable
0

到目前为止很好,但是当您为a分配新属性时:

>>> a.classvariable = 2
>>> a.classvariable
2
>>> ScopeTest.classvariable
0

如果在类的方法中添加属性,则同样适用:

>>> a.confusion(4)
class: 0, self:4

这类类属性可以使所有对象通用,例如number_of_objects_created

>>> b = ScopeTest()
>>> b.number_of_brothers()
2
>>> a.number_of_brothers()
2

通过向该类添加另一个方法,您可以从中得到更多:

class ScopeTest(object):
    ...
    def other_function(self, classvariable=3):
        print(f"class: {ScopeTest.classvariable}\t"
              f"instance: {self.classvariable}\t"
              f"argument:{classvariable}")

并调用它(使用第一个“ confusion”方法设置self.classvariable之后):

>>> a.confusion()
class: 0, self:2
>>> a.other_function()
class: 0        instance: 2     argument:3

答案 3 :(得分:0)

您需要self.a。没有隐式的类范围。

答案 4 :(得分:0)

通过在print(a)中调用mydef1,python正在寻找局部变量(或全局变量,如您所见)“ a”。也就是说,无论如何都没有与MyClass1直接相关的变量,但尚未定义。

如果您尝试访问类变量a”(即a是类本身的成员,而不是其任何实例),则您必须使用MyClass1.a。另外,由于没有名为“ a”的实例变量,因此您也可以使用self.a来达到相同的效果。但是,一旦明确定义了self.aself.a == MyClass1.a可能就不成立。例如:

>>>class MyClass1:
...    a = 25
...
>>>my_obj = MyClass1()
>>>MyClass1.a
25
>>>my_obj.a
25
>>>MyClass1.a += 1
>>>MyClass1.a
26
>>>my_obj.a
26
>>>my_obj.a = 5  # explicitly define "a" for this instance; my_obj.a not longer synonymous with MyClass1.a
>>>MyClass1.a += 1
>>>MyClass1.a
27
>>>my_obj.a
5