python之元编程
一、什么是元编程
元编程是一种编写计算机程序的技术,这些程序可以将自己看作数据,因此你可以在运行时对它进行内省、生成和/或修改。
Python在语言层面对函数、类等基本类型提供了内省及实时创建和修改的能力;我们可以使用装饰器向现有的函数、方法或类添加附加功能;同时我们也可以通过修改一些特殊的方法来变更类的行为;
二、使用的例子
- 面对一个复杂多变的json数据结构,虽然Python提供了处理JSon数据的API,但是返回的类型是dict,使用非常不方便不友好;接下来通过Python提供的元编程的能力,来实现一个类似对象层次结构的访问方式;
import json
str = r'''{
"name":"mango",
"age": 30,
"address":{
"city":"beijing"
},
"schools":["xiaoxue","zhongxue"],
"sons":[
{
"name":"qing"
}
]
}'''
obj = json.loads(str)
print(type(obj))
print(obj.get('name'))
# <class 'dict'>
# mango
- 面向对象变成提倡封装数字字段,通过访问控制器来控制数据字段的校验;接下来通过python提供的元编程能力进行实现;
三、通过__getattr__响应动态字段的获取
__getattr__是一个实例方法,适用于访问未定义的属性的时候调用,即该属性在实例中以及对应的类的基类以及祖先类中都不存在的时候调用;
获取字段值的时候,我们先检测对应的字段是否存在,如果不存在则抛出异常;如果字段存在,则检测字段类型并决定是否对嵌套结构进行处理;
import json
from collections import abc
def loadJsonStr():
str = r'''{
"name":"mango",
"age": 30,
"address":{
"city":"beijing"
},
"schools":["xiaoxue","zhongxue"],
"sons":[
{
"name":"qing"
}
]
}'''
result = json.loads(str)
return result;
class JsonObject:
def __init__(self, jsondict):
self._data = dict(jsondict)
def __getattr__(self, name):
if name in self._data:
val = self._data.get(name)
if isinstance(val, abc.Mapping) or isinstance(val, abc.MutableSequence):
return self.initinner(val)
else:
return val
else:
raise AttributeError(f"{name} field does not exist")
def initinner(self, obj):
if isinstance(obj, abc.Mapping):
return self.__class__(obj)
elif isinstance(obj,abc.MutableSequence):
return [self.initinner(item) for item in obj]
else:
return obj
jobj = JsonObject(loadJsonStr())
print(jobj.name)
print(jobj.address)
print(jobj.address.city)
print(jobj.schools)
print(jobj.sons[0].name)
print(jobj.noField)
# mango
# <__main__.JsonObject object at 0x7ff7eac1cee0>
# beijing
# ['xiaoxue', 'zhongxue']
# qing
# AttributeError: noField field does not exist
五、使用__new__动态创建对象
我们通常把__init__称为构造方法,但是其实用于构建实例的是__new__:这是一个必须返回一个实例的类方法。返回的实例会作为第一个参数(即self)传给__init__方法。因为调用__init__方法时要传入实例,而且禁止返回任何值,所以__init__方法其实是“初始化方法”。真正的构造方法是__new__。我们可以在构造函数中国完成对JSon字段值的解析处理;
import json
from collections import abc
def loadJsonStr():
str = r'''{
"name":"mango",
"age": 30,
"address":{
"city":"beijing"
},
"schools":["xiaoxue","zhongxue"],
"sons":[
{
"name":"qing"
}
]
}'''
result = json.loads(str)
return result;
class JsonObject:
def __new__(cls, args, **kwargs):
obj = args
if isinstance(obj, abc.Mapping):
return super().__new__(cls)
elif isinstance(obj,abc.MutableSequence):
return [cls(item) for item in obj]
else:
return obj
def __init__(self, jsondict):
self._data = dict(jsondict)
def __getattr__(self, name):
if name in self._data:
val = self._data.get(name)
if isinstance(val, abc.Mapping) or isinstance(val, abc.MutableSequence):
return self.__class__(val)
else:
return val
else:
raise AttributeError(f"{name} field does not exist")
jobj = JsonObject(loadJsonStr())
print(jobj.name)
print(jobj.address)
print(jobj.address.city)
print(jobj.schools)
print(jobj.sons[0].name)
print(jobj.noField)
# mango
# <__main__.JsonObject object at 0x7ff7eac1cee0>
# beijing
# ['xiaoxue', 'zhongxue']
# qing
# AttributeError: noField field does not exist
六、使用property装饰器添加校验逻辑
我们可以利用Python提供的property属性,为数据字段添加校验逻辑,从而可以避免调用方的变更;虽然property装饰器定义在类上,但是编译器会首先在类上查找,如果找不到才会从类的实例上查找;
import sys
class OrderItem:
def __init__(self, desc, count, price):
self.desc = desc
self.count = count
self.price = price
def subtotal(self):
return self.count * self.price
@property
def price(self):
print(f'{sys._getframe().f_code.co_name} getter')
return self._price
@price.setter
def price(self, val):
print(f'{sys._getframe().f_code.co_name} setter')
if val > 0:
self._price = val
else:
raise ValueError('price must be > 0')
@property
def count(self):
print(f'{sys._getframe().f_code.co_name} getter')
return self._count
@count.setter
def count(self, val):
print(f'{sys._getframe().f_code.co_name} setter')
if val > 0:
self._count = val
else:
raise ValueError('count must be > 0')
pbook = OrderItem('python books', 1, 50)
print(pbook.subtotal())
print(OrderItem.price)
print(OrderItem.price.setter)
print(OrderItem.price.getter)
print(vars(pbook))
jbook = OrderItem('java books', 0, 50)
print(jbook.subtotal())
# count setter
# price setter
# count getter
# price getter
# 50
# <property object at 0x7ffa8ddf8a90>
# <built-in method setter of property object at 0x7ffa8ddf8a90>
# <built-in method getter of property object at 0x7ffa8ddf8a90>
# {'desc': 'python books', '_count': 1, '_price': 50}
# count setter
# ValueError: count must be > 0
可以将创建字段的逻辑抽取出来作为公用的方法
import sys
def buildVolidateField(name):
_name = f'_{name}'
def getter(obj):
return obj.__dict__.get(_name)
def setter(obj, value):
if value > 0:
obj.__dict__[_name]= value
else:
raise ValueError(f'{name} must be > 0')
return property(getter, setter)
class OrderItem:
price = buildVolidateField('price')
count = buildVolidateField('count')
def __init__(self, desc, count, price):
self.desc = desc
self.count = count
self.price = price
def subtotal(self):
return self.count * self.price
pbook = OrderItem('python books', 1, 50)
print(pbook.subtotal())
print(OrderItem.price)
print(OrderItem.price.setter)
print(OrderItem.price.getter)
print(vars(pbook))
jbook = OrderItem('java books', 0, 50)
print(jbook.subtotal())
# 50
# <property object at 0x7fbc90cfdd60>
# <built-in method setter of property object at 0x7fbc90cfdd60>
# <built-in method getter of property object at 0x7fbc90cfdd60>
# {'desc': 'python books', '_count': 1, '_price': 50}
# ValueError: count must be > 0
七、使用描述符类实现字段校验逻辑
描述符是实现了特定协议的类,这个协议包括__get__、__set__和__delete__方法。property类实现了完整的描述符协议。通常,可以只实现部分协议。其实,我们在真实的代码中见到的大多数描述符只实现了__get__和__set__方法,还有很多只实现了其中的一个。
import sys
class VolidateDescr:
def __init__(self, name):
self.name = f'_{name}'
def __set__(self, instance, value):
if value > 0:
instance.__dict__[self.name] = value
else:
raise ValueError(f'{self.name} must be > 0')
def __get__(self, instance, default):
if instance is None:
return self;
else:
return instance.__dict__[self.name]
class OrderItem:
price = VolidateDescr('price')
count = VolidateDescr('count')
def __init__(self, desc, count, price):
self.desc = desc
self.count = count
self.price = price
def subtotal(self):
return self.count * self.price
pbook = OrderItem('python books', 1, 50)
print(pbook.subtotal())
print(OrderItem.price)
print(OrderItem.price.__set__)
print(OrderItem.price.__get__)
print(vars(pbook))
jbook = OrderItem('java books', 0, 50)
print(jbook.subtotal())
# 50
# <__main__.VolidateDescr object at 0x7f162d0ac9a0>
# <bound method VolidateDescr.__set__ of <__main__.VolidateDescr object at 0x7f162d0ac9a0>>
# <bound method VolidateDescr.__get__ of <__main__.VolidateDescr object at 0x7f162d0ac9a0>>
# {'desc': 'python books', '_count': 1, '_price': 50}
# ValueError: _count must be > 0
目前只是两个数字字段添加了校验,接下来为desc字符串字段添加非空校验;两种数据类型字段只有校验的差异,我们将校验逻辑跟字段的访问控制进行抽离,分别实现两个具体的校验类;
import abc
class FieldDescr:
_countor = 0
def __init__(self):
self.name = f'_{self.__class__.__name__}_{self.__class__._countor}'
self.__class__._countor += 1
def __set__(self, instance, value):
setattr(instance, self.name, value)
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(instance, self.name)
class Validated(FieldDescr):
def __set__(self, instance, value):
value = self.validate(instance, value)
super().__set__(instance, value)
@abc.abstractmethod
def validate(self, instance, value):
'''this is abstract method'''
class GreatZeroIntField(Validated):
def validate(self, instance, value):
if value <= 0:
raise ValueError(f'{self.name} value must be > 0')
return value
class NoEmptyStrField(Validated):
def validate(self, instance, value):
value = value.strip()
if len(value) == 0:
raise ValueError('value cant not be empty or blank')
return value
class OrderItem:
descr = NoEmptyStrField()
price = GreatZeroIntField()
count = GreatZeroIntField()
def __init__(self, descr, price, count):
self.descr = descr
self.price = price
self.count = count
def subtotal(self):
return self.count * self.price
pbook = OrderItem('python books', 1, 50)
print(pbook.subtotal())
print(OrderItem.price)
print(OrderItem.price.__set__)
print(OrderItem.price.__get__)
print(vars(pbook))
jbook = OrderItem('java books', 0, 50)
print(jbook.subtotal())
# 50
# <__main__.GreatZeroIntField object at 0x7fa2eb37fd00>
# <bound method Validated.__set__ of <__main__.GreatZeroIntField object at 0x7fa2eb37fd00>>
# <bound method FieldDescr.__get__ of <__main__.GreatZeroIntField object at 0x7fa2eb37fd00>>
# {'_NoEmptyStrField_0': 'python books', '_GreatZeroIntField_0': 1, '_GreatZeroIntField_1': 50}
# ValueError: _GreatZeroIntField_0 value must be > 0
- 定制数据字段的名字
到现在我们已经封装自动生成特性,自动生成的数据字段的名字并不能很好的跟类上对应的特性名称对应上;接下来通过类装饰器和元类来定制数据字段的名字;
类装饰器在编译器编译完类之后执行,这个时候类上的特性已经生成完毕,我们可以遍历类的__dict__,找到对应的特性并修改其name字段的值即可;
import abc
def renamePrivateField(cls):
for key,value in cls.__dict__.items():
if isinstance(value, Validated):
value.name = f'_{value.__class__.__name__}_{key}'
return cls
class FieldDescr:
_countor = 0
def __init__(self):
self.name = f'_{self.__class__.__name__}_{self.__class__._countor}'
self.__class__._countor += 1
def __set__(self, instance, value):
setattr(instance, self.name, value)
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(instance, self.name)
class Validated(FieldDescr):
def __set__(self, instance, value):
value = self.validate(instance, value)
super().__set__(instance, value)
@abc.abstractmethod
def validate(self, instance, value):
'''this is abstract method'''
class GreatZeroIntField(Validated):
def validate(self, instance, value):
if value <= 0:
raise ValueError(f'{self.name} value must be > 0')
return value
class NoEmptyStrField(Validated):
def validate(self, instance, value):
value = value.strip()
if len(value) == 0:
raise ValueError('value cant not be empty or blank')
return value
@renamePrivateField
class OrderItem:
descr = NoEmptyStrField()
price = GreatZeroIntField()
count = GreatZeroIntField()
def __init__(self, descr, price, count):
self.descr = descr
self.price = price
self.count = count
def subtotal(self):
return self.count * self.price
pbook = OrderItem('python books', 1, 50)
print(pbook.subtotal())
print(OrderItem.price)
print(OrderItem.price.name)
print(OrderItem.price.__set__)
print(OrderItem.price.__get__)
print(vars(pbook))
# 50
# <__main__.GreatZeroIntField object at 0x7f23e67bf2b0>
# _GreatZeroIntField_price
# <bound method Validated.__set__ of <__main__.GreatZeroIntField object at 0x7f23e67bf2b0>>
# <bound method FieldDescr.__get__ of <__main__.GreatZeroIntField object at 0x7f23e67bf2b0>>
# {'_NoEmptyStrField_descr': 'python books', '_GreatZeroIntField_price': 1, '_GreatZeroIntField_count': 50}
由于类装饰器在类编译完整之后直接执行,可能会出现被子类覆盖的情况,元类可以很好的解决这个问题
import abc
class FieldDescr:
_countor = 0
def __init__(self):
self.name = f'_{self.__class__.__name__}_{self.__class__._countor}'
self.__class__._countor += 1
def __set__(self, instance, value):
setattr(instance, self.name, value)
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(instance, self.name)
class Validated(FieldDescr):
def __set__(self, instance, value):
value = self.validate(instance, value)
super().__set__(instance, value)
@abc.abstractmethod
def validate(self, instance, value):
'''this is abstract method'''
class GreatZeroIntField(Validated):
def validate(self, instance, value):
if value <= 0:
raise ValueError(f'{self.name} value must be > 0')
return value
class NoEmptyStrField(Validated):
def validate(self, instance, value):
value = value.strip()
if len(value) == 0:
raise ValueError('value cant not be empty or blank')
return value
class renamePrivateFieldMeta(type):
def __init__(cls, name, bases, attr_dict):
super().__init__(name, bases, attr_dict)
for key, value in cls.__dict__.items():
if isinstance(value, Validated):
value.name = f'_{value.__class__.__name__}_{key}'
class OrderEntity(metaclass=renamePrivateFieldMeta):
'''rename entity'''
class OrderItem(OrderEntity):
descr = NoEmptyStrField()
price = GreatZeroIntField()
count = GreatZeroIntField()
def __init__(self, descr, price, count):
self.descr = descr
self.price = price
self.count = count
def subtotal(self):
return self.count * self.price
pbook = OrderItem('python books', 1, 50)
print(pbook.subtotal())
print(OrderItem.price)
print(OrderItem.price.name)
print(OrderItem.price.__set__)
print(OrderItem.price.__get__)
print(vars(pbook))
# 50
# <__main__.GreatZeroIntField object at 0x7f393be8c070>
# _GreatZeroIntField_price
# <bound method Validated.__set__ of <__main__.GreatZeroIntField object at 0x7f393be8c070>>
# <bound method FieldDescr.__get__ of <__main__.GreatZeroIntField object at 0x7f393be8c070>>
# {'_NoEmptyStrField_descr': 'python books', '_GreatZeroIntField_price': 1, '_GreatZeroIntField_count': 50}
python之元编程的更多相关文章
- Python的元编程案例
Python的元编程案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是元编程 元编程概念来自LISP和smalltalk. 我们写程序是直接写代码,是否能够用代码来生成 ...
- Python类元编程
类元编程是指在运行时创建或定制类.在Python中,类是一等对象,因此任何时候都可以使用函数创建新类,而无需用class关键字.类装饰器也是函数,不过能够审查.修改,甚至把被装饰的类替换成其他类.元类 ...
- python之元编程(元类实例)
本实例是元类实例,功能是记录该的子类的类名,并以树状结构展示子类的类名. RegisterClasses继承自type,提供的功能是在__init__接口,为类创建了childrens的集合,并类名保 ...
- Python类元编程初探
在<流畅的Python>一书中提到: Classes are first-class object in Python, so a function can be used to crea ...
- Python元编程
简单定义"元编程是一种编写计算机程序的技术,这些程序可以将自己看做数据,因此你可以在运行时对它进行内审.生成和/或修改",本博参考<<Python高级编程>> ...
- python元编程(metaclass)
Python元编程就是使用metaclass技术进行编程,99%的情况下不会使用,了解即可. Python中的类和对象 对于学习Python和使用Python的同学,你是否好奇过Python中的对象究 ...
- Python 元编程 - 装饰器
Python 中提供了一个叫装饰器的特性,用于在不改变原始对象的情况下,增加新功能或行为. 这也属于 Python "元编程" 的一部分,在编译时一个对象去试图修改另一个对象的信息 ...
- PYTHON黑帽编程 4.1 SNIFFER(嗅探器)之数据捕获(下)
上一节(<4.1 SNIFFER(嗅探器)之数据捕获(上)>)中, 我们讲解了通过Raw Socket的方式来编写Sniffer的基本方法. 本节我们继续来编写Sniffer,只不过使用现 ...
- Java元编程及其应用
首先,我们且不说元编程是什么,他能做什么.我们先来谈谈生产力. 同样是实现一个投票系统,一个是python程序员,基于django-framework,用了半小时就搭建了一个完整系统,另外一个是标准的 ...
随机推荐
- Lynis 漏洞扫描工具部署及效果展示
Lynis 漏洞扫描工具部署及效果展示 介绍 Lynis是一个安全审计工具,它可以在Linux,macOS和其他基于Unix的系统上运行.Lynis的主要重点是执行系统的运行状况检查,它还有助于检测漏 ...
- 一次简单的SQL注入绕WAF
本人也是小白一枚,大佬请绕过,这个其实是六月份的时候做的,那时候想多点实战经验,就直接用谷歌搜索找了一些网站,这个是其中一个 1.目标网站 2.发现有WAF防护 3.判断存在注入 4.猜测了一下闭合为 ...
- pycharm环境下配置scrap爬虫环境
[写在开头] 参考文章后面给出了备注信息,是在解决这个问题的时候,查找的比较有亮点的参考文章,如果本文章写的不太清楚的,可以去原文章进行查看.下面列举的四个文章有参考的成分也有验证的成分,解决办法重点 ...
- 【数据结构】c语言实现集合的交并差运算
待改写:存储数据类型int-->char 重复的元素可存储 功能上不完善 #include <stdio.h> #include <stdlib.h> typedef s ...
- javascript运算符和表达式
1.表达式的概念 由运算符连接操作组成的式子,不管式子有多长,最终都是一个值. 2.算术运算符 加+ 减- 乘* 除/ 取模% 负数- 自增++ 自减-- 3.比较运算符 等于== 严格等于=== ...
- 81. 搜索旋转排序数组 II
题目 已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同. 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋 ...
- Go语言核心36讲(Go语言进阶技术七)--学习笔记
13 | 结构体及其方法的使用法门 我们都知道,结构体类型表示的是实实在在的数据结构.一个结构体类型可以包含若干个字段,每个字段通常都需要有确切的名字和类型. 前导内容:结构体类型基础知识 当然了,结 ...
- [no code][scrum meeting] Beta 9
$( "#cnblogs_post_body" ).catalog() 例会时间:5月23日15:30,主持者:肖思炀 下次例会时间:5月25日11:30,主持者:伦泽标 一.工作 ...
- Noip模拟71 2021.10.7
T1 签到题 结论题,找到规律就会做 规律是每个点的度数$\mod$颜色种数,如果不是$0$则贡献一个答案 1 #include<bits/stdc++.h> 2 #define int ...
- LP-DDR 和其他 DDR
一篇技術文檔比較 LP-DDR 和其他 DDR. 就觀念來說,LP-DDR 就是 Low Power 的 DDR:但就架構來說,LP-DDR 和其他 DDR 是截然不同的東西. 他們分屬不同的 JDE ...