资讯专栏INFORMATION COLUMN

Python中的类元编程

alexnevsky / 2921人阅读

摘要:类元编程是指在运行时创建或定制类的技艺。注意在中做元编程时最好不要用和函数。三元类基础知识元类是类元编程最高级的工具使用元类可以创建具有某种特质的全新变种,例如抽象基类。建议除非开发框架,否则不要在生产代码中定义元类或抽象基类。

导语:本文章记录了本人在学习Python基础之元编程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。
本文重点:
1、了解运行时创建类的方法——类工厂函数;
2、熟悉元类的基础知识和使用场景;
3、了解元类的__prepare__的意义;
4、了解class的属性以及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最后一个参数是一个映射,指定新类的属性名和值。

注意:在Python中做元编程时, 最好不要用 exec 和 eval 函数.。如果接接收的字符串来自不可信的源,这两个函数会带来严重的安全风险.

二、定制描述符的类装饰器

类装饰器:本质上是高阶函数,其参数是被装饰的类,用于审查审查、修改、甚至把被装饰的类替换成其他类。

类装饰器的重大缺点:只对直接依附的类有效。即被装饰的类的子类可能继承也可能不继承装饰器所做的改动,具体情况视改动的方式而定。

三、元类 1、基础知识

元类:是类元编程最高级的工具:使用元类可以创建具有某种特质的全新变种,例如抽象基类。
功能:元类是创建类的类。
特点:

所有类都直接或间接地是type的实例,不过只有元类同时也是type的子类。

具体而言,元类可以通过实现__init__方法定制类。

为了避免无限回溯,type.__class是type,即type是自身的实例。

建议:除非开发框架,否则不要在生产代码中定义元类或抽象基类。

实例:使用元类定制描述符

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"""
2、元类的使用场景:

验证属性

一次把装饰器依附到多个方法上

序列化对象或转换数据

对象关系映射

基于对象的持久存储

动态转换使用其他语言编写为所有类定义的方法

四、元类的特殊方法__prepare__

__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
五、其他 1、Python中的导入时和运行时

在进程中首次导入模块时,Python解释器还会运行所导入模块中的全部顶层代码。以后导入相同的模块则使用缓存,只做名称绑定。

对函数而言,首次导入模块时:解释器会执行顶层的 def 语句,编译函数的定义体,把函数对象绑定到对应的全局名称上,但是显然解释器不会执行函数的定义体。通常这意味着解释器在导入时定义顶层函数,但是仅当在运行时调用函数时才会执行函数的定义体。

对类而言,首次导入模块时:解释器会执行每个类的定义体,甚至会执行嵌套类的定义体。执行类定义体的结果是,定义了类的属性和方法,并构建了类对象。从这个意义上理解,类的定义体属于“顶层代码”,因为它在导入时运行。

2、类的属性

class除了除__mro__、__class__、和__name__之外还有以下属性:

cls.__bases__:由类的基类组成的元组。

cls.__qualname__:其值是类或函数的限定名称,即从模块的全局作用域到类的点分路径。

cls.__subclasses__():这个方法返回一个列表,包含类的直接子类。其实现使用弱引用,防止超类和子类之间出现循环引用。这个方法返回的列表是内存里现存的子类。

cls.mro():构建类时,如果需要获取储存在类属性__mro__ 中的超类元组,解释器会调用这个方法。元类可以覆盖这个方法,定制要构建的类解析方法的顺序。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41522.html

相关文章

  • 理解Ruby的类

    摘要:序言源起于开发者公众号转载的深刻理解中的元类一文回忆着自己看过的元编程一书参照写个相应的版和在很多方面都非常相像特别是的设计部分参考了但在很多方面它俩的很多概念并非一一对应的在这里的元类在中并没有相应的概念如果理解为创建类的类最相近的应该是 live with scope 序言 源起于Python开发者公众号转载的深刻理解Python中的元类一文, 回忆着自己看过的 Ruby元编程 一...

    tanglijun 评论0 收藏0
  • How does it work - with_metaclass

    摘要:先简单介绍下中的元类。元类就是创建类的类,对于元类来说,类是它的实例,将返回。中的所有类,都是的实例,换句话说,是元类的基类。 我在看源代码的时候,经常蹦出这一句:How does it work!竟然有这种操作?本系列文章,试图剖析代码中发生的魔法。顺便作为自己的阅读笔记,以作提高。 先简单介绍下Python中的元类(metaclass)。元类就是创建类的类,对于元类来说,类是它的实...

    testbird 评论0 收藏0
  • [译]什么是元类metaclass?

    摘要:如果还是没有找到,就会使用父类中的元类来创建类。元类通常用于处理比较复杂的情况。这是因为使用了元类,它会将中定义的字段转换成数据库中的字段。中所有数据类型都是对象,它们要么是类的实例要么是元类的实例。 原文地址:what is metaclass in Python?我的简书地址::nummy 类即对象 在理解元类之前,需要先掌握Python中的类,Python中类的概念与SmallT...

    zsirfs 评论0 收藏0
  • Synchronized原理分析

    摘要:而导致这个问题的原因是线程并行执行操作并不是原子的,存在线程安全问题。如果已经有线程持有了锁,那这个线程会独占锁,直到锁释放完毕之前,其他线程都会被阻塞。当锁处于重量级锁状态,其他线程尝试获取锁时,都会被阻塞,也就是状态。 1. 什么时候需要用SynchronizedSynchronized主要作用是在多个线程操作共享数据的时候,保证对共享数据访问的线程安全性。比如两个线程对于i这个共...

    everfly 评论0 收藏0
  • Python入门学习笔记汇总

    摘要:导语本文章汇总了本人在学习基础之绪论篇数据结构篇函数篇面向对象篇控制流程篇和元编程篇学习笔记的链接,打算入门的朋友们可以按需查看并交流。 导语:本文章汇总了本人在学习Python基础之绪论篇、数据结构篇、函数篇、面向对象篇、控制流程篇和元编程篇学习笔记的链接,打算入门Python的朋友们可以按需查看并交流。 第一部分:绪论篇 1、Python数据模型 第二部分:数据结构篇 2、序列构成...

    U2FsdGVkX1x 评论0 收藏0

发表评论

0条评论

alexnevsky

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<