元编程(Metaprogramming)for Python:构造改道

it2023-07-24  69

简单说元编程

元编程概念来自LISP和smalltalk。用代码来写代码,用编程来编程的程序。 我们写程序是直接写代码,是否能够用代码来生成未来我们需要的代码吗?这就是元编程。 例如,我们写一个类class A,能否用代码生成一个类出来? 用来生成代码的程序称为元程序metaprogram,编写这种程序就称为元编程metaprogramming。Python语言能够通过反射实现元编程。 Python中,所有非object类都继承自object类 object->解决where的问题。所有类的类型包括type类都是type,type(type)也是是type。 type根基类,解决->what的问题;type构造出类。type类继承自object类,object类的类型也是type类

元编程(Metaprogramming)……

听起来很酷!听起来像是一种用于高级企业架构的设计技术,或是一个在报纸、杂志上流行的时尚词。 事实上,元编程远非一个抽象概念或一个宣传名词,它其实是一种脚踏实地的、务实的编程技术。它不只是听起来酷,它确实酷。在Python中,可以用它完成如下一些工作。 ●编写一个Python程序来连接外部系统——也许是一个Web服务或一个Java 程序。使用元编程,可以编写一个包装器用来接受任何方法调用,然后把这些调用转发给这个外部系统。如果某个人后来为这个外部系统添加了方法,则无须修改这个Python的包装器,它会立刻自动支持这些新加入的方法。这很神奇吧! ●当遇到一个最好是使用某种特定的语言来解决的问题,但如果自己去定义这种语言、自己去定制解析器及其他种种工作会遇到不小的麻烦时,你可以仅仅使用Python语言,把它改造成像专门处理这个问题的专属语言。你甚至还可以写出一个微型解释器从文件中读取这个基于Python语言的代码。 ●可以把Python程序的重复性降到一个Java程序员都不敢想的程度。比如有一个包含 20个方法的类,这些方法看起来都差不多。如果这些方法只用几行代码一块定义会怎么样?或者你想调用一长串名字相似的方法,如果能用一小行代码就能调用这些名字具有一定模式的方法(比如所有的方法名都以 test打头),那么你喜不喜欢呢? ●可以改造 Python使之满足你的需要,而不是去适应语言本身。例如,可以用你喜欢的方式去增强任何一个类(即使是像 Array这样的核心类);可以把日志功能封装到你所要监控方法的起止处;当客户继承你关心的类时,可以执行一段定制代码……这个列表还可以继续列下去,远远超出你的想象力。 
> 元编程赋予了你这些能力。下面让我们一起来看看它是个什么样的。

"元"这个字眼

> 你很可能期望从定义开始理解元编程。 元编程是写出编写代码的代码。 
> 下面会给出一个更准确的定义,但是目前先使用这个。"编写代码的代码"究竟是什么意思?它到底在日常编程中有什么用处?在回答这些问题之前,先看看编程语言本身。

鬼城与自由市场
Ghost Towns and Marketplaces

> 如果把代码看成是一个世界,则它充斥着各种生机勃勃的市民∶变量、类、方法等。如果说得更技术性一些,则可以把这些市民称为语言构件 
> (language construct)。 
> 在很多编程语言中,语言构件的行为更像是幽灵,而不是有血有肉的人∶你可以在源代码中看到它们,然而在程序运行前它们就消失了。以C++为例,一旦编译器完成了它的工作,像变量和方法这样的东西就看不见摸不着了∶它们只是内存的位置而已。你不能向类询问它的实例方法,因为在问这个问题时,这个类已经淡出视野了。像C+这样的语言,运行时是一个可怕的寂静之所———鬼城。 
> 在其他的语言中(比如 Ruby),运行时看起来更像是一个繁忙的自由市场,绝大多数的语言构件依然存在,还在到处忙碌着。你甚至可以走到一个构件面前并且询问关于它自己的问题。这称为内省(introspection)。

代码生成器与编译器

> 在元编程中,你可以写出编写代码的代码。这不是代码生成器与编译器所做的工作么?比如,你可以写出带注解的 Java 代码,然后使用代码生成器输出 XML配置文件。从广义上说,这个XML生成过程也是元编程的一个例子。事实上,很多人听到"元"这个字眼时,首先会想到代码生成。 
> 这种方式的元编程意味着你会先使用一个程序来创建或处理另外一个独立的程序,然后运行后面这个程序。在运行完代码生成器之后,最终运行之前,你可以阅读这些生成的代码(如果想检验自己的忍耐力),甚至手工修改这些代码。这也是C++模板背后的原理∶C+编译器会在编译之前把模板转换为一个普通的C++代码,然后再运行那个编译好的程序。


从源码看什么是元编程

# 我们可以看到源码里面,type有三个参数(name,bases,dict) ->a new type print(2,"-"*30) ##参数1,name并不重要 # 我们用元编程写一个类,默认根基类是type XClass = type('X',(),{}) # print(XClass) print(type(XClass)) print(XClass.__name__) #这里names是“X",但标识符是XClass print(XClass.__bases__) a = XClass() print(3,"-"*30) # 第二个参数,父类。 # 我们再用list做基类生成一个类,那这个类就有父类list的特征: XClass = type('X',(list,),{}) # #就相当于语法糖的写法 Class XClass(list)" pass,但是语法糖的写法不能自定义第一个参数name print(XClass) print(type(XClass)) print(XClass.__name__) #这里names是“X",但标识符是XClass print(XClass.__bases__) a = XClass() a.append(1) a.extend(range(10,15)) print(a) print(4,"-"*40) #第三个参数是属性: XClass = type('X',(list,),{'a':123,'b':'abc'}) # print(XClass) print(type(XClass)) print(XClass.__name__) #这里names是“X",但标识符是XClass print(XClass.__bases__) print(XClass.__dict__) a = XClass() a.append(1) a.extend(range(10,15)) print(a) print(5, "-"*50) # 用元编程的方法写一个类: def a(self): print("init~~~~~") self.x =1000 def show(self): print(self.x) XClass= type('name_XYZ', (list,),{'a':123,"b":"xyz",'__init__':a,'show':show}) print(XClass) print(6,"-"*60) # 等价于我们语法糖的写法 class XClass(list): a = 123 b = "xyz" def __int__(self): self.x = 1000 def show(self): print(self.x) print(XClass)

执行结果:

2 ------------------------------ <class '__main__.X'> <class 'type'> X (<class 'object'>,) 3 ------------------------------ <class '__main__.X'> <class 'type'> X (<class 'list'>,) [1, 10, 11, 12, 13, 14] 4 ---------------------------------------- <class '__main__.X'> <class 'type'> X (<class 'list'>,) {'a': 123, 'b': 'abc', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'X' objects>, '__weakref__': <attribute '__weakref__' of 'X' objects>, '__doc__': None} [1, 10, 11, 12, 13, 14] 5 -------------------------------------------------- <class '__main__.name_XYZ'> 6 ------------------------------------------------------------ <class '__main__.XClass'>

演示如何使用元编程

#构造其它类的类 class ModelMeta(type): def __new__(cls, *args, **kwargs): print(cls) #类本身,ModelMeta print(args) # 三元素:name,bases,dict print(kwargs) #没用到 return super().__new__(cls,*args,**kwargs) #这里的cls只与当前类有关,所以就是ModelMeta # class A: # 这样写是继承自object,使用元类type构造A类对象。 class A(metaclass=ModelMeta): #构造A的类是ModelMeta,元类变了;继承自object不会变 pass print(type(A), A.__bases__) # A的元类是ModelMeta,A的__base__继承自依然是object

既然没有用到**kwargs,我们就可以把代码进一步拆解,实现继承的改道:有原来的继承自object,改为继承自ModelMeta。

#构造其它类的类,实现继承改道 class ModelMeta(type): def __new__(cls, name, bases, attrs): print(cls) #类本身,ModelMeta print(name, bases, attrs) # 三元素:name,bases,dict attrs['a'] = 123 return super().__new__(cls, name, bases, attrs) #这里的cls只与当前类有关,所以就是ModelMeta # class A: # 这样写是继承自object,使用元类type构造A类对象。 class A(metaclass=ModelMeta): #构造A的类是ModelMeta,元类变了;继承自object不会变 pass print(type(A), A.__bases__) # A的元类是ModelMeta,A的__base__继承自依然是object

执行结果:

<class '__main__.ModelMeta'> A () {'__module__': '__main__', '__qualname__': 'A'} <class '__main__.ModelMeta'> (<class 'object'>,)

元类的继承

class ModelMeta(type): def __new__(cls, name, bases, attrs): print(cls) #类本身,ModelMeta print(name, bases, attrs,"++++") # 三元素:name,bases,dict attrs['a'] = 123 return super().__new__(cls, name, bases, attrs) #这里的cls只与当前类有关,所以就是ModelMeta # class A: # 这样写是继承自object,使用元类type构造A类对象。 class A(metaclass=ModelMeta): #构造A的类是ModelMeta,元类变了;继承自object不会变 pass print(type(A), A.__bases__) # A的元类是ModelMeta,A的__base__继承自依然是object print("**"*30) # 那进一步推论,一些更深入的问题 class B(A): pass print(type(B),B.__bases__) ### ? # B的类是modelMeta,因为B通过构造A的ModelMeta new构造出来的; # B的object __bases__ 是A,因为B继承自A

执行结果:

<class '__main__.ModelMeta'> A () {'__module__': '__main__', '__qualname__': 'A'} ++++ <class '__main__.ModelMeta'> (<class 'object'>,) ************************************************************ <class '__main__.ModelMeta'> B (<class '__main__.A'>,) {'__module__': '__main__', '__qualname__': 'B'} ++++ <class '__main__.ModelMeta'> (<class '__main__.A'>,)

总结:父类的元类改了,所有子类的type都跟着改

那如果类元类的对象呢?

C = ModelMeta('CCC',(),{}) print(type(C),C.__bases__) # ModelMeta, object # C是ModelMeta的一个类实例,因为Modelmeta是由super()objectnew出来的,所以C的type是object # C的__base()__继承来自其父type的父object,所以结果是object

如果能推理出这个结果,就说明理解了元编程的构造和继承:

<class '__main__.ModelMeta'> A () {'__module__': '__main__', '__qualname__': 'A'} ++++ <class '__main__.ModelMeta'> (<class 'object'>,) ************************************************************ <class '__main__.ModelMeta'> B (<class '__main__.A'>,) {'__module__': '__main__', '__qualname__': 'B'} ++++ <class '__main__.ModelMeta'> (<class '__main__.A'>,) <class '__main__.ModelMeta'> CCC () {} ++++ <class '__main__.ModelMeta'> (<class 'object'>,)

简单说,只要不进入元编程类,就不会引起构造类变化:

class D: pass print(type(D),D.__bases__) E = type("E",(),{}) print((type(E),E.__bases__)) print("**"*50)

打印结果:

<class 'type'> (<class 'object'>,) (<class 'type'>, (<class 'object'>,))

最终我们进入元编程类:

class F(ModelMeta): pass print(type(F), F.__bases__) # F是个普通类型,不是元类。它继承自ModelMeta,ModelMeta继承自type,所以bases是ModeMeta; print("F.Mro",F.mro(F)) # ModelMeta是由type构造的,F也要通过type构造。 print("__"*60)

输出结果:

<class '__main__.ModelMeta'> A () {'__module__': '__main__', '__qualname__': 'A'} ++to用元类来构造类对象++ <class '__main__.ModelMeta'> (<class 'object'>,) ************************************************************ <class '__main__.ModelMeta'> B (<class '__main__.A'>,) {'__module__': '__main__', '__qualname__': 'B'} ++to用元类来构造类对象++ <class '__main__.ModelMeta'> (<class '__main__.A'>,) ******************************************************************************** <class '__main__.ModelMeta'> CCC () {} ++to用元类来构造类对象++ <class '__main__.ModelMeta'> (<class 'object'>,) <class 'type'> (<class 'object'>,) (<class 'type'>, (<class 'object'>,)) **************************************************************************************************** <class 'type'> (<class '__main__.ModelMeta'>,) F.Mro [<class '__main__.F'>, <class '__main__.ModelMeta'>, <class 'type'>, <class 'object'>] ________________________________________________________________________________________________________________________

如果这些输出结果都可以正确预测出来,就说明元编程理解了。

元编程的应用

因为框架使用到了架构设计,所以元类就有很多使用:

class ModelMeta(type): def __new__(cls, name, bases, attrs): #构造Studen得来这里。 print("Meta here once ~~~") return super().__new__(cls, name, bases,attrs) class Model(metaclass= ModelMeta): pass # Model的子类,Model的元类做了修改,这样只要构造这个类对象,都得去元类里面。 class Field: def __int__(self, fieldname=None, pk=False, nullable=False): self.fieldname = fieldname self.pk =pk self.nullable = nullable class Student(Model): id = Field(pk = True) name = Field(fieldname = "username", nullable = False) age = Field(nullable=True) def __repr__(self): return "<Field({} , {}, {} )".format( self.id,self.name,self.age ) __str__ = __repr__

用元类做CRM框架:

# Model的子类,Model的元类做了修改,这样只要构造这个类对象,都得去元类里面。 class Field: def __int__(self, fieldname=None, pk=False, nullable=False): self.fieldname = fieldname self.pk =pk self.nullable = nullable class ModelMeta(type): def __new__(cls, name, bases, attrs): #构造Studen得来这里。 print("Meta here once ~~~") return super().__new__(cls, name, bases,attrs) class Model(metaclass= ModelMeta): pass class Student(Model): id = Field(pk=True, null=False) name = Field('username', null=False) age = Field() # class Student(Model): # id = Field(pk = True) # name = Field(fieldname = "username", nullable = False) # age = Field(nullable=True) def __repr__(self): return "<Field({} , {}, {} )".format( self.id,self.name,self.age ) __str__ = __repr__

附:Django官方源码Model类的写法:

最新回复(0)