元类(metaclass)是什么?元类是一种可以创建类的一种特殊类
元类继承自type类
metaclass、class、instance之间的关系:type/metaclass -> class -> instance,也就是type/metaclass创建了类,类创建了实例
type类
1.用户自定义类都是type这个类的实例,也就是说type这个类是造物的上帝
这可以在代码中验证:
class MyClass:
pass
instance = MyClass()
print(type(instance))
print(type(MyClass))
# 输出:
2.使用type动态创建类
比如,用户定义一个类:
class MyClass:
data = 1
python真正执行的是下面这段代码:
MyClass = type(classname, superclasses, attributedict)
type会进一步执行:
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)
可以通过如下代码验证:
class MyClass:
data = 1
instance = MyClass()
print(MyClass, instance)
print(instance.data)
#####################################
# 使用type来创建类
MyClass = type('MyClass', (), {'data': 1}) # 第一个参数是类名、第二个参数是父类、第三个参数是类成员
instance = MyClass()
print(MyClass, instance)
print(instance.data)
# 输出:
<__main__.MyClass object at 0x7f435125c080>
1
<__main__.MyClass object at 0x7f435125c908>
1
3.实例化自定义类
自定义类实例化时会调用type的__call__方法,执行MyClass的__new__和__init__方法进行初始化。
举个例子
建议跑一下如下代码,感受一下:
# 自定义元类
class Mymeta(type):
def __init__(cls, name, bases, dic): # 这里的cls是传入的自定义类
super().__init__(name, bases, dic)
print('===>Mymeta.__init__()')
print(cls.__name__)
print(dic)
print(cls.data)
def __new__(cls, *args, **kwargs): # 这里的cls是Mymeta,也就是metaclass本身
print('===>Mymeta.__new__()')
print(cls.__name__)
return type.__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
"""可以在这里改变自定义类实例化时的过程,比如可以在这里实现单例模式"""
print('===>Mymeta.__call__()')
# 如下代码等价于:return super().__call__(*args, **kwargs),也就是type元类的__call__方法也会执行自定义类的初始化
obj = cls.__new__(cls)
cls.__init__(cls, *args, **kwargs)
return obj
# 自定义类,并将metaclass设为我们定义的元类, 等价于:Foo = Mymeta("Foo", (), {'data': 'Foo data'})
class Foo(metaclass=Mymeta):
data = 'Foo data'
def __init__(self, name):
print('Foo.__init__()')
= name
def __new__(cls, *args, **kwargs):
print('Foo.__new__()')
return object.__new__(cls)
# 实例化: 实例化时会调用Mymeta的__call__方法
foo = Foo('foo-instance')
print()
输出:
===>Mymeta.__new__()
Mymeta
===>Mymeta.__init__()
Foo
{'__module__': '__main__', '__qualname__': 'Foo', 'data': 'Foo data', '__init__': , '__new__': }
Foo data
===>Mymeta.__call__()
Foo.__new__()
Foo.__init__()
foo-instance
元类和装饰器的对比
相同点:元类和装饰器都有装饰的作用,都可以改变原对象或原类的行为;
类装饰器就是在类里面定义了__call__方法,之后在函数执行的时候会调用类的__call__方法。而在元类中重载了__call__方法,在类实例化的时候也是调用了元类的__call__方法,从这方面来讲是很像。
不同点:装饰器改变的是函数的行为,而元类改变的是类的行为。
什么时候会用到元类需要改变用户自定义类实例化时的行为(重载元类的__call__方法),比如下面“使用metaclass实现单例模式”的例子。当然可以直接对类进行修改,但如果有多个类都要进行同样的操作,那么可以使用元类避免重复代码;
需要根据用户自定义类进行动态修改的(重载元类的__new__或__init__方法),比如下面“改写sqlalchemy数据模型元类”的例子。
应用案例
1.使用metaclass实现单例模式
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class MyClass(metaclass=Singleton):
pass
MyClass实例化的时候会调用元类的__call__方法。
如果有多个类都要实现单例模式,那么直接将它们的元类改为我们自定义的元类(Singleton)即可,非常简洁。
2.改写sqlalchemy数据模型元类
一般建立ORM数据模型的方式:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.mysql import INTEGER, BIGINT, VARCHAR, TIMESTAMP, FLOAT, BOOLEAN, TEXT
Base = declarative_base()
class Order(Base):
__tablename__ = "order"
order_id = Column(Integer, primary_key=True)
customer_name = Column(String(30), nullable=False)
order_date = Column(DateTime, nullable=False, default=datetime.now())
order_items = relationship(
"OrderItem", cascade="all, delete-orphan", backref="order"
)
需求:
如果需要定义多个数据模型,且这几个模型有很多一样标准的字段,那么上述的方式就比较冗余且不好维护
改写:
# 修改基本数据模型的元类DeclarativeMeta
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
from sqlalchemy.dialects.mysql import INTEGER, BIGINT, VARCHAR, TIMESTAMP, FLOAT, BOOLEAN, TEXT
class MyModelMeta(DeclarativeMeta):
"""在DeclarativeMeta元类的基础上添加根据标准字段自动创建字段的功能,也就是在创建类的过程中,根据columns的字段列表创建类属性,属性名就是字段列表的一个个字段名,属性值就是standard_columns_cls类对应字段的属性值。"""
def __init__(cls, classname, bases, dict_):
if hasattr(cls, "columns") and hasattr(cls, "standard_columns_cls"):
for column in cls.columns:
if hasattr(cls.standard_columns_cls, column):
value = getattr(cls.standard_columns_cls, column)
type.__setattr__(cls, column, value)
else:
raise Exception("The column '%s' is not in StandardColumns" % column)
DeclarativeMeta.__init__(cls, classname, bases, dict_)
Base = declarative_base(metaclass=MyModelMeta)
class StandardColumns(object):
"""定义标准字段规范类,统一管理"""
order_id = Column(Integer, primary_key=True)
customer_name = Column(String(30), nullable=False)
order_date = Column(DateTime, nullable=False, default=datetime.now())
order_items = relationship(
"OrderItem", cascade="all, delete-orphan", backref="order"
)
class Order(Base):
__tablename__ = "order"
standard_columns_cls = StandardColumns # 选择使用的标准字段规范类
columns = ["order_id", "customer_name", "order_date", "order_items"] # 标准字段列表
这样,用户在写模型类时,只需要定义一个字段列表以及指定标准字段规范类即可,元类会帮我们自动映射到标准字段上。