考虑以下代码:
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在分配之前不评估名称?
编译类时,使用类似的函数规则来解析名称。因为类主体包含对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__
中设置属性值。
在范围内,如果指定名称,则该范围内是本地名称。类主体是范围。因此,_bool
是本地的,因此尝试将_bool
分配给它是一个错误,因为_bool
尚未定义。与其他一些语言不同,当范围内尚未定义名称时,Python不会查看外部范围。
正如您所说,nonlocal
无法解决问题,因为它会导致外部作用域的名称被分配。
简单的答案是使用不同的名称。
另一种可能性,如果你只在S
的方法中使用这些值,就是简单地使用一个闭包并访问方法中的值,就像它们是本地值一样。那么你根本不需要它们作为类的属性。
让我们用另一个问题回答你的问题 - 你怎么认为Python知道哪个bool_
是哪个bool_ = bool_
线?
def gee(bool_, int32, int64, str_):
class S:
bool_ = bool_
第一个bool_
是一个类属性吗?或者是来自函数的nonlocal
bool_
?或者它是* gasps *一个global
对象?分配的对象怎么样,= bool_
应该引用哪个范围?
这就是为什么Python的口头禅是“明确比隐含更好”。应该为Python定义范围,以了解您所指的是哪个,否则解释器会假设最直接的范围。
但是你已经知道了这个解决方案 - 简单地将它重命名为不同的名称将解决问题,因为它明确地告诉Python解释器对象所指的范围。
另一种方法是可以将类属性定义移到类范围之外:
def gee(bool_, int32, int64, str_):
class S:
pass
S.bool_ = bool_
S.int32 = int32
...
return S
这样你就可以清楚地定义哪个bool_
属于哪个范围。
变量命名和范围是您的问题。如果你稍微改变你的代码,它应该工作。
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
您可以使用此代码。无需更改变量的名称。您可以使用属性和类构造函数来解决此问题。
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)
一种可能性是使它们在init方法中分配的实例变量将参数默认为调用中提供的值。