【Python】学习笔记——-8.6、面向对象高级编程:6.使用元类

Python中元类(Metaclasses)详解:掌握高级编程的钥匙 除了使用默认的type元类外,我们还可以自定义元类来控制类的创建过程。在Python中,元类通常继承自type,并重写__new__方法或__init__方法来定制类的创建行为。# 自定义类创建逻辑pass在自定义元类中,__new__cls表示元类本身,name表示类名,bases表示父类元组,dct表示类的属性字典。通过重写__new__方法,我们可以完全控制类对象的创建过程。 阅读详情

type()

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

比方说我们要定义一个Hello的class,就写一个hello.py模块:

class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)

当Python解释器载入hello模块时,就会依次执行该模块的所有语句,执行结果就是动态创建出一个Hello的class对象,测试如下:

>>> from hello import Hello
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class 'hello.Hello'>

type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello。

我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:

>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

正常情况下,我们都用class Xxx...来定义类,但是,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

metaclass

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:

定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:

class MyList(list, metaclass=ListMetaclass):
    pass

当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

__new__()方法接收到的参数依次是:

  1. 当前准备创建的类的对象;

  2. 类的名字;

  3. 类继承的父类集合;

  4. 类的方法集合。

测试一下MyList是否可以调用add()方法:

>>> L = MyList()
>>> L.add(1)
>> L
[1]

而普通的list没有add()方法:

>>> L2 = list()
>>> L2.add(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'add'

动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。

但是,总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。

ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

让我们来尝试编写一个ORM框架。

编写底层模块的第一步,就是先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,我们期待他写出这样的代码:

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

# 创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()

其中,父类Model和属性类型StringField、IntegerField是由ORM框架提供的,剩下的魔术方法比如save()全部由metaclass自动完成。虽然metaclass的编写会比较复杂,但ORM的使用者用起来却异常简单。

现在,我们就按上面的接口来实现该ORM。

首先来定义Field类,它负责保存数据库表的字段名和字段类型:

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)

在Field的基础上,进一步定义各种类型的Field,比如StringField,IntegerField等等:

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')

下一步,就是编写最复杂的ModelMetaclass了:

class ModelMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        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)
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)

以及基类Model:

class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    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

    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.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到,就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclass的ModelMetaclass来创建User类,也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。

在ModelMetaclass中,一共做了几件事情:

  1. 排除掉对Model类的修改;

  2. 在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);

  3. 把表名保存到__table__中,这里简化为表名默认为类名。

在Model类中,就可以定义各种操作数据库的方法,比如save(),delete(),find(),update等等。

我们实现了save()方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句。

编写代码试试:

u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
u.save()

输出如下:

Found model: User
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: id ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
SQL: insert into User (password,email,username,id) values (?,?,?,?)
ARGS: ['my-pwd', 'test@orm.org', 'Michael', 12345]

可以看到,save()方法已经打印出了可执行的SQL语句,以及参数列表,只需要真正连接到数据库,执行该SQL语句,就可以完成真正的功能。

不到100行代码,我们就通过metaclass实现了一个精简的ORM框架。

小结

metaclass是Python中非常具有魔术性的对象,它可以改变类创建时的行为。这种强大的功能使用起来务必小心。

Carla自动驾驶仿真一:快速安装与运行Carla 网上有不少的Windows版本的Carla的安装教程,这里简单记录下自己安装过程,写的也比较详细,基本不用看其他文章。 阅读详情

相关推荐

从零开始:如何用Python建立你的第一个人工智能模型

在这篇文章中,我们将介绍如何从零开始使用Python建立你的第一个人工智能模型。无论你是刚接触编程的新手,还是有经验的开发者想进一步探索人工智能领域,这篇文章都将为你提供清晰、详细的指南。我们将一步步探索数据预处理、模型建立、训练和测试的过程,以及如何解读模型的结果。

Allen1862105的博客 4万+

Python使用元类

type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。 比方说我们要定义一个Hello的class,就写一个hello.py模块: class Hello(object): def hello(self, name='world'): print('Hello, %s.' % name) 当Pyth

dolphin98629的专栏 688

从实验室到晶圆厂:一文看懂二维半导体(如MoS2)的CVD生长与晶体管制造挑战

本文深入探讨了二维半导体(如MoS2)从实验室研究到晶圆厂量产的CVD生长与晶体管制造关键技术挑战。重点分析了晶圆级二维半导体的CVD生长工艺优化、晶体管制造的四大界面工程难题,以及掺杂与集成的产业化解决方案,为二维半导体在集成电路领域的应用提供技术参考。

weixin_42651748的博客 472

python-关于返回值return

所有函数在没有指定返回值时都有一个默认的返回值None。如果函数没有return语句,则默认返回None.None表示空的意思。"return"是编程语言中的一个关键字,通常用于函数或方法的结尾,表示将执行结果返回给调用者。2.退出当前函数,导致return下方的(函数体内部的‘缩进的’)代码不执行。用return print(' ')也会报错,如果要打印字符串可以用return f' '在类方法中,__str__中的return后只能是字符串类型。1.负责函数返回值,返回一个参数或数据等。

2401_84246493的博客 4763

SAP License USMM SLAW2 SAP审计

引言 企业每年都会收到SAP License审计邮件,邮件内容大致就是根据贵公司与SAP签定的合同XXX条款规定,贵公司所安装的SAP系统需要进行测定。 关键字: SAP License、 SAP USMM、 SLAW2、 SAP 账号、SAP 账号审计、 SAP License 审计、 SAP 审计报告、SAP 账号优化 SAP License审计邮件 企业每年都会收到SAP License审计邮件,邮件内容大致就是根据贵公司与SAP签定的合同XXX条款规定,贵公司所安...

感悟人生 2981

手把手教你RK3588 NPU驱动与RKNN Toolkit,AI开发不迷路!

本文详细介绍了RK3588 NPU驱动安装与RKNNToolkit使用方法。首先阐述了RK3588芯片在嵌入式AI领域的优势,包括6TOPS算力和混合量化支持。随后分步骤讲解了NPU驱动安装流程,包括准备工作、编译安装、验证调试及常见问题解决。接着全面介绍了RKNNToolkit的环境搭建、基本使用流程和图像分类实战案例,涵盖模型转换、推理优化等关键环节。最后展望了RK3588在智能安防、工业自动化等领域的应用前景,为开发者提供了完整的嵌入式AI开发指南。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 433

Python 使用 type 动态创建类

type 函数有两种形式的调用: class type(object) class type(name, bases, dict) 第一种比较常见, 用于查看对象所属的类 第二种可以动态创建类, 无需使用 class 语句 下面对比两种创建类的方法 静态创建 class TestClass1: time = "2021-03-31" def test(self): print("Test1") 动态创建 name 直接绑定类的 __name__ 属性, bases 可

jianghuixin 2301

DSShop移动商城网店系统 反序列化RCE漏洞复现

DSShop单店铺移动商城网店系统的getCartList方法的cart参数存在php反序列化漏洞,攻击者可以通过漏洞执行任意代码,获取服务器权限。

0xSec 1472

python type()函数专题——动态创建类

​type()#或者前一种用法接受一个对象(变量),返回 object 的类型。返回值是一个 type 对象,通常与 ​​所返回的对象相同。name = ""print("我可以呼吸")a = Animal('大象')print(type(a)) # 返回类Animal的实例对象 (aka 'object') 或者 )尝试一下他的返回值是:我们可以看出,这是一个Animal对象。

m0_67373485的博客 2217

海外购物商城源码 带即时通讯IM源码

在海外购物商城中,即时通讯IM源码可以为用户提供更便捷、高效的沟通方式,帮助解决跨国交易中的语言和文化差异问题。总之,即时通讯IM源码在海外购物商城中扮演着重要的角色,可以为用户提供更方便、高效的沟通方式,解决跨国交易中的语言和文化差异问题。(购买本专栏可免费下载栏目内所有资源不受限制,持续发布中,需要注意的是,本专栏为批量下载专用,并无法保证某款源码或者插件绝对可用,介意不要购买)实时翻译:即时通讯IM源码提供实时翻译功能,能够快速准确地翻译不同语言之间的文字、语音、视频等信息,帮助用户克服语言障碍。

很酷的站长的博客 259

利用 RSAU_LOG_API 把 SAP 安全审核日志接入企业 SIEM 的完整指南

在 SAP 世界里,安全审核日志(Security Audit Log,简称 SAL)是审计和合规的核心基础设施之一。系统会在后台把安全相关的关键事件记录下来,比如:(用户登录与登录失败用户锁定、解锁权限对象或角色变更远程调用(RFC)相关活动关键事务码的启动程序直接执行、批处理作业相关的敏感操作传统 ABAP 系统里,这些日志会写到本地应用服务器上的审计文件中(背后对应类似表RSAUENTR2的结构),包含事件标识、客户端、用户、终端、事务码、时间戳等信息。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 163

Python函数常见return返回值类型

Python函数常见return返回值类型

xaiozhan的博客 5348

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Python 面向对象高级编程-使用元类

函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

分享编程知识与技巧 致力于技术交流与学习 找我合作私聊 有偿解决计算机问题 1017

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Python教程(十二):面向对象高级编程详解

在学习了Python的基本面向对象编程(OOP)概念之后,本篇文章将深入探讨Python中的高级OOP特性。这些特性包括类的内部变量、特殊方法(Magic Methods)、装饰器、枚举类以及元类。通过对这些高级特性的介绍和实例演示,帮助你更好地掌握Python的面向对象编程。枚举(Enum)是一种特殊的类,用于表示一组相关的常量值。枚举类中的每个成员都是唯一的,可以使用名称或值进行访问。可以使用Python的enum模块定义枚举类。元类(Metaclass)是用于创建类的类。

前端探索者 2181

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Python中的元类:类的类与高级面向对象编程

定义一个元类其实就是定义一个继承自type的类。一个简单的元类如下所示:# 自定义一个元类 class MyMeta(type): def __new__(cls, name, bases, dct): print(f"Creating class {name}") return super().__new__(cls, name, bases, dct) # 使用元类来创建一个类 class MyClass(metaclass=MyMeta): passMyMeta是一个元类,它继承自type。

2501_94180726的博客 1031

深入浅出 Python 面向对象编程:从基础到高级特性

本文全面介绍了Python面向对象编程(OOP)的核心概念与实践。主要内容包括:1)类与对象的基本概念,类作为对象模板、对象作为实例的关系;2)面向对象三大特性:封装(数据与操作绑定)、继承(代码复用与扩展)、多态(统一接口不同实现);3)类成员的分类与使用:实例属性/方法、类属性/方法、静态方法;4)魔法函数的应用,实现初始化、运算符重载等特殊功能;5)高级特性如动态添加成员、单例模式、元类、迭代器与生成器;6)设计实践包括混合类(Mixin)和最佳编程原则。文章通过丰富代码示例,系统讲解了从基础到高级的

2402_85519509的博客 1264

Python 元类(Meta Class):解密 Python 面向对象编程的幕后推手

在类创建前,为所有方法添加日志。

记录学习痕迹的公众号:Piper蛋窝 1504

Python 元类编程详解:从原理到实践

元类是继承自 type 的类,它重写了__new__或__init__方法来控制类的创建过程。

lady_mumu的博客 1174

Python 类的元编程-元类metaclass

元编程(Metaprogramming)是指编写可以操作、修改或生成代码的代码。在 Python 中,元编程是一种非常强大的工具,允许在运行时动态创建类、修改类的行为,甚至在代码执行过程中改变对象的结构。特别是在面向对象编程中,类的元编程提供了对类定义、继承和行为的精细控制。本文将深入探讨 Python 中的类的元编程,介绍类的元编程的基本概念、常见的元编程技术,以及如何通过元类(metaclass)来实现这些功能。

2074

RTK(Rust Token Killer)官方指南:Hook 拦截、输出过滤与 Token 节省度量的完整工作流

本篇技术文章以 RTK 官方文档指南首页([docs/guide/index.md](https://link.gitcode.com/i/4ba9ef8778273af993a0315a843407dc))为骨架展开:先讲清 RTK「CLI 代理 + Hook 透明重写」的核心机制,再覆盖从安装初始化、命令过滤到 `rtk gain` / `rtk discover` / `rtk sessio

gitblog_00803的博客 836

Python 类与面向对象编程:从基础到高级应用

Python类与面向对象编程:从基础到高级应用 摘要:Python作为一门简洁强大的语言,其面向对象编程(OOP)特性通过类(Class)机制实现。本文系统讲解了Python类的基本定义与实例化方法,展示了类如何作为结构或记录使用。深入解析了初始化方法__init__的用途与约定,以及@property装饰器在属性管理中的应用。文章覆盖了从类实例化、属性操作到高级特性如私有成员和元类等内容,通过理论结合代码示例的方式,帮助读者全面掌握Python面向对象编程的核心概念和实践技巧。无论是初学者还是经验丰富的开

静水深流 1035

Python元类:深入理解元编程的奥秘

更多Python学习内容:ipengtao.com元类(Metaclass)是Python中一种高级的编程概念,用于创建类的类。元类可以控制类的创建过程,实现类的定制化行为,是实现元编程的重要工具。本文将深入探讨Python中元类的概念、用法、应用场景以及示例代码,帮助全面了解和掌握元类的奥秘。元类简介元类是Python中非常强大和灵活的特性,它允许开发者在创建类时动态地控制类的行为和属性。每个类...

GitHub_miao的博客 1242

Python编程:元类的简单使用

元类 Python 2.7.5 旧式类 class Foo(): pass foo = Foo() print(type(foo)) print(type(Foo)) print(type(type)) # <type 'instance'> # <type 'classobj'> # <type 'type'> Python 3.6.5 新式类 ...

彭世瑜的博客 1394

python面向对象高级编程:使用元类

元类通常是通过继承type来创建的。你可以通过重写type的__new__和__init__方法来控制类的创建和初始化过程。python复制代码# 在这里可以修改类的定义# 在这里可以执行类创建后的操作元类是Python中一个强大但复杂的特性。它们允许你深入控制类的创建和初始化过程,并可以实现一些高级编程模式。然而,由于元类的使用可能会使代码变得难以理解和维护,因此它们通常只在必要时才使用。在大多数情况下,普通的类和继承关系足以满足需求。

2301_78045723的博客 529

Python面试题:使用Python进行元编程:元类和元编程技巧

元类: 用于创建和修改类的定义,是高级的元编程技术。通过自定义元类,可以控制类的创建过程和行为。装饰器: 用于修改函数或类的行为而无需直接修改它们的代码。可以在函数或类定义之前通过装饰器添加功能。描述符: 用于控制对对象属性的访问,通过实现__get____set__和__delete__方法来实现。动态属性和方法: 使用setattr和getattr可以动态地添加或修改对象的属性和方法。类工厂函数: 动态创建类,并根据需求生成不同的类定义。

杰哥在此的专栏 1373

Python 魔法学院 - 第11篇:Python 元类编程 ⭐⭐⭐

探索 Python 元类编程的神秘世界!元类是 Python 中用于创建类的“模板”,赋予你动态修改类行为的能力。本文通过生动的示例和清晰的内存结构模拟,带你从基础到实战,掌握元类的核心概念与应用场景,如单例模式、ORM 框架等。

星核日记,一个编程开发者的小天地。我将和大家一起,穿梭于多语言迷宫,剖析算法精妙,分享实战项目。 1196
上一篇: 【Python】学习笔记——-8.5、面向对象高级编程:5.使用枚举类
下一篇: 【Python】学习笔记——-09、错误、调试和测试
白衣染霜陈
博客等级 码龄22年 682粉丝 338原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值