相信很多朋友在编程的时候都会想修改一下已经写好的程序行为代码,而最常见的方式就是通过子类来重写父类的一些不满足需求的方法。比如说下面这个例子。

class Dog:
def bark(self):
print 'Woof!' class Husky(Dog):
def bark(self)
print 'Howl!'

我们可以用上述方式来修改我们自己写的代码,但是我们应该怎么修改第三方代码呢?当然,我们也可以自己编写一个子类,调用子类的实例对象来实现修改,但是这样可能会引入其他一系列问题。所以我们得想个办法用我们自己的方法替换掉原来的对象方法,这就是本文接下来要介绍的“打补丁”的方式。

给类打补丁

如果我们想新增或是修改对象的方法的话,最简单的方式莫过于给类打个补丁了。结合上面的例子,如果我们想给我们自己的 Dog 类写一个新的 howl 方法的话,我们可以定义一个新的 howl 函数,像下面的代码一样把它添加到我们的类中:

def newbark(self):
print 'Wrooof!' def howl(self):
print 'Howl!' # Replace an existing method
Dog.bark = newbark # Add a new method
Dog.howl = howl

很简单吧?但是这里有几个问题需要我们注意。首先,被修改的类的所有实例中的方法都会被更新,所以更新后的方法不仅仅存在于新创建的对象中,之前创建的所有对象都会拥有更新之后的方法,除非只是新增而不是覆盖掉原来的方法。第二,你修改或者新增的方法应当是与对象绑定的,所以方法的第一个参数应当是被调用的对象(在这里就是类的实例self)。

给类实例打补丁

单个对象也可以在不影响这个类的其他实例的情况下打补丁。但是还是有点小技巧的哦!先让我们看看下面这个例子。

def herd(self, sheep):
self.run()
self.bark()
self.run() border_collie = Dog()
border_collie.herd = herd

然后我们再试试调用新定义的方法:

border_collie.herd(sheep)

TypeError: herd() takes exactly 2 arguments (1 given)
The problem with the previous code is that the herd is not a bound method, just take a look at the following code: print border_collie.herd <function herd at 0xf9c5f0>

出错啦!引发错误的原因就是被调用的对象并没有作为第一个参数传给我们写的函数。当然我们可以自己把参数传进去,但是在这个替换类方法的场景下并不奏效。解决这个问题的正确方案是用 type 这个模块里的 MethodType 函数,我们可以看看下面的示例代码:

import types

border_collie = Dog()
border_collie.herd = types.MethodType(herd, border_collie) print border_collie.herd
<bound method ?.herd of <__main__.Dog instance at 0x23c9518>> border_collie.herd(sheep)

现在我们的方法已经和实例绑定了,大功告成!

总结

运行中替换或者添加方法是非常有用的,比如说在单元测试中,有些负责和外界服务通信的函数就需要替换掉,方便测试。这个技巧不仅很常用,而且在你最终决定要修改代码之前还可以保持代码的可维护性,是一个非常重要的技巧。

动态修改Python类和实例的方法(转)的更多相关文章

  1. 第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获

    一. 引言 在<第7.23节 Python使用property函数定义属性简化属性访问的代码实现>和<第7.26节 Python中的@property装饰器定义属性访问方法gette ...

  2. 第8.6节 Python类中的__new__方法深入剖析:调用父类__new__方法参数的困惑

    上节<第8.5节 Python类中的__new__方法和构造方法__init__关系深入剖析:执行顺序及参数关系案例详解>通过案例详细分析了两个方法的执行顺序,不知大家是否注意到了,在上述 ...

  3. python 动态修改 类和实例 的方法

    相信很多朋友在编程的时候都会想修改一下已经写好的程序行为代码,而最常见的方式就是通过子类来重写父类的一些不满足需求的方法.比如说下面这个例子. class Dog: def bark(self): p ...

  4. Python 面向对象基础(类、实例、方法、属性封装)

    python是面向对象语言,一切皆对象. 面向过程: 变量和函数. “散落” 在文件的各个位置,甚至是不同文件中.看不出变量与函数的相关性,非常不利于维护,设计模式不清晰. 经常导致程序员,忘记某个变 ...

  5. Python类的实例属性详解

    实例属性 1.类被实例化后才会具有的属性 2.一般在_init_()方法中创建并初始化 3.直接使用即定义:self.<属性名> 4.引用方法:self.<属性名> 5.sel ...

  6. python类:magic魔术方法

    http://blog.csdn.net/pipisorry/article/details/50708812 魔术方法是面向对象Python语言中的一切.它们是你可以自定义并添加"魔法&q ...

  7. (转)python类:magic魔术方法

    原文:https://blog.csdn.net/pipisorry/article/details/50708812 版权声明:本文为博主皮皮http://blog.csdn.net/pipisor ...

  8. python 类、对象、方法、属性

    在python中,一个对象的特征也称为属性(attribute).它所具有的行为也称为方法(method) 结论:对象=属性+方法 在python中,把具有相同属性和方法的对象归为一个类(class) ...

  9. python类、对象、方法、属性之类与对象笔记

    python中一切皆为对象,所谓对象:我自己就是一个对象,我玩的电脑就是对象,坐着的椅子就是对象,家里养的小狗也是一个对象...... 我们通过描述属性(特征)和行为来描述一个对象的.比如家里的小狗, ...

随机推荐

  1. JUC回顾之-线程池的原理和使用

    Java并发编程:线程池的使用   Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程 ...

  2. mybatis传递Map和List集合示例

    1.List示例 java文件: dao: public List<ServicePort> selectByIps(List<String> ips); xml文件: < ...

  3. catch(…) vs catch(CException *)?

    转自:https://stackoverflow.com/questions/7412185/what-is-the-difference-between-catch-vs-catchcexcepti ...

  4. fireworks切图

    下载安装完成后打开软件 打开一张图片 首选参数的撤销次数改成999 按住空格键 鼠标会变成小手的形状 这时候可以拖拽图像 找到切片工具 记住缩放比例的快捷键 ctrl+空格 放大某个区域 切的时候按住 ...

  5. 启用lazyload插件,减少图片加载

    使用lazyload的js插件,减少图片加载,提高页面加载速度和节省流量.虽然这个头像是第三方服务器来的,不消费博客的流量,但是能节省部分访客的加载时间,也是不错的哦. 用lazyload插件,只是后 ...

  6. centos7 安装zookeeper 集群

    data 创建myid  设置1 zookeeper默认端口2181  同步端口 20881 和 30881 设置zoo.cfg文件

  7. hadoop程序MapReduce之WordCount

    需求:统计一个文件中所有单词出现的个数. 样板:word.log文件中有hadoop hive hbase hadoop hive 输出:hadoop 2 hive 2 hbase 1 MapRedu ...

  8. /etc/motd

    /etc/motd 用于自定义欢迎界面,用法如下: [root@localhost ~]$ cat /etc/motd .=""=. / _ _ \ | d b | \ /\ / ...

  9. linux下串口调试工具/串口终端推荐: picocom(转)

    对于picocom, kermit, minicom, picocom 最简单易用,也完全符合我的使用需求. 安装(mint / ubuntu):$ sudo apt-get install pico ...

  10. JDBC--Result 获取返回集合

    package jdbc.chap05; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql. ...