Python扩展方法一二事

前言

跟着一个有强迫症的老板干活是一件极其幸福的事情(你懂的)。最近碰到一个问题,简单的说就是对一个对象做出部分修改后仍然返回此对象,于是我就写了一个方法,老板看了之后只有一句话:不雅观,改成直接对此对象调用此方法。我脑海里千万个不情愿,然而没有办法,不得不低头,精通C#、Java、Scala等多种语言HelloWorld的我,一想便知这是扩展方法。于是开始Google之,看似简单的问题,其实里面也有一些细节需要注意,在此记录之。

Level 1

原理很简单,将方法的第一个对象改成self(self即为需要扩展方法的对象的实例),其余不变,无需返回此对象,在此方法外部将此方法赋给此类。当需要调用的时候引入此包即可。大致如下:

class A():
    def m2():
        ...
        
    ...
    
def m1(self):
    // TODO: Do sth to this instance.
    ...
    
A.m1 = m1

a = A()
a.m1()

当然m1方法也可以添加其他参数,只需要放在self后面即可。

Level 2

过了几天,老板又提了一个新的需求:某个类有一个m1方法,但是老板想修改此方法,使其更加完善。看上去没有什么难的吗,同样是个扩展方法,于是我写代码如下:

class A():
    def m1():
        ...
       
    def m2():
        ...
    ...
    
def m1(self):
    // TODO: Do sth to this instance.
    self.m1()
    ...
    
A.m1 = m1

a = A()
a.m1()

满心欢喜本以为这么简单的就解决了问题。调试,悲剧的是直接报错,错误为递归调用。细细想来确实如此,在此作用域内直接对self对象调用m1方法,应当是已经扩展后的方法,那么当然会产生循环调用的问题。

我想到应当可以通过先修改类中m1方法名称来解决此问题,但是具体不知道如何操作,于是在StackOverflow中提了个问题,很快就有老外大牛回复了。见https://stackoverflow.com/questions/46460250/how-to-call-a-method-that-has-been-overridden-by-an-extension-method#。所以问题的核心在于我们可以通过下述代码定义一个类的方法:

_m1 = A.m1

同样调用的时候将实例作为第一个参数传入。于是代码进行如下改造即可:

class A():
    def m1():
        ...
       
    def m2():
        ...
    ...

_m1 = A.m1

def m1(self):
    // TODO: Do sth to this instance.
    _m1(self)
    ...
    
A.m1 = m1

a = A()
a.m1()

这样即解决了递归调用的问题,但是此处有一个细节需要注意,_m1必须定义在m1方法上部,由于_m1是定义在m1扩展方法之上的,所以此处仍是A类中的m1方法,若是定义在m1扩展方法中,则又会出现递归调用的问题。

Level 3

下午看了一下上面在StackOverflow中提的问题,又有一个新的答案,分析了一下他主要介绍了两点知识,覆盖类的方法和覆盖实例的方法,即扩展方法对整个类生效和只对某个实例生效。

对整个类扩展

除了直接写A.plot = plot外,还可以写成:

setattr(A, 'plot', plot)

A代表需要扩展的类,'plot'为扩展后的方法名,plot为重写的扩展方法。当然如果扩展后的方法名在原类中已有,则覆盖之;若无则为新的方法。

对具体实例扩展

让我比较意外的是python可以对某个实例进行方法扩展,这在其他语言中似乎是基本没有的。

当然我们不能用a.plot = plot的形式为a这个A的实例扩展方法,但是可以通过下述方式对a扩展方法:

a.plot = types.MethodType(plot, a)

这样只有a有此扩展方法,或者是只有a的此方法被修改了,其他实例并不受影响。

总结

本文简单记录了Python扩展方法实现方式、类的方法的重定义、实例方法扩展等细节,供需查看。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IMWeb前端团队

代码写了那么多,你搞明白yield是个啥没?

本文作者:IMWeb 否子戈 原文出处:IMWeb社区 未经同意,禁止转载 ES6真是颠覆JavaScript的东西,随便翻一个新特性出来,就让自以为...

2129
来自专栏我的博客

PHP7特性

1、标量类型声明 2、返回值类型声明 3、null合并运算符 $username = $_GET[‘user’] ?? ‘nobody’; $usern...

3645
来自专栏陈树义

藏在正则表达式里的陷阱

前几天线上一个项目监控信息突然报告异常,上到机器上后查看相关资源的使用情况,发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具,我们...

84021
来自专栏喵了个咪的博客空间

zephir-(12)php函数和异常处理

#zephir-php函数和异常处理# ? ##前言## 先在这里感谢各位zephir开源技术提供者 经过了一个多月的学习,zephir的文档译文和基础讲解也将...

3576
来自专栏web前端教室

js不好学并不是因为它难,而是因为它容易混淆

这段时间我主讲的前端零基础课,让我感觉js这东西是越来越简单了。你当然可以说我这是越讲课越熟。确实是有这部分因素,但更主要的是,我发现js中虽然有许多的概念各不...

2487
来自专栏企鹅号快讯

verilog编程要素整理时刻牢记

verilog编程建议 1、不使用初始化语句; 2、不使用延时语句; 3、不使用循环次数不确定的语句,如:forever,while等; 4、尽量采用同步方式设...

1948
来自专栏游戏杂谈

Objective-C 入门笔记

既然有头文件,所以很多人会拿它与C++进行类比,它最重要的三个特性是:Message、Category、Protocol,与C++类比,二者之间的不同:

1033
来自专栏智能大石头

深度解析C++拷贝构造函数

自2003年开始,断断续续用了12年C++,直到这两年做物联网嵌入式开发,感觉对C++的掌握仅有10%左右。 习惯了C#开发,C++倒显得难以下手!今天就一个函...

2099
来自专栏达观数据

达观数据前端分享:理解 JavaScript 中的对象的属性

在达观数据的前端工作中,对象的属性是经常接触和使用的,正好最近重温了一下《JavaScript 高级程序设计》,把书中理解对象属性的部分整理一下与大家分享。 ...

3409
来自专栏Java技术栈

44个Java代码性能优化总结

代码优化的最重要的作用应该是:避免未知的错误。在代码上线运行的过程中,往往会出现很多我们意想不到的错误,因为线上环境和开发环境是非常不同的,错误定位到最后往往是...

40712

扫码关注云+社区

领取腾讯云代金券