考虑以下代码:
def gee(bool_, int32, int64, str_):
class S:
bool_ = bool_
int32 = int32
int64 = int64
str_ = str_
return S
gee(1, 2, 3, 4)
运行此操作会出现错误:
Traceback (most recent call last):
File "test_.py", line 36, in <module>
gee(1, 2, 3, 4)
File "test_.py", line 27, in gee
class S:
File "test_.py", line 28, in S
bool_ = bool_
NameError: name 'bool_' is not defined
我不知道在这里适用哪些范围/关闭规则。 nonlocal
修复了该错误,但结果并非我所期望的:
def gee(bool_, int32, int64, str_):
class S:
nonlocal bool_, int32, int64, str_
bool_ = None
int32 = None
int64 = None
str_ = None
print(bool_, int32, int64, str_ )
return S
g = gee(1, 2, 3, 4)
g.bool_
输出:
None None None None
Traceback (most recent call last):
File "test_.py", line 38, in <module>
g.bool_
AttributeError: type object 'S' has no attribute 'bool_'
除了重命名外,我还能做些什么来使第一段代码起作用?为什么会这样呢?因为有name = ...
吗?为什么Python在分配之前不评估名称?
答案 0 :(得分:2)
在范围内,如果您分配给名称,则该名称在该范围内是本地的。类主体是作用域。因此,_bool
是本地的,因此尝试为其分配_bool
是错误的,因为尚未定义_bool
。与其他一些语言不同,当尚未在作用域中定义名称时,Python不会在外部作用域中查找。
nonlocal
不能解决问题,因为它会导致将外部作用域的名称分配给它。
简单的答案是使用不同的名称。
如果仅在S
的方法中使用这些值,另一种可能性是简单地使用闭包并在方法中访问值,就好像它们是本地值一样。然后,您根本就不需要它们作为类的属性。
答案 1 :(得分:2)
编译类时,将使用与功能相似的规则来解析名称。因为类主体包含对bool_
变量的赋值,所以解释器将其视为类变量,因此当被要求在赋值的右侧解析其值时,它会在类名称空间中查找。
一种可能性是使它们成为在__init__
方法中分配的实例变量,从而将参数默认为对gee
的调用中提供的值:
def gee(bool_, int32, int64, str_):
class S:
def __init__(self, bool_=bool_, int32=int32, int64=int64, str_=str_):
self.bool_ = bool_
self.int32 = int32
self.int64 = int64
self.str_ = str_
return S
my_class = gee(1, 2, 3, 4)
my_instance = my_class()
print(my_instance.bool_)
您应该找到此代码打印1
。之所以可能这样做,是因为命名参数使用标准(词汇)作用域规则来解析值表达式,而参数名称本身在每次调用开始时便被注入名称空间。
如果您改为使用形式赋值将值绑定到类变量,则代码也将起作用
S.bool_ = bool_
因为在运行__init__
方法时,类名S已经绑定在词法包围的名称空间中。即使这些值已绑定到该类,也可以相对于实例进行引用-由于它们不存在,因此解释程序会继续在实例的类中进行查找。
但是,无论哪种情况,都需要调用该类才能在__init__
内设置属性值。
答案 2 :(得分:1)
让我们用另一个问题回答您的问题-您如何假设Python知道bool_
行中的哪个bool_ = bool_
?
def gee(bool_, int32, int64, str_):
class S:
bool_ = bool_
第一个bool_
是类属性吗?还是函数中的nonlocal
bool_
?还是“喘气”一个global
对象?那么,分配的对象呢?= bool_
应该指向哪个范围?
这就是为什么Python的口头禅是“显式优于隐式”。应该为Python很好地定义范围,以便理解您所指的范围,否则解释器会假定最直接的范围。
但是您已经知道解决该问题的方法-只需将其重命名为其他名称即可解决该问题,因为它明确地告诉Python解释器对象所指向的范围。
另一种方法是,您可以将类属性定义移出类范围:
def gee(bool_, int32, int64, str_):
class S:
pass
S.bool_ = bool_
S.int32 = int32
...
return S
这样,您可以清楚地定义哪个bool_
属于哪个范围。
答案 3 :(得分:0)
变量命名和范围是您的问题。如果将代码稍作更改,它应该可以工作。
def gee(some_bool, some_int32, some_int64, some_str):
class S:
bool_ = some_bool
int32 = some_int32
int64 = some_int64
str_ = some_str
return S
print(gee(1, 2, 3, 4))
# 1, 2, 3, 4
答案 4 :(得分:0)
您可以使用此代码。无需更改变量名称。您可以使用属性和类构造函数来解决此问题。
def gee(bool_, int32, int64, str_):
class S:
def __init__(self, bool_, int32, int64, str_):
self.bool_ = bool_
self.int32 = int32
self.int64 = int64
self.str_ = str_
def get_bool_(self):
return self.bool_
def set_bool_(self, bool_):
self.bool_ = bool_
def get_int32(self):
return self.int32
def set_int32(self, int32):
self.int32 = int32
def get_int64(self):
return self.int64
def set_int64(self, int64):
self.int64 = int64
def get_str_(self):
return self.str_
def set_str_(self, str_):
self.str_ = str_
print(bool_, int32, int64, str_ )
return S
g = gee(1, 2, 3, 4)
答案 5 :(得分:0)
一种可能性是使它们成为在 init 方法中分配的实例变量,从而将参数默认为调用中提供的值。