# 第21章 类元编程"""
类元编程指的是运行时创建或定制类的技艺1.类是一等对象,任何时候都可以使用函数新建类,而无需使用class关键字2.类装饰器也是函数,不过能够审查/修改/甚至把被装饰的类替换成其他类3.元类功能强大,但难以掌握,类装饰器能用更简单的方式解决很多问题
"""# 21.1 类工厂函数
# collections.namedtuple:
#     把一个类名和几个属性名传给这个函数,它会创建一个tuple的子类,
#     其中的元素通过名称获取,还为调式提供了友好的字符串表示形式(__repr__)
# 使用类似于collections.namedtuple工厂函数的方式,建立一个类工厂函数
# 21-2 record_factory.py:一个简单的类工厂函数# type:
"""
通常,我们把type()视为函数,调用type(my_object)获取对象所属的类
效果和my_object.__class__一样,
然而type是一个类,当作类使用时传入三个参数可以新建一个类:MyClass = type('MyClass',(MySuperClass,MyMixin),{'x':42,'x2':lambda self :self.x*2})
三个参数分别是 name/base/dictdict是映射,指定新类的属性和值上述代码等价于class MyClass(MySuperClass,MyMixin):x = 42def x2(slef):return self.x *2
type的实例是类,type是自身的实例
把三个参数传给type是动态创建类的常用方式
collctions.namedtuple使用的是另一种方式:先申明一个_class_template变量,其值是字符串形式的源码模版,然后namedtuple函数调用_class_template的format方法,填充模版里的空白最后使用内置的exec函数计算得到的源码字符串
"""# 21.2 定制描述符的类装饰器
"""
类装饰器和函数装饰非常类似,是参数为类对象的函数,返回原来的类或修改后的类"""#  示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem类
#  示例 21-4 model_v6.py :一个简单的类装饰器"""
类装饰器有个重大缺陷:只对直接依附的类有效,被装饰的类的子类可能继承也可能不会继承装饰器所做的改动具体情况视改动的方式而定
"""# 21.3导入时和运行时的比较
"""
导入时和运行时:python解释器会从上到下一次性解析完.py的源码,然后生成用于执行的字节码如果句法有错误,就在此时报告如果本地的__pycache__文件夹中有最新的.pyc文件,则跳过上述步骤,因为已经有运行所需的字节码了编译肯定是导入时的活动,不过那个时期还会做其他事情,因为python语句几乎都是可执行的,也就是说语句可能会运行用户代码,修改用户程序状态,尤其是import语句,它不只是申明在用户首次导入模块时,还会运行模块中的全部顶层代码--以后带入相同的模块则使用缓存,只做名称绑定.哪些顶层代码可以做任何事情,包括通常在运行时做的事情,比如连接数据库对于模块中的函数.运行顶层代码时,会编译函数的定义体,但并不会执行,仅当在运行时调用函数才执行对于类来说,情况就不同了在导入时,解释器会执行每个类的定义体,甚至会执行嵌套类的定义体结果是定义了类的属性和方法,并构建了类对象
"""# 理解计算时间的练习
# 示例21-6 evaltime.py 按顺序写出输出的序号标记<[N]># 21.4 元类的基础知识
"""
元类是制造类的工厂,不过不是函数而是类
根据Python对象模型,类是对象,因此类肯定是另外某个类的实例
默认情况下,python中的类是type类的实例,即
type类是大多数内置的类和用户自定义的类的元类object和type的关系:object是type的实例,type是object的子类所有的类都直接或者间接地是type的实例,不过只有元类同时也是type的子类
"""
print('spam'.__class__)  # <class 'str'>
print(str.__class__)  # <class 'type'>
print(type.__class__)  # <class 'type'> type是其自身的实例# 其他元类 abc.ABCMeta
import collectionsprint(collections.Iterable.__class__)  # <class 'abc.ABCMeta'>
import abcprint(abc.ABCMeta.__class__)  # <class 'type'>
print(abc.ABCMeta.__mro__)"""
元类可以通过实现__init__方法定制实例,元类的__init__方法可以做到
类装饰器能做的任何事情,但是作用更大"""# 理解元类计算时间的练习
# 示例21-10 evaltime_meta.py :ClassFive是MetaAleph元类的实例# 21.5 定制描述符的元类
# 示例 21-14 bulkfood_v7.py : 有元类支持,继承model.Entity类即可
# 示例 21-15 model_v7.py:EntityMeta元类以及它的一个实例# 21.6元类的特殊方法__prepare__
# 示例21-16 model_v8.py:这一版的EntityMeta用到了__prepare__方法,而且为Entity定义了field_names类方法"""
元类的作用:1.验证属性2.一次把装饰器依附到多个方法上3.序列化对象或者转换数据4.对象关系映射5.基于对象的持久存储6.动态转换使用其他语言编写的类结构
"""#21.7 类作为对象
"""
python数据模型为每个类定义了很多属性__mro____class____name__cls.__bases:由类的基类组成的元组cls.__qualname__:其值是类或者函数的限定名称,即从模块的全局作用域到类的点分路径cls.__subclasses__():这个方法返回一个列表,包含类的直接子类,这个方法的实现使用弱引用cls.mro():构建类时,如果需要获取存储在类属性__mro__中的超类元组,解释器会调用这个方法,定制要构建类的解析方法顺序"""
# 21-2 record_factory.py:一个简单的类工厂函数
# 21-2 record_factory.py:一个简单的类工厂函数
def record_factory(cls_name,field_names):try:field_names = field_names.replace(',',' ').split()  # 1except AttributeError:  # 不能调用replace或者split方法pass # 假定field_names本就是标识符组成的序列field_names = tuple(field_names)  # 2def __init__(self,*args,**kwargs):  # 3attrs = dict(zip(self.__slots__,args))attrs.update(kwargs)for name,value in attrs.items():setattr(self,name,value)def __iter__(self):  # 4for name in self.__slots__:yield getattr(self,name)def __repr__(self):  # 5values = ', '.join('{}={!r}'.format(*i)for i in zip(self.__slots__,self))return '{}({})'.format(self.__class__.__name__,values)cls_attrs = dict(__slots__ = field_names,  # 6__init__ = __init__,__iter__ = __iter__,__repr__ = __repr__)return type(cls_name,(object,),cls_attrs)  # 7"""
说明:1.这里体现了鸭子类型:尝试在逗号或者空格拆分field_names,如果失败,那么假定field_names本就是可迭代的对象,一个元素对应一个属性名2.使用属性名构建元组,浙江成为新建类的__slots__属性;此外这个么做,还设定了拆包的顺序和字符串表示形式中各字段的顺序.3.这个函数将成为新建类的__init__方法,参数有位置参数或关键字参数4.实现了__iter__方法,把类的实例变成可迭代的对象;按照__slots__的顺序产出字段值5.迭代__slots__和self,生成友好的字符串表示形式6.组建类属性字典7.调用type构造方法,构建新类,返回将其返回"""
if __name__ == '__main__':Dog = record_factory('Dog','name weight owner')rex = Dog('REX',30,'Bob','color'=='white')  #  可以这样写,但是不能把属性加入实例的属性# print(rex.color)  # 报错print(repr(rex))# print(rex.__slots__)# print(Dog.__dict__)name,weight,_ = rex  # 实例是可迭代的对象print(name,weight)print("{2}'s dog weight {1}kg".format(*rex))  # 也可以拆包rex.weight = 32  # 实例是可变的对象print(repr(rex))print(Dog.__mro__)  # 新类继承自object和工厂函数没有关系
#  示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem类
#  示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem类import model_v6 as model
@model.entity  #添加了类装饰器
class LineItem:weight = model.Quantity()price = model.Quantity()description = model.NonBlank()def __init__(self,description,weight,price):self.description = descriptionself.weight = weightself.price = pricedef subtotal(self):return self.weight * self.priceif __name__ == '__main__':raisins = LineItem('Golden raisins',10,6.95)print(dir(raisins))  # '_NonBlank#description', '_Quantity#price', '_Quantity#weight'print(LineItem.description.storage_name)  #_NonBlank#descriptionprint(raisins.description)print(getattr(raisins, '_NonBlank#description'))
#  示例 21-4 model_v6.py :一个简单的类装饰器
#  示例 21-4 model_v6.py :一个简单的类装饰器import abc
class AutoStorage:  #1 AutoStorage提供了之前Quantity的大部分功能__counter = 0def __init__(self):cls = self.__class__prefix = cls.__name__index = cls.__counterself.storage_name = '_{}#{}'.format(prefix,index)cls.__counter += 1def __get__(self,instance,ower):# ower是托管类的引用if instance is None:return self  # 如果不是通过实例调用,返回描述符自身else: # 否则像之前一样返回托管属性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..验证部分除外class Validated(abc.ABC,AutoStorage):  # 3 抽象类也继承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value)  # 4 把验证操作委托给validate方法super().__set__(instance,value)  # 5 然后把返回的值传给超类的__set__方法@abc.abstractmethoddef validate(selfs,instance,value):  # 6 validate是抽象方法'''return validated value or raise ValueError'''class Quantity(Validated):  # 7 Quantity和NonBlank都继承自Validated'''a number greater than zero'''def validate(selfs,instance,value):if value <= 0:raise ValueError('value must be > 0 ')return valueclass NonBlank(Validated):'''a string with at least one non-space character'''def validate(selfs,instance,value):value = value.strip()if len(value) == 0:raise ValueError('value cannot be empty or blank')return value  # 8 要求具体的方法返回验证后的值,借机可以清理\转换和规范化接收的数据
"""===============以上为modle_v5.py的代码=================="""def entity(cls):  # 1for key,attr in cls.__dict__.items(): # 2if  isinstance(attr,Validated):  # 3type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key)  # 4return cls  # 5"""
说明:1.装饰器的参数是一个类2.迭代存储类属性的字典3.如果属性是Validated描述符实例4.使用买描述符类的托管属性的名称命名storage_name(例如_NonBlank#description)5.返回修改后的类"""
# 示例21-6 evaltime.py 按顺序写出输出的序号标记<[N]>
from evalsupport import deco_alpha
print('<[1]> evaltime module start ')
class ClassOne:print('<[2]> CalssOne body ')def __init__(self):print('<[3]> CalssOne.__init__')def __del__(self):print('<[4]> CalssOne.__del__')def method_x(self):print('<[5]> CalssOne.method_x')class ClassTwo(object):print('<[6]> CalssTwo body ')@deco_alpha
class ClassThree():print('<[7]> ClassThree body ')def method_y(self):print('<[8]> CalssThree.method_y')class ClassFour(ClassThree):print('<[9]> ClassFour body ')def method_y(self):print('<[10]> CalssFour.method_y')if __name__ == '__main__':print('<[11]> ClassOne tests',30*".")one = ClassOne()one.method_x()print('<[12]> ClassThree tests', 30 * ".")three = ClassThree()three.method_y()print('<[13]> ClassFour tests', 30 * ".")four = ClassFour()four.method_y()print('<[14]> evaltime module end')
evalsupport.py
print('<[100]> evalsuport module start')
def deco_alpha(cls):print('<[200]> deco_alpha')def inner_1(self):print('<[300]> deco_alpha:inner_1')cls.method_y = inner_1return clsclass MetaAleph(type):print('<[400]> MetaAleph body')def __init__(cls,name,bases,dic):print('<[500]> MetaAleph.__init__')def inner_2(self):print('<[600]> MetaAleph.__init__:inner_2')cls.method_z = inner_2
print('<[700]> evalsuport module end')
# 示例21-10 evaltime_meta.py :ClassFive是MetaAleph元类的实例
from evalsupport import deco_alpha
from evalsupport import MetaAlephprint('<[1]> evaltime_meta module start ')
@deco_alpha
class ClassThree():print('<[2]> ClassThree body ')def method_y(self):print('<[3]> CalssThree.method_y')class ClassFour(ClassThree):print('<[4]> ClassFour body ')def method_y(self):print('<[5]> CalssFour.method_y')class ClassFive(metaclass=MetaAleph):print('<[6]> ClassFive body ')def method_y(self):print('<[7]> CalssFive.method_y')def method_z(self):print('<[8]> CalssFive.method_z')class ClassSix(ClassFive):print('<[9]> ClassSix body ')def method_z(self):print('<[10]> CalssSix.method_z')if __name__ == '__main__':print('<[11]> ClassThree tests',30*".")three = ClassThree()three.method_y()print('<[12]> ClassFour tests', 30 * ".")four = ClassFour()four.method_y()print('<[13]> ClassFive tests', 30 * ".")five = ClassFive()five.method_z()print('<[14]> ClassSix tests', 30 * ".")six = ClassSix()six.method_z()print('<[15]> evaltime_meta module end')
# 示例 21-14 bulkfood_v7.py : 有元类支持,继承model.Entity类即可
# 示例 21-14 bulkfood_v7.py : 有元类支持,继承model.Entity类即可# import model_v7 as model
import model_v8 as model
class LineItem(model.Entity):  # LineItem是model.Entity的子类weight = model.Quantity()price = model.Quantity()description = model.NonBlank()def __init__(self,description,weight,price):self.description = descriptionself.weight = weightself.price = pricedef subtotal(self):return self.weight * self.priceif __name__ == '__main__':raisins = LineItem('Golden raisins',10,6.95)print(dir(raisins))  # '_NonBlank#description', '_Quantity#price', '_Quantity#weight'print(LineItem.description.storage_name)  #_NonBlank#descriptionprint(raisins.description)print(getattr(raisins, '_NonBlank#description'))# model_v8的新功能for name in LineItem.field_names():print(name)
# 示例 21-15 model_v7.py:EntityMeta元类以及它的一个实例
# 示例 21-15 model_v7.py:EntityMeta元类以及它的一个实例import abc
class AutoStorage:  #1 AutoStorage提供了之前Quantity的大部分功能__counter = 0def __init__(self):cls = self.__class__prefix = cls.__name__index = cls.__counterself.storage_name = '_{}#{}'.format(prefix,index)cls.__counter += 1def __get__(self,instance,ower):# ower是托管类的引用if instance is None:return self  # 如果不是通过实例调用,返回描述符自身else: # 否则像之前一样返回托管属性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..验证部分除外class Validated(abc.ABC,AutoStorage):  # 3 抽象类也继承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value)  # 4 把验证操作委托给validate方法super().__set__(instance,value)  # 5 然后把返回的值传给超类的__set__方法@abc.abstractmethoddef validate(selfs,instance,value):  # 6 validate是抽象方法'''return validated value or raise ValueError'''class Quantity(Validated):  # 7 Quantity和NonBlank都继承自Validated'''a number greater than zero'''def validate(selfs,instance,value):if value <= 0:raise ValueError('value must be > 0 ')return valueclass NonBlank(Validated):'''a string with at least one non-space character'''def validate(selfs,instance,value):value = value.strip()if len(value) == 0:raise ValueError('value cannot be empty or blank')return value  # 8 要求具体的方法返回验证后的值,借机可以清理\转换和规范化接收的数据
"""===============以上为modle_v5.py的代码=================="""class EntityMeta(type):'''元类,用于创建带验证字段的业务实体'''def __init__(cls,name,bases,attr_dict):super().__init__(name,bases,attr_dict)  # 1 在超类type上调用__init__方法for key,attr in attr_dict.items():  # 2 与类装饰器的逻辑一样if isinstance(attr,Validated):type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key)
class Entity(metaclass=EntityMeta):  # 这个类的存在只是为了用起来便利,这个模块的用户直接继承Entity即可'''带有验证字段的实体业务'''
# 示例21-16 model_v8.py:这一版的EntityMeta用到了__prepare__方法,而且为Entity定义了field_names类方法
# 示例21-16 model_v8.py:这一版的EntityMeta用到了__prepare__方法,而且为Entity定义了field_names类方法
# 示例 21-15 model_v7.py:EntityMeta元类以及它的一个实例import abc
import collections
class AutoStorage:  #1 AutoStorage提供了之前Quantity的大部分功能__counter = 0def __init__(self):cls = self.__class__prefix = cls.__name__index = cls.__counterself.storage_name = '_{}#{}'.format(prefix,index)cls.__counter += 1def __get__(self,instance,ower):# ower是托管类的引用if instance is None:return self  # 如果不是通过实例调用,返回描述符自身else: # 否则像之前一样返回托管属性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..验证部分除外class Validated(abc.ABC,AutoStorage):  # 3 抽象类也继承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value)  # 4 把验证操作委托给validate方法super().__set__(instance,value)  # 5 然后把返回的值传给超类的__set__方法@abc.abstractmethoddef validate(selfs,instance,value):  # 6 validate是抽象方法'''return validated value or raise ValueError'''class Quantity(Validated):  # 7 Quantity和NonBlank都继承自Validated'''a number greater than zero'''def validate(selfs,instance,value):if value <= 0:raise ValueError('value must be > 0 ')return valueclass NonBlank(Validated):'''a string with at least one non-space character'''def validate(selfs,instance,value):value = value.strip()if len(value) == 0:raise ValueError('value cannot be empty or blank')return value  # 8 要求具体的方法返回验证后的值,借机可以清理\转换和规范化接收的数据
"""===============以上为modle_v5.py的代码=================="""class EntityMeta(type):'''元类,用于创建带验证字段的业务实体'''@classmethoddef __prepare__(cls, name, bases):return collections.OrderedDict()  # 返回一个空的OrderedDict实例,类属性将存储在里面def __init__(cls,name,bases,attr_dict):super().__init__(name,bases,attr_dict)cls._field_names = []  # 在要构建的类中创建一个_field_names属性for key,attr in attr_dict.items(): # 这里的 attr_dict是一个OrderedDict对象,会按顺序迭代if isinstance(attr,Validated):type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key)cls._field_names.append(key)  # 把找到的各个Validated字段添加到_field_names中
class Entity(metaclass=EntityMeta):  # 这个类的存在只是为了用起来便利,这个模块的用户直接继承Entity即可'''带有验证字段的实体业务'''@classmethoddef field_names(cls):for name in cls._field_names:yield name  # 按照添加字段的顺序产出字段的名称

35岁学python,也不知道为了啥?

读书笔记:《流畅的Python》第21章 类元编程相关推荐

  1. 读书笔记-流畅的python(1-6章)

    前言:这正是本书的主要目的:着重讲解这门语言的基本惯用法,让你的代码简洁.高效且可读,把你打造成熟练的 Python 程序员. 自己总结学习作为输出,很多为了节省时间只是复制粘贴,不具有广泛意义 第一 ...

  2. 【读书笔记】.NET本质论第四章-Programming with Type(Part Two)

    欢迎阅读本系列其他文章: [读书笔记].NET本质论第一章 The CLR as a Better COM [读书笔记].NET本质论第二章-Components(Part One) [读书笔记].N ...

  3. python归一化 增大差异_简学Python第六章__class面向对象编程与异常处理

    Python第六章__class面向对象编程与异常处理 欢迎加入Linux_Python学习群 群号:478616847 目录: 面向对象的程序设计 类和对象 封装 继承与派生 多态与多态性 特性pr ...

  4. Python第9章 类

    Python第9章 类 根据类创建实例 class Dog(): """一次模拟小狗的简单完成""" def init(self,name, ...

  5. 闭关之 C++ 函数式编程笔记(四):monad 和 模板元编程

    目录 第十章 monad 注意 10.1 仿函数并不是以前的仿函数 10.1.1 处理可选值 10.2 monad: 更强大的仿函数 10.3 基本的例子 10.4 range 与 monad 的嵌套 ...

  6. 《Real-Time Rendering 4th Edition》读书笔记--简单粗糙翻译 第七章 阴影 Shadows

    写在前面的话:因为英语不好,所以看得慢,所以还不如索性按自己的理解简单粗糙翻译一遍,就当是自己的读书笔记了.不对之处甚多,以后理解深刻了,英语好了再回来修改.相信花在本书上的时间和精力是值得的. -- ...

  7. 《Real-Time Rendering 4th Edition》读书笔记--简单粗糙翻译 第五章 着色基础 Shading Basics

    写在前面的话:因为英语不好,所以看得慢,所以还不如索性按自己的理解简单粗糙翻译一遍,就当是自己的读书笔记了.不对之处甚多,以后理解深刻了,英语好了再回来修改.相信花在本书上的时间和精力是值得的. -- ...

  8. 【读书笔记】用Python获取A股行情数据的4种方法

    本人大三在校小学渣一枚,非金融专业,也从来没有过股票期货等金融产品的投资经验,但最近收到了清华出版社赠送的<深入浅出Python量化交易实战>一书,因为平时对数据科学和机器学习都比较感兴趣 ...

  9. 《Real-Time Rendering 4th Edition》读书笔记--简单粗糙翻译 第六章 纹理 Texturing

    写在前面的话:因为英语不好,所以看得慢,所以还不如索性按自己的理解简单粗糙翻译一遍,就当是自己的读书笔记了.不对之处甚多,以后理解深刻了,英语好了再回来修改.相信花在本书上的时间和精力是值得的. -- ...

最新文章

  1. .net连接mysql数据_.net连接MYSQL数据库的方法及示例!
  2. 开源 免费 java CMS - FreeCMS1.5-建站向导
  3. VC++ 使用TeeChart图表控件(1)
  4. Ubuntu14.04 YouCompleteMe Configure
  5. MySQL分组查询的介绍
  6. turtle库基础练习
  7. 前端学习(2601):什么是跨域请求
  8. c++ 舞伴配对问题_R绘图:配对样本差异表达作图ggpubr
  9. C#LeetCode刷题之#141-环形链表(Linked List Cycle)
  10. 面向对象设计原则之7-迪米特法则
  11. AntiModerate – 渐进式图片加载的 JavaScript 库
  12. No matching distribution found for docx(配置cmd控制台代理)
  13. 【预测模型】Gompertz 曲线方程预测
  14. 技术状态管理(四)-技术状态控制
  15. 微信小程序401unauthorized授权问题解决方法
  16. 【26】Superscalar和VLIW:如何让CPU的吞吐率超过1?
  17. 瑞幸咖啡2022,摆脱困境,迎来坦途
  18. css 标记选择器,CSS标记选择器
  19. pda通用扫描app_智能仓储盘点——PDA扫码盘点APP真正实现“轻松盘点”!
  20. 自然语言处理NLP中文分词,词性标注,关键词提取和文本摘要

热门文章

  1. win7注册表关闭防火墙服务器,怎么样修改注册表来关闭windows防火墙?
  2. ggplot2的自定义调色板
  3. xml元素 标签 属性
  4. iphone邮箱看不到已发送_不看不知道 教你如何设置iPhone邮箱
  5. 上岸美团,我为何放弃算法转开发
  6. ZIP压缩算法详细分析及解压实例
  7. idea中tomcat项目修改Module名之后修改配置信息
  8. 【学习分享】创龙TI KeyStone C66x开发例程使用手册
  9. Unity DOTS学习导航
  10. 用爬虫批量采集阿里巴巴1688商品数据