python 元类的call总结_Python 类与元类的深度挖掘 I【经验】

上一篇介绍了 Python 枚举类型的标准库,除了考虑到其实用性,还有一个重要的原因是其实现过程是一个非常好的学习、理解 Python 类与元类的例子。因此接下来两篇就以此为例,深入挖掘 Python 中类与元类背后的机制。

翻开任何一本 Python 教程,你一定可以在某个位置看到下面这两句话:

Python 中一切皆为对象(Everything in Python is an object);

Python 是一种面向对象编程(Object Oriented Programming, OOP)的语言。

虽然在上面两句话的语境中,对象(Object)的含义可能稍有不同,但可以肯定的是对象在 Python 中具有非常重要的意义,也是我们接下来将要讨论的所有内容的基础。那么,对象到底是什么?

对象(Object)

对象是 Python 中对数据的一种抽象,Python 程序中所有数据都是通过对象或对象之间的关系来表示的。[ref: Data Model]

港台将 Object 翻译为“物件”,可以将其看作是一个盛有数据的盒子,只不过除了纯粹的数据之外还有其它有用的属性信息,在 Python 中,所有的对象都具有id、type、value三个属性:

+---------------+

| |

| Python Object |

| |

+------+--------+

| ID | |

+---------------+

| Type | |

+---------------+

| Value| |

+---------------+

其中 id 代表内存地址,可以通过内置函数 id()查看,而type表示对象的类别,不同的类别意味着该对象拥有的属性和方法等,可以通过 type()方法查看:

def who(obj):

print(id(obj), type(obj))

who(1)

who(None)

who(who)

4515088368

4514812344

4542646064

对象作为 Python 中的基本单位,可以被创建、命名或删除。Python 中一般不需要手动删除对象,其垃圾回收机制会自动处理不再使用的对象,当然如果需要,也可以使用 del 语句删除某个变量;所谓命名则是指给对象贴上一个名字标签,方便使用,也就是声明或赋值变量;接下来我们重点来看如何创建一个对象。对于一些 Python 内置类型的对象,通常可以使用特定的语法生成,例如数字直接使用阿拉伯数字字面量,字符串使用引号 '',列表使用 [],字典使用 {} ,函数使用 def 语法等,这些对象的类型都是 Python 内置的,那我们能不能创建其它类型的对象呢?

类与实例

既然说 Python 是面向对象编程语言,也就允许用户自己创建对象,通常使用 class 语句,与其它对象不同的是,class 定义的对象(称之为类)可以用于产生新的对象(称之为实例):

class A:

pass

a = A()

who(A)

who(a)

140477703944616

4542635424

上面的例子中 A 是我们创建的一个新的类,而通过调用 A() 可以获得一个 A 类型的实例对象,我们将其赋值为 a,也就是说我们成功创建了一个与所有内置对象类型不同的对象 a,它的类型为 __main__.A!至此我们可以将 Python 中一切的对象分为两种:

可以用来生成新对象的类,包括内置的 int、str 以及自己定义的 A 等;

由类生成的实例对象,包括内置类型的数字、字符串以及自己定义的类型为 __main__.A 的 a。

单纯从概念上理解这两种对象没有任何问题,但是这里要讨论的是在实践中不得不考虑的一些细节性问题:

需要一些方便的机制来实现面向对象编程中的继承、重载等特性;

需要一些固定的流程让我们可以在生成实例化对象的过程中执行一些特定的操作;

这两个问题主要关于类的一些特殊的操作,也就是这一篇后面的主要内容。如果再回顾一下开头提到的两句话,你可能会想到,既然类本身也是对象,那它们又是怎样生成的?这就是后一篇将主要讨论的问题:用于生成类对象的类,即元类(Metaclass)。

super, mro()

0x00Python 之禅中提到的最后一条,命名空间(namespace)是个绝妙的理念,类或对象在 Python 中就承担了一部分命名空间的作用。比如说某些特定的方法或属性只有特定类型的对象才有,不同类型对象的属性和方法尽管名字可能相同,但由于隶属不同的命名空间,其值可能完全不同。在实现类的继承与重载等特性时同样需要考虑命名空间的问题,以枚举类型的实现为例,我们需要保证枚举对象的属性名称不能有重复,因此我们需要继承内置的 dict 类:

class _EnumDict(dict):

def __init__(self):

dict.__init__(self)

self._member_names = []

def keys(self):

keys = dict.keys(self)

return list(filter(lambda k: k.isupper(), keys))

ed = _EnumDict()

ed['RED'] = 1

ed['red'] = 2

print(ed, ed.keys())

{'RED': 1, 'red': 2} ['RED']

在上面的例子中 _EnumDict 重载同时调用了父类 dict的一些方法,上面的写法在语法上是没有错误的,但是如果我们要改变 _EnumDict 的父类,不再是继承自 dict,则必须手动修改所有方法中 dict.method(self) 的调用形式,这样就不是一个好的实践方案了。为了解决这一问题,Python 提供了一个内置函数 super():

print(super.__doc__)

super() -> same as super(__class__, )

super(type) -> unbound super object

super(type, obj) -> bound super object; requires isinstance(obj, type)

super(type, type2) -> bound super object; requires issubclass(type2, type)

Typical use to call a cooperative superclass method:

class C(B):

def meth(self, arg):

super().meth(arg)

This works for class methods too:

class C(B):

@classmethod

def cmeth(cls, arg):

super().cmeth(arg)

我最初只是把 super() 当做指向父类对象的指针,但实际上它可以提供更多功能:给定一个对象及其子类(这里对象要求至少是类对象,而子类可以是实例对象),从该对象父类的命名空间开始搜索对应的方法。

以下面的代码为例:

class A(object):

def method(self):

who(self)

print("A.method")

class B(A):

def method(self):

who(self)

print("B.method")

class C(B):

def method(self):

who(self)

print("C.method")

class D(C):

def __init__(self):

super().method()

super(__class__, self).method()

super(C, self).method() # calling C's parent's method

super(B, self).method() # calling B's parent's method

super(B, C()).method() # calling B's parent's method with instance of C

d = D()

print("\nInstance of D:")

who(d)

4542787992

C.method

4542787992

C.method

4542787992

B.method

4542787992

A.method

4542788048

A.method

Instance of D:

4542787992

当然我们也可以在外部使用 super() 方法,只是不能再用缺省参数的形式,因为在外部的命名空间中不再存在 __class__和 self:

super(D, d).method() # calling D's parent's method with instance d

4542787992

C.method

上面的例子可以用下图来描述:

+----------+

| A |

+----------+

| method()

+----------+ |

|

+----------+ +----------+

| B | | D |

+----------+ super(C,self) +----------+

| method()

+----------+ +----------+

|

+----------+ |

| C | |

+----------+ | super(D,self)

| method()

+----------+

可以认为 super()方法通过向父类方向回溯给我们找到了变量搜寻的起点,但是这个回溯的顺序是如何确定的呢?上面的例子中继承关系是 object->A->B->C->D 的顺序,如果是比较复杂的继承关系呢?

class A(object):

pass

class B(A):

def method(self):

print("B's method")

class C(A):

def method(self):

print("C's method")

class D(B, C):

def __init__(self):

super().method()

class E(C, B):

def __init__(self):

super().method()

d = D()

e = E()

B's method

C's method

Python 中提供了一个类方法mro()可以指定搜寻的顺序,mro 是Method Resolution Order的缩写,它是类方法而不是实例方法,可以通过重载 mro() 方法改变继承中的方法解析顺序,但这需要在元类中完成,在这里只看一下其结果:

D.mro()

[__main__.D, __main__.B, __main__.C, __main__.A, object]

E.mro()

[__main__.E, __main__.C, __main__.B, __main__.A, object]

super() 方法就是沿着 mro() 给出的顺序向上寻找起点的:

super(D, d).method()

super(E, e).method()

B's method

C's method

super(C, e).method()

super(B, d).method()

B's method

C's method

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/503570.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tp5 php跨域,TP5.1解决跨域

TP5.1解决跨域博客说明文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!介绍在前后端分离开发的时候就会遇到跨域的问题,在本地调试的时候可能不…

python中的pow怎么使用_python中的pow函数怎么用

描述pow() 方法返回 xy(x的y次方) 的值。语法以下是 math 模块 pow() 方法的语法:import mathmath.pow( x, y )内置的 pow() 方法pow(x, y[, z])函数是计算x的y次方,如果z在存在,则再对结果进行取模,其结果等效于pow(x,y) %z注意:…

nginx 配置php版本号,隐藏Apache、nginx和PHP的版本号的配置方法

最近提示说有漏洞,暴露apache、nginx和php的版本号。网上搜了下,整理的方法如下:首先说apache在http.conf文件里添加下面两行,默认是没有的ServerSignature OffServerTokens ProdServerSignature出现在Apache所产生的像404页面、目…

python爬取电子病历_利用 BERT 模型解析电子病历

项目原始地址项目地址本项目改编自此 Github 项目,鸣谢作者。问题描述我们希望能从患者住院期间的临床记录来预测该患者未来30天内是否会再次入院,该预测可以辅助医生更好的选择治疗方案并对手术风险进行评估。在临床中治疗手段常见而预后情况难以控制管…

python获取坐标颜色,python – 根据一组坐标的数据着色地图

您的第一种方法称为Voronoi diagramm对于使用D3库的javascipt,这种图表有一个解决方案为了使这个解决方案完整,我在这里粘贴M.Bostock示例中的代码var w 1280,h 800;var projection d3.geo.azimuthal().mode("equidistant").origin([-98, 38]).scale(1400).transl…

最少交换次数python_leetcode第200周赛第三题leetcode1536. 排布二进制网格的最少交换次数...

leetcode1536. 排布二进制网格的最少交换次数给你一个 n x n 的二进制网格 grid,每一次操作中,你可以选择网格的 相邻两行 进行交换。一个符合要求的网格需要满足主对角线以上的格子全部都是 0 。请你返回使网格满足要求的最少操作次数,如果无…

php常用功能代码,10段PHP常用功能代码(1)_PHP教程

1、使用PHP Mail函数发送Email$to "viralpatel.netgmail.com"; $subject "VIRALPATEL.net"; $body "Body of your message here you can use HTML too. e.g. ﹤br﹥ ﹤b﹥ Bold ﹤/b&#xfe6…

如何避免_如何避免变频器受负载冲击

电工学习网:www.diangon.com关注电工学习网官方微信公众号“电工电气学习”,收获更多经验知识。为了保障变频器的安全运行,避免变频器受负载冲击,必须做好以下几点:㈠尽量保证变频器有充足的加减速时间变频器在开机或升速时&#…

哪种语言 连接 oracle,Go语言连接Oracle(就我这个最全)

综合参考了网上挺多的方案倒腾了半天终于连接好了Go都出来这么多年了还没有个Oracle的官方驱动。。。过程真的很蛋疼。。一度想放弃直接连ODBC首先交代一下运行环境和工具版本:WIN10MINGW64ORACLE INSTANCCLIENT_18_3 x64Jetbrins Goland看完这篇文章,…

补丁程序正在运行_针对微软4月14日更新补丁会导致蓝屏问题的检测及解决方法...

近期,我们接连收到用户求助,在使用电脑过程中会突然出现蓝屏问题,经火绒工程师分析发现,大部分用户出现蓝屏问题,是因为安装了微软于4月14日推送的补丁所致(详见下图)。目前微软方面表示正在调查相关问题。Win10系统蓝…

服务器访问oracle数据库,Oracle数据库的访问——通过不同服务器名对数据库的访问...

服务器端完成配置后,现在客户端可以通过不同的网络服务名配置来访问这个数据库,下面是一个配置示范:EYGLE(DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST 172.16.33.11)(PORT 1521)))(CONNECT_DATA (SERVICE_NAME eygle)))JU…

oracle质数怎么算,借花献佛之使用Oracle sql求质数(笔记)

首先声明一点,文章内容从itpub论坛上看到的,原文链接 http://www.itpub.net/thread-1849398-1-1.html,本文主要是记录下笔记,原文中有更详细的分析。使用sql求质素没什么实用价值,重要的是思路。(一)最简单的方法思路&…

商城html源码_Java开源商城源码推荐,从菜鸡到大神,永远绕不开的商城系统

每个Java程序员,从懵逼菜鸡,再到懵懂菜鸟,再到小鸟,大鸟,最后到技术大神,始终绕不开商城系统,里面蕴含了大量的业务,涉及到了大量的知识点和解决方案。今天锋哥介绍一款Java开源商城…

Oracle distinct后加as,【大话IT】为何加distinct之后就不走索引了

还是一样&#xff0c;16:45:44 SQL> l1 select site_id,2 count(*) sendnum3 from4 (select site_id,5 ewb_no6 from prod_send t7 where scan_time > to_date(2015-05-15, yyyy-mm-dd)8 and scan_time < to_date(2015-05-16, yyyy-mm-…

cpu只能单通道是什么表现_【小白入门】为什么要组内存双通道?

更新时间&#xff1a;2020年5月11日 内容提要&#xff1a; 1.内存双通道的原理 2.如何组双通道很多小白在购买内存的时候&#xff0c;不知道该购买一根单16G还是两根单8G&#xff0c;看完本篇文章你将知道内存双通道的优势。1.内存双通道的原理选择两根单8G组成双通道&#xff…

matlab 图像矢量量化,MATLAB环境下基于矢量量化的说话人识别系统(1)

第21卷第6期湖 北 工 业 大 学 学 报2006年12月Vol.21No.6 Journal of Hubei Univer sity of Technology Dec.2006[收稿日期]2006-10-13[作者简介]宋 敏(1979-),女,湖北武汉人,湖北工业大学硕士研究生,研究方向:计算机语音技术应用.[文章编号]1003-4684(2006)1220027203MATLAB …

雷云3灯光配置文件_雷蛇的哪种键盘最适合入手?3款最佳雷蛇键盘推荐。

更新时间2020.8.6本次主要内容是雷蛇的三款不同价位的雷蛇键盘推荐&#xff0c;有需要的小伙伴可以看一下哦&#xff0c;也许你想要入手的键盘就在其中。---------------------------------雷蛇黑寡妇蜘蛛精英版--------------------------------------黑寡妇蜘蛛精英版在猎魂光…

oracle窗帘位图索引,Greenplum数据库设计开发规范参考.docx

Greenplum数据库设计开发规范参考Greenplum数据库设计开发规范参考文档2016年7月目 录Greenplum数据库设计开发规范1V1.511 前言41.1 文档目的41.2 文档范围41.3 预期读者41.4 参考资料42 开发规范检查项43 GP与TD的差异关注点64 系统级设计74.1 用户设计74.1.1 超级用户84.1.2…

某些您可以编辑的区域交叠在一起 可能不能同时显示_DX200操作要领—修改与编辑程序(三十九)...

3.5 修改程序3.5.1 程序的调出1. 选择主菜单中的【程序内容】2. 选择【程序选择】–显示程序一览表。3. 选择要调出的程序3.5.2 程序相关画面程序相关画面有下面5种&#xff0c;可以确认/编辑每个程序的设定或登录。•程序标题画面显示和编辑注释、登录日期、编辑禁止的状态等。…

求二叉树中以x为根的子树的深度_还在玩耍的你,该总结啦!(本周小结之二叉树)...

给「代码随想录」一个星标吧&#xff01;❝有学习就要有总结❞本周小结本周赶上了十一国庆&#xff0c;估计大家已经对本周末没什么概念了&#xff0c;但是我们该做总结还是要做总结的。本周的主题其实是「简单但并不简单」&#xff0c;本周所选的题目大多是看一下就会的题目&a…