在 class 体内使用非局部或全局

using nonlocal or global inside a class body

在python中,在class中使用import语句来定义刚刚从其他模块中窃取的class变量是有效的:

class CustomMath:
    from math import pi

assert isinstance(CustomMath.pi, float) # passes

用非常混乱的语句x=x

将全局变量引用到class也是有效的
x = 1
class Test:
    # this loads the global variable x and stores in in the class scope
    x = x
assert Test.x == 1 # passes

但是,如果我使用函数生成 classes,是否有类似的方法将变量从函数参数复制到 class 主体?

我想要这种性质的东西:

def generate_meta_options(model, design):
    class Meta:
        # copy the nonlocal variable model to this scope
        # but this isn't valid syntax
        model = (nonlocal model)
        design = translate_old_design_spec_to_new_format((nonlocal design))
    return Meta

参数名称很重要,因为它是通过关键字而不是顺序 generate_meta_options(design="A", model=Operation) 传递的,并且 class 中的名称必须相同,我已经通过创建变量别名找到了解决方法所有输入,但这有点烦人

def generate_meta_options_works(model, design):
    _model = model
    _design = design
    class Meta:
        model = _model
        design = translate_old_design_spec_to_new_format(_design)
    return Meta

是否有使用 nonlocal 关键字实现此目的的更好方法?似乎 python 确实允许 nonlocalglobal 在 class 中使用,但是变量没有保留在 class 中所以我无法想象它的一个用例。

gl = 'this is defined globally'
def nonlocal_in_class_test():
    lo = 'this is defined locally'
    class Test:
        nonlocal lo
        global gl
        lo = 'foo'
        gl = 'bar'
    assert lo == 'foo' # passes
    assert gl == 'bar' # passes
    assert hasattr(Test, 'lo') or hasattr(Test, 'gl'), 'lo nor gl was put in the class scope' # fails

我确定我正在使用的库只需要一个具有属性查找的对象,这样我就可以远离 classes 但我看到的每个示例都使用 classes(我怀疑是因为继承多个配置是很自然的)所以我犹豫要不要走那条路。

从不同的模块复制变量比在上述范围内复制一个变量更容易,这似乎真的很奇怪,所以我想我会问,有没有办法从一个变量复制一个变量nonlocal 范围到 class 属性而不创建具有不同名称的单独变量?

作为切线,在 class 正文中使用 nonlocalglobal 关键字是否有用?我认为它没有用,但也没有理由做额外的工作来禁止它。

nonlocal 在任何情况下都不起作用,因为在 nonlocal 适用的情况下变量只有一个作用域(函数局部变量,与 class 定义作用域略有不同);通过尝试使用 nonlocal,您会说 modelnever 定义范围的一部分,只是 class 定义范围之外的东西。

我个人更喜欢你有点老套的重新分配,所以 class 外的 _model 和 class 内的 model 不会冲突,但如果你讨厌它,那么是直接访问 class-in-progress 命名空间的选项,vars()(或 locals();在这种情况下两者是等价的,但我不 认为class 范围的 是本地人,尽管他们的行为很像。

因为范围不是 真正的 函数范围,实际上您可以通过 dictvars/locals 改变它,让您想要的结果看起来像:

def generate_meta_options(model, design):
    class Meta:
        vars().update(
            model=model,
            design=translate_old_design_spec_to_new_format(design)
        )
    return Meta

使用 dict.update 的关键字参数传递形式意味着代码甚至 看起来 大部分都像正常赋值。如果您在 class 定义中更早(查看外部范围)或稍后(查看新定义的名称)使用这些名称,Python 不会抱怨:

def generate_meta_options(model, design):
    print("Before class, in function:", model, design)  # Sees arguments
    class Meta:
        print("Inside class, before redefinition:", model, design)  # Sees arguments
        vars().update(
            model=model,
            design=design+1
        )
        print("Inside class, after redefinition:", model, design)  # Sees class attrs
    print("After class, in function:", model, design)  # Sees arguments
    return Meta

MyMeta = generate_meta_options('a', 1)
print(MyMeta.model, MyMeta.design)

Try it online!