python:元类中 __new__ 的计时

发布于 2024-09-19 08:17:07 字数 613 浏览 1 评论 0原文

以下代码无法编译;它说

NameError:名称“字段”不是 定义

在最后一行 。是因为在达到 fields 赋值之后才调用 __new__ 吗?我应该怎么办?

class Meta(type):
    def __new__(mcs, name, bases, attr):
        attr['fields'] = {}
        return type.__new__(mcs, name, bases, attr)

class A(metaclass = Meta):
    def __init__(self, name):
        pass

class B(A):
    fields['key'] = 'value'

编辑:

我发现这不是时间问题;而是问题。这是一个隐藏名字的问题。如果我改为编写 A.fields ,则效果很好。

我想知道为什么我不能使用 fieldssuper().fields

The following code doesn't compile; it says

NameError: name 'fields' is not
defined

in the last line. Is it because __new__ isn't called until after the fields assignment is reached? What should I do?

class Meta(type):
    def __new__(mcs, name, bases, attr):
        attr['fields'] = {}
        return type.__new__(mcs, name, bases, attr)

class A(metaclass = Meta):
    def __init__(self, name):
        pass

class B(A):
    fields['key'] = 'value'

EDIT:

I found that it's not a problem with timing; it's a problem with name hiding. Works fine if I write A.fields instead.

I'd like to know why I can't use fields or super().fields.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寄风 2024-09-26 08:17:07

fields['key'] = 'value' 在元类机制启动之前运行。

class foo(object):
    var1 = 'bar'

    def foobar(self):
        pass

当 python 命中 class 语句时,它会进入一个新的本地命名空间。

  1. 它评估 var1 = 'bar' 语句。这相当于 locals()['var1'] = 'bar'

  2. 然后它会计算 def foobar 语句。这相当于 locals()['var'] = 编译函数的结果

  3. then传递locals(),连同类名、继承类和元类到元类 __new__ 方法。在示例中,元类只是type

  4. 然后,它退出新的本地命名空间,并将从 __new__ 返回的类对象粘贴到名为 foo 的外部命名空间中。

当您使用 A.fields 时,您的代码可以工作,因为类 A 已经创建,因此上述过程已在安装 Meta 时执行A 中的字段

您不能使用 super().fields,因为类名 B 未定义为在 super 运行时传递给 super。也就是说,您需要它是 super(B).fieldsB 是在类创建之后定义的。

更新

这里有一些代码可以根据您对我对问题的评论的回复来执行您想要的操作。

def MakeFields(**fields):
    return fields

class Meta(type):
    def __new__(mcs, name, bases, attr):
        for base in bases:
            if hasattr(base, 'fields'):
                inherited = getattr(base, 'fields')
                try:
                    attr['fields'].update(inherited)
                except KeyError:
                    attr['fields'] = inherited
                except ValueError:
                    pass
        return type.__new__(mcs, name, bases, attr)

class A(metaclass=Meta):
    fields = MakeFields(id='int',name='varchar') 

class B(A):
    fields = MakeFields(count='int')

class C(B):
    pass

class Test(object):
    fields = "asd"

class D(C, Test):
    pass

print C.fields
print D.fields

The fields['key'] = 'value' runs before the metaclass machinery kicks in.

class foo(object):
    var1 = 'bar'

    def foobar(self):
        pass

when python hits the class statement, it enters a new local namespace.

  1. it evaluates the var1 = 'bar' statement. this is equivalent to locals()['var1'] = 'bar'

  2. it then evaluates the def foobar statement. this is equivalent to locals()['var'] = the result of compiling the function

  3. It then passes locals(), along with the classname, the inherited classes and the metaclass to the metaclasses __new__ method. In the example case, the metaclass is simply type.

  4. It then exits the new local namespace and sticks the class object returned from __new__ in the outer namespace with the name foo.

Your code works when you use A.fields because the class A has already been created and the above process has hence been executed with your Meta installing fields in A.

You can't use super().fields because the classname B is not defined to pass to super at the time that super would run. that is that you would need it to be super(B).fields but B is defined after class creation.

Update

Here's some code that will do what you want based on your reply to my comment on the question.

def MakeFields(**fields):
    return fields

class Meta(type):
    def __new__(mcs, name, bases, attr):
        for base in bases:
            if hasattr(base, 'fields'):
                inherited = getattr(base, 'fields')
                try:
                    attr['fields'].update(inherited)
                except KeyError:
                    attr['fields'] = inherited
                except ValueError:
                    pass
        return type.__new__(mcs, name, bases, attr)

class A(metaclass=Meta):
    fields = MakeFields(id='int',name='varchar') 

class B(A):
    fields = MakeFields(count='int')

class C(B):
    pass

class Test(object):
    fields = "asd"

class D(C, Test):
    pass

print C.fields
print D.fields
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文