导语:本文章记录了本人在学习Python基础之元编程篇的重点知识及我的心得,打算入门Python的朋友们能够来一块儿学习并交流。
一、了解运行时建立类的方法——类工厂函数;
二、熟悉元类的基础知识和使用场景;
三、了解元类的__prepare__的意义;
四、了解class的属性以及Python解释器如何处理导入的模块。
类元编程是指在运行时建立或定制类的技艺。
python
类是一等对象,所以任什么时候候均可以使用函数新建类,而无需使用class关键字。
编程
实例: 下面咱们写一个简单的类工厂函数:缓存
def record_factory(cls_name,field_names): try: field_names=field_names.replace(',',' ').split() except AttributeError: pass field_names=tuple(field_names)#使用属性名构建元组,这将成为新的__slots__。 def __init__(self,*args,**kwards):#经过字典解析输入数据,而后将值赋给新建的类的属性。 attrs=dict(zip(self.__slots__,args)) attrs.update(kwards) for key,value in attrs.items(): setattr(self,key,value) def __iter__(self):#迭代特殊方法的思想在于让解释器查询到属性对应的值。 for name in self.__slots__: yield getattr(self,name)#实现迭代的方法在于:经过内置getattr方法获取已经初始化的类属性对应的值。 def __repr__(self):#返回一个合适的字符串。 msg=', '.join('{}={!r}'.format(*i) for i in zip(self.__slots__,self)) return '{}({})'.format(self.__class__.__name__,msg) cls_attrs=dict(__slots__=field_names, __init__=__init__, __iter__=__iter__, __repr__=__repr__) return type(cls_name,(object,),cls_attrs)#使用type构造方法构建新类并返回,注意object后的逗号很重要。 animal=record_factory('animal','name,age,price') dog=animal('dog','Bob','1000') print(dog)#构造animal实例对象 a,b,_=dog#能够实现预期的拆包赋值 print(a,b)
输出:安全
animal(name='dog', age='Bob', price='1000') dog Bob
type的三个参数:class type(name, bases, dict)
type最后一个参数是一个映射,指定新类的属性名和值。app
注意:在Python中作元编程时, 最好不要用 exec 和 eval 函数.。若是接接收的字符串来自不可信的源,这两个函数会带来严重的安全风险.框架
类装饰器:本质上是高阶函数,其参数是被装饰的类,用于审查审查、修改、甚至把被装饰的类替换成其余类。函数
类装饰器的重大缺点:只对直接依附的类有效。即被装饰的类的子类可能继承也可能不继承装饰器所作的改动,具体状况视改动的方式而定。工具
元类:是类元编程最高级的工具:使用元类能够建立具备某种特质的全新变种,例如抽象基类。
功能:元类是建立类的类。
特色:
学习
全部类都直接或间接地是type的实例,不过只有元类同时也是type的子类。
具体而言,元类能够经过实现__init__方法定制类。
为了不无限回溯,type.__class是type,即type是自身的实例。
建议:除非开发框架,不然不要在生产代码中定义元类或抽象基类。
code
实例:使用元类定制描述符
class EntityMeta(type): """Metaclass for business entities with validated fields""" def __init__(cls, name, bases, attr_dict): super().__init__(name, bases, attr_dict) # 建立类对象。 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): # <3> """Business entity with validated fields"""
__prepare__的功能:知道类的属性定义的顺序。
使用场景
:__prepare__只在元类中有用,并且必须声明为类方法(即要使用@classmethod 装饰器定义)。参数要求
:__prepare__方法的第一个参数是元类,随后两个参数分别是要构建的类的名称和基类组成的元组, 返回值必须是映射。工做原理
:元类构建新类时,解释器会先调用__prepare__ 方法,使用类定义体中的属性建立映射。接着把__prepare__方法返回的映射会传给__new__ 方法的最后一个参数,而后再传给__init__ 方法。
实例:使用__prepare__
class EntityMeta(type): """Metaclass for business entities with validated fields""" @classmethod def __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(): if isinstance(attr, Validated): type_name = type(attr).__name__ attr.storage_name = '_{}#{}'.format(type_name, key) cls._field_names.append(key) class Entity(metaclass=EntityMeta): """Business entity with validated fields""" @classmethod def field_names(cls): # field_names 类方法的做用简单:按照添加字段的顺序产出字段的名称 for name in cls._field_names: yield name
在进程中首次导入模块时,Python解释器还会运行所导入模块中的所有顶层代码。之后导入相同的模块则使用缓存,只作名称绑定。
对函数而言,首次导入模块时:解释器会执行顶层的 def 语句,编译函数的定义体,把函数对象绑定到对应的全局名称上,可是显然解释器不会执行函数的定义体。
一般这意味着解释器在导入时定义顶层函数,可是仅当在运行时调用函数时才会执行函数的定义体。
对类而言,首次导入模块时:解释器会执行每一个类的定义体,甚至会执行嵌套类的定义体。执行类定义体的结果是,定义了类的属性和方法,并构建了类对象。
从这个意义上理解,类的定义体属于“顶层代码”,由于它在导入时运行。
class除了除__mro__、__class__、和__name__以外还有如下属性:
cls.__bases__
:由类的基类组成的元组。cls.__qualname__
:其值是类或函数的限定名称,即从模块的全局做用域到类的点分路径。cls.__subclasses__()
:这个方法返回一个列表,包含类的直接子类。其实现使用弱引用,防止超类和子类之间出现循环引用。这个方法返回的列表是内存里现存的子类。cls.mro()
:构建类时,若是须要获取储存在类属性__mro__ 中的超类元组,解释器会调用这个方法。元类能够覆盖这个方法,定制要构建的类解析方法的顺序。