Python一分钟:元类(metaclass)是什么?

类可以被动态声明

通常我们声明类的方式是静态的,如下:

class A:
    age = 18
    def f(self):
        print(1)

调用类(不是实例)的__dict__属性将会返回一个字典,该字典以键值对的形式存储了类的属性和方法。使用.调用方法或属性的时候,实际是在__dict__中寻找。

print(A.__dict__)
# {'__module__': '__main__', 'age': 18, 'f': <function A.f at 0x00000168d5dbcfe0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

打印类(不是实例)的类型,我们会发现它的类型是type

print(type(A))
#  <class 'type'>

使用type可以以动态的方式声明一个类,type要求三个参数:类名 、父类 和__dict__字典。下面以同态的方式重新声明A类。

def f(self):
    print(1)

d = {
    "f": f,
    "age": 18,
    }

A = type("A", (), d)

和静态声明相同,我们可以对其进行实例化。

a = A()

元类什么?

在了解元类之前先介绍类的__new__方法和__init__方法。
类在被实例化的过程中,首先会调用__new__方法,接着会调用__init__方法:

class A:
    def __new__(cls, *args, **kwargs):
        print("__new__ method called")
        print(f"cls: {cls}")
        return super().__new__(cls)
    
    def __init__(self) -> None:
        print("__init__ method called")

a = A()
__new__ method called
cls: <class '__main__.A'>
 __init__ method called

声明一个类并继承type,它就能像type一样声明其它类,继承了type的类称之为元类。

  • 动态声明
class M(type):
    def __new__(cls, name, bases, attrs):
        print("__new__ is called")
        return type.__new__(cls, name, bases, attrs)
    
A = M('A', (), {})
# 注意这个过程中`__new__`被调用了
# __new__ is called
  • 静态声明
class M(type):
    def __new__(cls, name, bases, attrs):
        print("__new__ is called")
        return type.__new__(cls, name, bases, attrs)
    
class A(metaclass=M):
    pass
# 注意这个过程中`__new__`被调用了,不过是隐式调用。
# __new__ is called

元类有什么作用?

元类(metaclass)一般用于解决继承无法解决的问题,比如我想规定一个类中不能有以test_开头的方法。

class M(type):
    def __new__(cls, name, bases, attrs):
        for key in attrs.keys():
            if key.startswith('test_'):
                raise ValueError('test_开头的方法不允许')
        return type.__new__(cls, name, bases, attrs)
    
class A(metaclass=M):
    def test_case(self):
        pass

我们在元类中定义__new__, __init____call__方法,它们被调用的时机是不同的,可以自己测试一下。

from typing import Any

class M(type):
    def __new__(cls, name, bases, attrs):
        return type.__new__(cls, name, bases, attrs)
    
    def __init__(self, name, bases, attrs):
        return type.__init__(self, name, bases, attrs)
    
    def __call__(cls, *args: Any, **kwds: Any) -> Any:
        return super().__call__(cls, *args, **kwds)
    
class A(metaclass=M):
    def test_case(self):
        pass

a = A()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值