当前位置: 首页 > news >正文

Python Cookbook-6.19 调用超类的__init__方法

任务

想确保所有的超类的__init__方法被自动调用(如果该超类定义过__init__方法)但 Python 并不会自动调用此方法。

解决方案

如果你的类是新风格类,内建的 super 会使这个任务变得极其简单(如果所有的超类的__init__方法也用相似的方式使用 super):

class NewstyleOnly(A,B,C):def __init__(self):super(NewStyleOnly,self).__init__()initialization specific to subclass NewstyleOnly

讨论

对新的开发任务,经典类不太值得推荐:它们的存在只是为了保证对于老版本Python的向后兼容性。应当尽量使用新风格类(直接或间接从object派生)。在新风格类中唯一你不能做的事情是将它的实例当做异常对象抛出,异常对象必须是旧风格的,不过对这些类你并不需要使用本节提供的功能。因此,下面将要进行的讨论会比较深入同时适用面也更窄,你如果不感兴趣的话可以直接略过。

你可能需要在经典类中使用这个功能,或者,你也想在新风格类中使用这个功能,但是这个类的某些超类并不满足前面提到的条件,即以正确的方式调用内建函数 super。针对这些情况,首先你要做的是前期准备工作——将所有的类转变为新风格类并且使它们正确地调用 super。如果你确实无法完成准备工作,那么你能做的就是循环检查基类–对每个基类,都看它是否拥有__init__,如果有的话,调用该方法:

class LookBeforeYouLeap(X,Y,Z):def __init__(self):for base in self__class__.__bases__:if hasattr(base,'__init__'):base.__init__(self)initialization specific to subclass lookBeforeYouLeap

更一般的,并不仅限于__init__ 方法,我们常常需要调用一个实例或者类的其他方法如果这个方法存在的话;如果实例或类的此方法不存在,我们就什么也不做,或者只做默认的动作。解决方案中展示的基于内建的 super 技术并不具有通用性:它只适用于当前对象的超类,要求那些超类以正确的方式调用 super,还要求被调用的方法在那些超类中存在。不过,所有的新风格类都拥有__init__方法:它们都是object的子类,而object定义了__init__(默认什么也不做,接受并忽略任何参数)。因此,所有的新风格类都拥有__init__方法,无论是继承来的还是重载过的。

在 LookBeforeYouLeap 类中展示的 LBYL, 技术具有更宽广的应用面,它可被用于包括__init__ 在内的各种方法。LBYL还可以和 super 合用,如下面这个例子所示:

class Base1(object):def met(self):print 'met in Base1'
class Der1(Base1):def met(self):s = super(Der1,self)if hasattr(s,'met'):s.met()print 'met in Der1'
class Base2(object):Pass
class Der2(Base2):def met(self):s = super(Der2,self)if hasattr(s,'met'):s.met()print 'met in Der2'
Der1().met()
Der2().met()

这个片段的输出是:

met in Base1
met in Derl
met in Der2

met 的实现对于所有的派生类,包括Der1(超类是 Base1,超类有 met 方法)和 Der2(超类是 Base2,超类没有这个方法),都具有相同的结构。通过将局部名字s绑定到super 的结果,并在调用方法之前先用 hasattr 检查超类是否拥有这个方法,这个LBYL结构使得可以用相似的代码处理两种情况。当然,在编写子类的时候,你通常就已经知道了超类有哪些方法以及是否需要和怎样调用它们。有时,当需要子类略微偏离它的超类的时候,这种技术能够提供一些额外的灵活度。

不过 LBYL 技术还远远谈不上完美:超类可能定义一个属性名为 met,这个 met 不能被调用也不需要参数。如果你确实非常需要灵活性,必须排除这种情况,可以获取超类的方法对象(如果有的话),然后用标准库模块inspect提供的getargspec函数来检查。但是当试图将这个概念推及到一般的情况下,复杂性就增加了。下面是一个例子,这个类的方法会试图不用参数调用超类中同名的方法(如果在超类中该属性存在而且可调用):

import inspect
class Der(A,B,C,D):def met(self):s = super(Der,self)#获取超类的被绑定对象,如果没有的话返回Nonem = getattr(s,'met',None)try:args,varargs,varkw,defaults = inspect.getargspec(m)except TypeError:#m不是方法,忽略passelse:#m是方法,它的所有参数都有默认值吗?if len(defaults) == len(args):#是的,调用之:m()print 'met in Der'

如果传递给 inspect.getargspec 的参数不是方法或函数,它会抛出一个TypeError 的异常这样我们就可以通过 try/except声明发现这种情况,如果确实有异常发生,我们只需在except子句中放入一个什么也不做的pass声明将其忽略即可。为了简化代码,我们并没有首先用hasattr 进行检查工作,而是用带有第三个参数的getattr 来获取“met”属性。因此,如果超类并没有任何一个叫做“met”的属性,m 会被设置成None,如果我们用 None 作为inspect.getargspec 的参数,同样会引发TypeError 异常——这样起到了一石二鸟的作用。如果在 ty子句中对inspect.getargspec 的调用没有引发 TypeError 异常,程序会继续执行 else 子句。

如果inspect.getargspec没有抛出TypeError,它会返回一个含有四个子项的元组,我们可以将各项绑定到一个本地名。在这里,我们关心的是 args,它是m的参数名列表,以及 defaults,一个 m 为它的参数提供的默认值元组。很显然,当且仅当 m 为它的参数提供了默认值时,我们才可以不使用参数调用 m。因此,通过比较args 列表和 defaults元组的长度,我们可以知道默认值的数目是否和参数的数目一致,只要两者一致我们就可以调用 m。

很显然,在大多数的情况下没必要使用这种高级的内省技术并进行细致地检查,但如果你确实需要这种能力,Python 提供了所有需要的技术。

相关文章:

  • QT对象树
  • pip安装包时网络不畅,替换国内PyPI镜像源
  • Seata TCC 实战笔记:从零搭建分布式事务 Demo (含源码)
  • Android 常用输入控件
  • 6.城市综合管廊工程
  • FastApi快速实践
  • 一键获取当前项目的所有文件结构并保存到文本文件
  • ​​工业机器人智能编程:从示教器到AI自主决策​​
  • 雅思听力--75个重点单词/词组
  • 在JSP写入Text文件方法指南
  • go语言实现用户管理系统
  • 【2025软考高级架构师】——2024年11月份真题与解析
  • 使用 OpenCV 和 Dlib实现轮廓绘制
  • 在写setup时遇到的问题与思考
  • 【2025软考高级架构师】——知识脑图总结
  • 管理配置信息和敏感信息
  • 【2025最新】Baichuan-M1-instruct部署教程
  • CPU缓存
  • 湖北理元理律师事务所:债务优化的合规化探索
  • 【大模型架构-Transformer、Mamba、Hyena】
  • G40迎来返程大车流,今明两日预计超13万辆车经长江隧桥进沪
  • 韩国前国务总理韩德洙正式宣布参加总统选举
  • 美国经济萎缩意味着什么?关税政策如何反噬经济?
  • 沈晓萍︱严金清:比斯坦因更早获得敦煌文物的无锡名士
  • 向左繁华都市,向右和美乡村,嘉兴如何打造城乡融合发展样本
  • 马上评|启动最高层级医政调查,维护医学一方净土