元编程概念来自LISP和smalltalk
用来生成代码的程序称为元程序metaprogram,编写这种程序就称为元编程metaprogramming
Python语言能够通过反射实现元编程
Python中
所有非object类都继承自object类
所有类的类型包括type类都是type
type类继承自object类,object类的类型也是type类
type类
type构建类
type源码
class type(object):
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
type(object) -> the object's type
,返回对象的类型,例如type(10)返回int类型
type(name, bases, dict) -> a new type
返回一个新的类型
what是构建类的名字
bases是构建类的基类, 默认是None(基类是object类)
dict是构建类的类字典
def __init__(self):
self.x = 1000
def show(self):
print(self.x)
XClass = type("x", (object,), {'a':100, 'b':'abc',
'__init__':__init__,
'show':show})
print(XClass)
print(XClass.__name__)
print(XClass.__bases__)
print(XClass.mro())
print(XClass().a)
print(XClass.__dict__)
print(XClass().x)
print(XClass().show())
#运行结果:
<class '__main__.x'>
x
(<class 'object'>,)
[<class '__main__.x'>, <class 'object'>]
100
{'a': 100, 'b': 'abc', '__init__': <function __init__ at 0x0000024344784A60>, 'show': <function show at 0x0000024344784AE8>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'x' objects>, '__weakref__': <attribute '__weakref__' of 'x' objects>, '__doc__': None}
1000
1000
None
可以借助type构造任何类, 用代码生成代码, 这就是元编程
构建元类
一个类可以继承自type类
class MetaClass(type):
def __new__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
return super().__new__(cls, *args,**kwargs)
继承自type类, MetaClass就是元类, 它可以创建出其他类
class MetaClass(type): # 继承自type
def __new__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
print()
return super().__new__(cls, *args, **kwargs)
class A(metaclass=MetaClass): # 元类默认type, 改成Metaclass
id = 200
def __init__(self):
print("A init---")
class B(A): # 继承时, 元类与父类的元类相同
pass
C = MetaClass('C', (), {}) # 元类使用这种方式构建新的类
print(type(A), A.__bases__)
print(type(B), B.__bases__)
print(type(C), C.__bases__)
# D, E是type的实例
class D:pass # D = type('D', (), {})
E = type('E', (), {})
print(type(D), D.__bases__)
print(type(E), E.__bases__)
print("==============")
class F(MetaClass):pass # F继承自MetaClass
print(type(F), F.__bases__)
<class '__main__.MetaClass'>
('A', (), {'__module__': '__main__', '__qualname__': 'A', 'id': 200, '__init__': <function A.__init__ at 0x000001C4BC4D4F28>})
{}
<class '__main__.MetaClass'>
('B', (<class '__main__.A'>,), {'__module__': '__main__', '__qualname__': 'B'})
{}
<class '__main__.MetaClass'>
('C', (), {})
{}
<class '__main__.MetaClass'> (<class 'object'>,)
<class '__main__.MetaClass'> (<class '__main__.A'>,)
<class '__main__.MetaClass'> (<class 'object'>,)
<class 'type'> (<class 'object'>,)
<class 'type'> (<class 'object'>,)
==============
<class 'type'> (<class '__main__.MetaClass'>,)
从运行结果可以分析出__new__(cls, *args, **kwargs)
的参数结构
中间是一个元组('A', (), {'__module__': '__main__', '__qualname__': 'A', 'id': 200, '__init__': <function A.__init__ at 0x000001C4BC4D4F28>})
对应(name, bases, dict)
修改代码如下:
class MetaClass(type): # 继承自type
def __new__(cls, name, bases, dict):
print(cls)
print(name)
print(bases)
print(dict)
return super().__new__(cls, name, bases, dict)
从运行结果可以看出,只要元类是MetaClass, 创建类对象时, 就会调用MetaClass的__new__
方法
上例中,F也是元类, F --继承自–> ModelMeta --继承自–> type。type(元类) 返回type。但是type(被metaclass修改了的元类的类)返回其元类, 就像A修改了元类为MetaClass, type(A)返回MetaClass, 继承A的B类也继承了基类的元类
元类的应用
以sqlalchemy模块为例, 不考虑内部描述器实现, 简单实现基本属性, 主要看元编程应用过程
# 不考虑描述器, 元编程实验例子
class Column:
def __init__(self, filename=None, pk=False, nullable=False):
self.filename = filename
self.pk = pk
self.nullable = nullable
def __repr__(self):
return "<{} {}>".format(__class__.__name__, self.filename)
# 简单基本属性实现
class ModelMeta(type):
def __new__(cls, name, bases, attrs:dict):
print(cls)
print(name, bases, attrs)
if '__tablename__' not in attrs:
attrs['__tablename__'] = name.lower() # 添加表名
primarykeys = []
for k, v in attrs.items():
if isinstance(v, Column):
if v.filename is None or v.filename.strip() == '':
v.filename = k # 字段没有名字使用属性名
if v.pk: # 主键
primarykeys.append(v)
attrs['__primarykeys__'] = primarykeys
return super().__new__(cls, name, bases, attrs)
class Base(metaclass=ModelMeta):
"""从MedolBase继承的类的类型都是ModelMeta"""
class Student(Base):
id = Column(pk=True, nullable=False)
name = Column('username', nullable=False)
age = Column()
print('--' * 30)
print(Student.__dict__)
元编程总结
元类是制造类的工厂,是用来构造类的类。
构造好元类, 就可以在类定义时, 使用关键字参数metaclass指定元类, 可以使用最原始的metatype(name,bases, dict)的方式构造一个类
元类的 __new__()
方法中,可以获取元类信息、当前类、基类、类属性字典。
元编程一般用于框架开发中
开发中除非你明确的知道自己在干什么,否则不要随便使用元编程
Django、SQLAlchemy使用了元类,让我们使用起来很方便