• 如果需要你将类中所有属性强制转为大写该怎么办
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 这个时候就需要使用Python的元类功能了
# metaclass will automatically get passed the same argument
# that you usually pass to 'type'
def upper_attr(future_class_name, future_class_parents, future_class_attrs):
    """
    返回一个类对象(class object),将它的属性全部转为大写。
    """
    # 如果属性是以__ 开头(魔法方法)不改变
    uppercase_attrs = [
        attr if attr.startwith('__') else attr.upper(): v
        for attr, v in future_class_attrs.items()
    ]
    # 使用type创建类对象
	return type(future_class_name, future_class_parents, uppercase_attrs)

# 使用metaclass 影响当前模块的所有类
__metaclass__ = upper_attr

class Foo():
    # 如果想要仅在Foo类中生效,可以将上面的__metaclass__放在Foo类里面。
    bar = 'bip'

测试

1
2
3
4
5
6
>>> hasattr(Foo, 'bar')
False
>>> hasattr(Foo, 'BAR')
True
>>> Foo.BAR
'bip'

现在为元类使用一个真正的类(上面是使用函数)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 看,这次是类
class UpperAttrMetaclass(type):
    # __new__ 方法在 __init__方法之前执行
    # 和java相同,__new__ 是为了在内存中创建这个对象并返回它
    # __init__ 只是将参数传入这个对象,并进行初始化
    # 只有当你想要控制这个类怎样生成的时候才会使用__new__
    # 这里,因为我们想要将类的属性全部转为大写,所以我们需要定制__new__方法
    def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attrs):
        uppercase_attrs = {
            attr if attr.startwith("__") else attr.upper(): v
            for attr, v in future_class_attrs.items()
        }
        return type(future_class_name, future_class_parents, future_class_attrs)
    
    

真实世界应该这样写(为了规范)

1
2
3
4
5
6
7
class UpperAttrMetaclass(type):
    def __new__(cls, clsname, bases, attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in attrs.items()
        }
        return type(clsname, bases, uppercase_attrs)

但是这不是正常的OOP,我们直接调用了type来生成并返回,而不是覆盖父类的new方法

1
2
3
4
5
6
7
class UpperAttrMetaclass(type):
    def __new__(cls, clsname, bases, attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in attrs.items()
        }
        return type.__new__(cls, clsname, bases, uppercase_attrs)

注意看最后一行,我们调用的是父类的new方法

因为调用的是父类的new方法,所以我们可以使用super方法来化简这个调用

1
2
3
4
5
6
7
8
9
class UpperAttrMetaclass(type):
    def __new__(cls, clsname, bases, attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in attrs.items()
        }
        # 区别
        return super(UpperAttrMetaclass, cls).__new__(
            cls, clsname, bases, uppercase_attrs)

然后在Python3中你就可以这样调用了

1
class Foo(object, metaclass=MyMetaclass, kwarg1=value1):

上面这种调用等价于在元类MyMetaclass中这样调用

1
2
3
class MyMetaclass(type):
    def __new__(cls, clsname, bases, dct, kwargs1=default):
        ...

使用元类的代码的复杂性背后的原因并不是元类,而是因为你通常使用元类来做一些扭曲的事情,这些事情依赖于内省、操纵继承、诸如 dict 这样的变量。

事实上,元类在使用黑魔法和复杂的东西时特别有用,但它们本身就很简单

元类通常的应用

  • intercept a class creation 拦截一个类的创建
  • modify the class 修改类
  • return the modified class 返回修改过的类

Why would you use metaclasses classes instead of functions?

魔法方法__metaclass__可以接受任何可低啊用的,为什么要使用一个明显更复杂的类呢? 有以下几个原因

  • The intention is clear. When you read UpperAttrMetaclass(type), you know what’s going to follow 它的意图很清晰,当你读UpperAttrMetaclas(type)这个类时,你知道接下来会发生什么

  • You can use OOP. Metaclass can inherit from metaclass, override parent methods. Metaclasses can even use metaclasses. 你可以使用面向对象编程,元类可以继承元类,重载父类的方法,元类甚至可以使用元类

  • Subclasses of a class will be instances of its metaclass if you specified a metaclass-class, but not with a metaclass-function. 如果你指定了一个元类,而不是一个元类函数(像第一种函数那样),那么一个类的子类就是它元类的实例

  • You can structure your code better. You never use metaclasses for something as trivial as the above example. It’s usually for something complicated. Having the ability to make several methods and group them in one class is very useful to make the code easier to read. 您可以更好地构造代码。对于上面的例子这样琐碎的事情,永远不要使用元类。通常是为了一些复杂的事情。能够创建多个方法并将它们分组到一个类中,这对于使代码更易于阅读非常有用。

  • You can hook on __new__, __init__ and __call__. Which will allow you to do different stuff, Even if usually you can do it all in __new__, some people are just more comfortable using __init__. 你可以挂接__new__, init__和__call。这将允许你做不同的事情,即使通常你可以在__new__做所有的事情,一些人只是更习惯使用__init__。

为什么使用元类

Python之父给出了答案

Metaclasses are deeper magic that 99% of users should never worry about it. If you wonder whether you need them, you don’t (the people who actually need them to know with certainty that they need them and don’t need an explanation about why).

The main use case for a metaclass is creating an API. A typical example of this is the Django ORM. It allows you to define something like this:

元类的主要用途就是创建APi,一个典型的例子就是Django ORM,它允许你像这样定义。

1
2
3
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

但是,如果你这样做

1
2
person = Person(name='bob', age='35')
print(person.age)

It won’t return an IntegerField object. It will return an int, and can even take it directly from the database. 它不会返回一个IntegerField 对象,它返回一个int对象,你甚至可以从数据库中直接拿到它。

这是可能的,因为模型。Model定义了__metaclass__,它使用了一些魔法,将你刚刚用简单语句定义的Person变成一个复杂的数据库字段钩子。

Django通过暴露一个简单的API并且通过使用元类,从这个API重新创建代码来完成幕后的实际工作,使复杂的事情变得简单。

参考资料 https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python