Python 模块级的 metaclass

13 投票
2 回答
2809 浏览
提问于 2025-04-16 23:22

我看了Python中的 metaclass 是什么?

然后我尝试复制例子中的上面那个 metaclass,结果发现这并不是在所有情况下都能用:

def upper(cls_name, cls_parents, cls_attr):                                     
    """ Make all class attributes uppper case """                               
    attrs = ((name, value) for name, value in cls_attr.items()                  
            if not name.startswith('__'))                                       
    upper_atts = dict((name.upper(), value) for name, value in attrs)           
    return type(cls_name, cls_parents, upper_atts)                              

__metaclass__ = upper #Module level
class Foo:                                                                      
    bar = 1                                                                     
f =  Foo()
print(f.BAR) #works in python2.6

上面的代码在 Python 3 中会失败(出现属性错误),我觉得这很正常,因为在 Python 3 中,所有的类都有一个叫做 object 的父类,而 metaclass 的解析会进入这个 object 类。

我的问题:

我该如何在 Python 3 中创建一个模块级的 metaclass?

2 个回答

4

如果你想把所有的属性都变成大写,最好是用 __init__ 方法来实现,而不是用元类。

元类是比99%的用户需要的东西更复杂的东西。如果你在想自己是否需要元类,那你其实是不需要的(真正需要的人早就知道自己需要,不用解释为什么)。

-- Python 大师 Tim Peters

如果你需要更复杂的功能,可以考虑使用 类装饰器

只要你想做的事情可以通过类装饰器或初始化来实现,就没必要去了解元类和类是怎么创建的。

不过,如果你真的想用元类,可以把它作为关键字参数传给类。

class Foo(object, metaclass=UpperCaseMetaClass)

这里的 UpperCaseMetaClass 是一个扩展了 type 的类,而不是一个方法。

class UpperCaseMetaClass(type):
    def __new__():
        #Do your Magic here.
12

模块级的元类其实并不是“模块级”的,它和类的初始化方式有关。在创建类的时候,系统会查找一个叫做 "__metaclass__" 的变量。如果在当前环境找不到,它就会去全局环境找。因此,如果你在模块级别定义了一个 __metaclass__,那么之后创建的每个类都会使用这个元类,除非你为某个类特别指定了其他的元类。

在Python 3中,你需要在类定义时通过 metaclass= 来指定元类。所以在Python 3里就没有模块级的元类了。

那么该怎么做呢?很简单:你需要为每个类明确指定元类

这其实并不会增加太多工作量,如果你有成百上千个类,不想手动去改,还可以用正则表达式来快速搜索和替换。

撰写回答