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中提了个问题,很快就有⽼外⼤⽜回复了。见。所以问题的核⼼在于我们可以通过下述代码定义⼀个类的⽅法:
_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可以对某个实例进⾏⽅法扩展,这在其他语⾔中似乎是基本没有的。
scala python当然我们不能⽤a.plot = plot的形式为a这个A的实例扩展⽅法,但是可以通过下述⽅式对a扩展⽅法:
a.plot = types.MethodType(plot, a)
这样只有a有此扩展⽅法,或者是只有a的此⽅法被修改了,其他实例并不受影响。
总结
本⽂简单记录了Python扩展⽅法实现⽅式、类的⽅法的重定义、实例⽅法扩展等细节,供需查看。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论