Class 在函数参数名称中与嵌套的 class 属性冲突
Class in function arguments name clash with nested class attributes
考虑这段代码:
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
我不知道哪些 scope/closure 规则适用于此。 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 不在赋值前计算名称?
在一个范围内,如果您分配给一个名称,则它在该范围内是局部的。 class 主体是一个范围。因此,_bool
是本地的,因此尝试将 _bool
分配给它是一个错误,因为 _bool
尚未定义。与其他一些语言不同,Python 不会在尚未在作用域中定义名称时查看外部作用域。
正如您所注意到的,nonlocal
并没有解决问题,因为它导致外部范围的名称被分配给。
简单的答案是使用不同的名称。
如果您仅在 S
的方法中使用这些值,另一种可能性是简单地使用闭包并访问方法中的值,就好像它们是局部值一样。那么你根本不需要它们作为 class 的属性。
当编译 class 时,使用与函数类似的规则来解析名称。因为 class 主体包含对 bool_
变量的赋值,解释器将其视为 class 变量,因此当被要求解析其值时,会在 class 名称空间中查找作业的右侧。
一种可能性是使它们成为实例变量,在 __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
。这是可能的原因是命名参数使用标准(词法)范围规则解析值表达式,而参数名称本身在每次调用开始时被注入命名空间。
如果您使用
形式的赋值将值绑定到 class 变量,您会发现代码也有效
S.bool_ = bool_
因为,在 __init__
方法运行时,classname S 已经绑定在词法封闭的命名空间中。即使值绑定到 class,也可以相对于实例引用它们 - 因为它们不存在,解释器会继续在实例的 class.
中查找
但是,无论哪种情况,都需要调用 class 来设置 __init__
中的属性值。
变量命名和范围是您的问题。如果您稍微更改代码,它应该可以工作。
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
让我们用另一个问题来回答您的问题 - 您认为 Python 如何知道 bool_ = bool_
行中的哪个 bool_
?
def gee(bool_, int32, int64, str_):
class S:
bool_ = bool_
第一个 bool_
是 class 属性吗?还是函数中的 nonlocal
bool_
?还是 *gasps* 一个 global
对象?那么分配的对象呢,= bool_
应该指的是哪个范围?
这就是为什么 Python 的口头禅是 "Explicit is better than Implicit"。范围应该明确定义 Python 以理解您所指的内容,否则解释器会假定最直接的范围。
但您已经知道这个问题的解决方案 - 只需将其重命名为不同的名称即可解决问题,因为它 明确地 告诉 Python 解释器哪个范围该对象指的是。
另一种方法是您可以将 class 属性定义移动到 class 范围之外:
def gee(bool_, int32, int64, str_):
class S:
pass
S.bool_ = bool_
S.int32 = int32
...
return S
这样你就可以清楚地定义哪个bool_
属于哪个范围。
您可以使用此代码。无需更改变量的名称。您可以使用属性和 class 构造函数来解决此问题。
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 方法中分配,将参数默认为调用中提供的值。
考虑这段代码:
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
我不知道哪些 scope/closure 规则适用于此。 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 不在赋值前计算名称?
在一个范围内,如果您分配给一个名称,则它在该范围内是局部的。 class 主体是一个范围。因此,_bool
是本地的,因此尝试将 _bool
分配给它是一个错误,因为 _bool
尚未定义。与其他一些语言不同,Python 不会在尚未在作用域中定义名称时查看外部作用域。
nonlocal
并没有解决问题,因为它导致外部范围的名称被分配给。
简单的答案是使用不同的名称。
如果您仅在 S
的方法中使用这些值,另一种可能性是简单地使用闭包并访问方法中的值,就好像它们是局部值一样。那么你根本不需要它们作为 class 的属性。
当编译 class 时,使用与函数类似的规则来解析名称。因为 class 主体包含对 bool_
变量的赋值,解释器将其视为 class 变量,因此当被要求解析其值时,会在 class 名称空间中查找作业的右侧。
一种可能性是使它们成为实例变量,在 __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
。这是可能的原因是命名参数使用标准(词法)范围规则解析值表达式,而参数名称本身在每次调用开始时被注入命名空间。
如果您使用
形式的赋值将值绑定到 class 变量,您会发现代码也有效S.bool_ = bool_
因为,在 __init__
方法运行时,classname S 已经绑定在词法封闭的命名空间中。即使值绑定到 class,也可以相对于实例引用它们 - 因为它们不存在,解释器会继续在实例的 class.
但是,无论哪种情况,都需要调用 class 来设置 __init__
中的属性值。
变量命名和范围是您的问题。如果您稍微更改代码,它应该可以工作。
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
让我们用另一个问题来回答您的问题 - 您认为 Python 如何知道 bool_ = bool_
行中的哪个 bool_
?
def gee(bool_, int32, int64, str_):
class S:
bool_ = bool_
第一个 bool_
是 class 属性吗?还是函数中的 nonlocal
bool_
?还是 *gasps* 一个 global
对象?那么分配的对象呢,= bool_
应该指的是哪个范围?
这就是为什么 Python 的口头禅是 "Explicit is better than Implicit"。范围应该明确定义 Python 以理解您所指的内容,否则解释器会假定最直接的范围。
但您已经知道这个问题的解决方案 - 只需将其重命名为不同的名称即可解决问题,因为它 明确地 告诉 Python 解释器哪个范围该对象指的是。
另一种方法是您可以将 class 属性定义移动到 class 范围之外:
def gee(bool_, int32, int64, str_):
class S:
pass
S.bool_ = bool_
S.int32 = int32
...
return S
这样你就可以清楚地定义哪个bool_
属于哪个范围。
您可以使用此代码。无需更改变量的名称。您可以使用属性和 class 构造函数来解决此问题。
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 方法中分配,将参数默认为调用中提供的值。