【Python基础知识库】Python中元编程
Python的高级技巧:深入探索元类与元编程
深入解析Python的元编程实践
超编程(SuperProgramming)有时也被称作超重技术。
它是指一类特殊的程序设计方法。
这些方法通常会将其他程序(包括自身)作为输入数据进行处理。
相较于手动编译完整代码的工作流程,
使用这种技术可以使程序员获得更高的效率,
或者提供更大的灵活性以应对新的情况而无需重新编译。
称作元语言的语言用于编写元程序;而被操作的对象程序所使用的语言则被称为"目标语言"。一种编程语言若能同时具备作为自身元语言的能力,则这种特性被称为"反射"或"自反"。例如,在Python作为一种胶水编程语言的情况下,在支持多种库(如ctypes和js2py等)方面展示了卓越的元编程能力。
元编程是一种可将程序视为数据进行操作的技术手段,在运行环境中具备解析、构建、分析和重构成其他程序代码的能力,并能在执行过程中动态调整自身的行为模式以适应不同的需求设定。
其主要目的是为系统增加另一个抽象层次以提高其功能模块间的交互效率和系统的整体性能水平。
在python中,元编程实现通常的手段有:
- 预定义方法
class A(object):
def __init__(self, o):
self.__obj__ = o
def __getattr__(self, name):
if hasattr(self.__obj__, name):
return getattr(self.__obj__, name)
return self.__dict__[name]
def __iter__(self):
return self.__obj__.iter__()
l = []
a = A(l)
for i in range(101):
a.append(i)
print(sum(a))
# 结果
'''
10
10
'''
该agent类结构较为简单,在实现核心功能时主要依赖于基本的数据操作与逻辑控制流程。其中涉及的核心组件之一是云定义函数部分。值得注意的是,在完成这两个特殊方法(即实现属性获取与设置)时,请确保不依赖于Python对象系统的默认属性获取机制。具体来说,在这两个方法体内可以访问实例自身所具有的字典属性(通过其自身的字典属性进行操作),而如果试图在自定义的属性获取逻辑中使用self.dict
会导致递归调用问题(在自定义的属性获取逻辑中使用self.dict
会导致递归调用问题)。因此建议采用object.getattribute方法来避免这种循环引用问题(其中的方法仅适用于基于新式派生类的对象)。
- 函数赋值
# socket.py中的例子
_delegate_methods = ("recv", "recvfrom", "recv_into", "recvfrom_into", "send", "sendto")
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
if _sock is None:
_sock = _realsocket(family, type, proto)
self._sock = _sock
for method in _delegate_method:
setattr(self, method, getattr(_sock, method))
# http代理装饰器
def http_proxy(proxyaddr, username=None, password=None):
def reciver(func):
def creator(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
sock = func(family, type, proto)
sock.connect(proxyaddr)
def newconn(addr):
http_connect(sock, addr, username, password)
sock.connect, sock.connect_ex = nerconn, newconn
return sock
return creator
return reciver
- 描述器
我们称具备__get__和__set__$...$方法的对象为描述器。当访问对象的属性时(此处指该对象所具有的属性),如果这些属性本身也是描述器,则会调用该描述器的\texttt{get}``\texttt{set}`方法来获取或设置值(此处指被访问的那个属性)。具有\texttt{set}方法的描述器被称为数据描述器(data descriptor),而仅具有\texttt{get}方法而不具备\texttt{set}方法的则被称为非数据描述器(non data descriptor)。
Python访问某个对象的某个属性时,是按照以下次序的:
该数据描述器类
在instance属性中,并非所有实例属性都会调用__get__方法
类中的属性类型也涵盖non data descriptors
class Meta(type):
def __new__(cls, name, bases, attrs):
for k, v in attrs.items():
if hasattr(v, '__meta_init__'):
v.__meta_init__(k)
return type.__new__(cls, name, bases, attrs)
class AttrBase(object):
def __meta_init__(self, k):
self.name = k
def __get__(self, obj, cls):
return obj[self.name]
def __set__(self, obj, value):
obj[self.name] = value
class Base(dict, metaclass = Meta):
pass
class User(dict):
name = AttrBase()
b = User()
b.name = 'shell'
print(b)
print(b.name)
# 结果
'''
{'name': 'shell'}
shell
'''
在获取对象属性时,在访问b.name的过程中实际上是对应地被获取为b['name']。而非通过重载User类的__getattr__方法来实现,默认情况下是通过descriptor机制。
- 元类
具体可以参考之前python中的元类metaclass
- eval函数
该函数用于评估字符串表达式,并返回计算结果。
eval(expression[, globals[, locals]])表示:
- expression:表示待评估的字符串表达式;
- globals:表示全局变量作用域(若提供,则需为字典对象);
- locals:表示局部变量作用域(若提供,则可为任意映射对象)。
s1 ='2 * 6 / 2 + 2'
s2 = 'pow(2, 3)'
s3 = '2 ** 3'
print(eval(s1))
print(eval(s2))
print(eval(s3))
# 结果
'''
8.0
8
8
经典案例:借助元类在Python语言与数据库之间构建一个抽象ORM层 (可参考元类内部提供的创建ORM实例的方法)
元编程常见的应用场景
- 增强语法重构能力
- 构建Domain-Specific Language(DSL)工具
- 自动生成高质量代码框架
- 在不同应用场景下智能地选择并优化代码结构
- 有效解决一些相互独立且复杂的架构设计难题
- 支持面向切面编程(AOP)的技术实现
