我试图理解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
答案 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.a
,self.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