Python类元编程初探

在《流畅的Python》一书中提到:

Classes are first-class object in Python, so a function can be used to create a new class ant any time, without using the class keyword.

在Python中,声明一个类可以有两种方法:

>>> class X:
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))

可见类X也是一个type类型的对象。

Import time和runtime

在import一个模块的时候,可以看出有哪些模块中的语句会执行。比如有模块a.py


A = 'A'

class ClassA:
    print('print ClassA')

def printf():
    print('print printf)

当我们执行import a的时候:

>>> import a
print ClassA
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'a': <module 'a' from 'D:\\Workspace\\a.py'>}

可以看到,类ClassA中的语句会被执行,可以根据根据代码所在在模块中的层级,将一些在顶级的代码称作top-level code。这类代码会在import time被执行。很显然,类中的代码块也会被执行。为什么模块a.py中的函数printf不会被执行呢?根据文章开头提到的,类也是一种对象。声明一个类,相当于执行type创建一个type对象。所以类声明语句会被执行,需要确定声明的类有哪些属性和方法。

__new__和__init__方法

官方文档给出的说明:

object.__new__(cls[, ...]), called to create a new instance of class cls.
object.__init__(self[, ...]), called after the instance has been created (by new()), but before it is returned to the caller.

new() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

在实例化一个类的对象的时候,首先会调用__new__来确定会实例化哪个类,然后在调用__init__来初始化具体的对象。一般很少使用这种特性。但是在编写一些框架的时候却很有用,比如编写ORM框架。

metaclass的具体使用

上文提到的,可以用type来创建一个类。当type不能满足需求的时候,就需要自定义type方法了。

在廖雪峰的使用元类一文中,需要实现一个简单的ORM。比如定义了一个模型User,它对应数据库中的users表,有四个字段,分别是id、name、email和password:

class User(Model):
    # 定义类的属性到列的映射:
    __tablename__ = 'users'
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

类属性(id、name、email和password)如何和表中的字段关联?如何只是声明一个这样的类,没有任何作用,解释器会调用type(‘User‘, (Model, ), {‘id‘: IntegerField(‘id‘)})来创建type对象。很显然默认的type方法不满足需要。所以需要自定义一个type方法:

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s==>%s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        print(cls)
        attrs['__table__'] = attrs['__tablename__']
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        print('ModelMetaclass.__new__')
        return type.__new__(cls, name, bases, attrs)

这样就可以调用ModelMetaclass(‘User‘, (Model,), {‘id‘: IntegerField(‘id‘)})来创建一个类对象。粘贴复制加魔改廖雪峰博文中的代码,省略部分代码:

class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type
    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

class StringField(Field):
    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')

class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s==>%s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        print(cls)
        attrs['__table__'] = name # 假设表名和类名一致
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        print('ModelMetaclass.__new__')
        print(attrs)
        return type.__new__(cls, name, bases, attrs)

class Model(dict):

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__tablename__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

User = ModelMetaclass('User', (Model, ), {'__tablename__': 'users', 'id': IntegerField('id')})

在上面的代码中,我们使用ModelMetaclass来动态的创建User类,这和廖雪峰博文中在声明类的时候指明metaclass=ModelMetaclass是一样的效果。sqlalchemy中就是使用这种方式来动态的创建类的:

def declarative_base(
    bind=None,
    metadata=None,
    mapper=None,
    cls=object,
    name="Base",
    constructor=_declarative_constructor,
    class_registry=None,
    metaclass=DeclarativeMeta,
):
    lcl_metadata = metadata or MetaData()
    if bind:
        lcl_metadata.bind = bind

    if class_registry is None:
        class_registry = weakref.WeakValueDictionary()

    bases = not isinstance(cls, tuple) and (cls,) or cls
    class_dict = dict(
        _decl_class_registry=class_registry, metadata=lcl_metadata
    )

    if isinstance(cls, type):
        class_dict["__doc__"] = cls.__doc__

    if constructor:
        class_dict["__init__"] = constructor
    if mapper:
        class_dict["__mapper_cls__"] = mapper

    return metaclass(name, bases, class_dict)

原文地址:https://www.cnblogs.com/mrzysv5/p/10759664.html

时间: 2024-08-29 20:09:49

Python类元编程初探的相关文章

小白尝试写一篇类元编程记录。

Java学了几个小时,这两天又被元编程搞死,准备粗粗写一些我的理解.后面还有协程需要理解.感觉年底之前搞定这些有点累. 先上参考文献:https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072 https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python/6581949#6581949 https://www.jianshu.com/p/

Julia元编程初探——以简单符号求导为例

Julia语言具有强大的元编程机制,本文用Julia实现<SICP>中文第二版中第 99 页中的实例:符号求导,体验一下Julia元编程. 运行结果如下: julia> include("deriv.jl") # 加载代码multiplicand (generic function with 1 method) julia> deriv(:(x + 3), :x) # 求表达式 x + 3 关于 x 的导数1 julia> deriv(:(x * y),

python之元编程(元类实例)

本实例是元类实例,功能是记录该的子类的类名,并以树状结构展示子类的类名. RegisterClasses继承自type,提供的功能是在__init__接口,为类创建了childrens的集合,并类名保存到对应的父类元组的childrens的集合中. 同时对__str__打印方法和__iter__迭代方法进行了定义,其中: __iter__方法返回类名的childrens集合,并对其中的元素进行输入. 而Sharp继承自RegisterClasses, 当 for s in Sharp: prin

Python学习(十一) Python 类

Python 类 面向对象编程是有效的软件编写方法之一. python程序编写方法 1.函数编程,使用函数方式 2.面向对象编程,使用类方式 创建类 创建方法 构造方法,__init__(self,arg) obj = 类('a1') 普通方法 obj = 类('xxx') obj.普通方法名() 格式如下: class DataBaseHelper: def __init__(self, ip, port, username, pwd): self.ip = ip self.port = po

python元编程之使用动态属性实现定制类--特殊方法__setattr__,__getattribute__篇

问题:实现一个类,要求行为如同namedtuple:只存在给定名称的属性,不允许动态添加实例属性. 主要知识点在于: __setattr__,__getattr__,getattribute__,__delattr__特殊方法的实现使用. 代码如下: 1 """ 2 运行环境 3 python 3.7+ 4 """ 5 from collections OrderedDict, namedtuple 6 #以下为要包装的对象:1个命名元组,用于存

python类:描述器Descriptors和元类MetaClasses

http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制当你想引用一个属性时所发生的操作. 构建描述器的方法是至少定义以下三个方法中的一个.需要注意,下文中的instance是包含被访问属性的对象实例,而owner则是被描述器修辞的类. __get__(self, instance, owner) – 这个方法是当属性被通过(value = obj.at

Python-面向对象(三 元编程)

译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了985点的赞同点数,更有人评论说这段回复应该加入到Python的官方文档中去.而e-satis同学本人在Stack Overflow中的声望积分也高达6

Python元编程

简单定义"元编程是一种编写计算机程序的技术,这些程序可以将自己看做数据,因此你可以在运行时对它进行内审.生成和/或修改",本博参考<<Python高级编程>>将对元编程内容进行详细描述,若有不正确之处希望大家指出. 1. 概述 Python元编程有两种方法,一是采用类似"装饰器"的工具对基本元素(例如函数.类.类型)内审和对其进行实时创建和修改,二是运用类型"元类"的方式对类实例的创建过程进行修改,甚至于允许重新设计Pyt

Python之元类详解

一.引子 元类属于Python面向对象编程的深层魔法,99%的人都不得要领,一些自以为搞明白元类的人其实也是自圆其说,点到为止,从队元类的控制上来看就破绽百出,逻辑混乱: 二.什么是元类 一切源自于一句话:Python中一切皆为对象.让我们先定义一个类,然后逐步分析 #!/usr/bin/env python # -*- coding: utf-8 -*- class MyTeacher(object): school='john' def __init__(self,name,age): se