函数参数名中的类与嵌套类属性发生冲突

问题描述 投票:1回答:6

考虑以下代码:

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在分配之前不评估名称?

python scope closures inner-classes
6个回答
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
投票

在范围内,如果指定名称,则该范围内是本地名称。类主体是范围。因此,_bool是本地的,因此尝试将_bool分配给它是一个错误,因为_bool尚未定义。与其他一些语言不同,当范围内尚未定义名称时,Python不会查看外部范围。

正如您所说,nonlocal无法解决问题,因为它会导致外部作用域的名称被分配。

简单的答案是使用不同的名称。

另一种可能性,如果你只在S的方法中使用这些值,就是简单地使用一个闭包并访问方法中的值,就像它们是本地值一样。那么你根本不需要它们作为类的属性。


1
投票

让我们用另一个问题回答你的问题 - 你怎么认为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_属于哪个范围。


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

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)

0
投票

一种可能性是使它们在init方法中分配的实例变量将参数默认为调用中提供的值。

© www.soinside.com 2019 - 2024. All rights reserved.