Python和self参数
我在使用Python的时候遇到了一些关于self参数的问题,这让我感到有点烦恼,所以我想问问懂行的人。我有一个类,叫做Foo
。这个类里面会有很多方法,比如m1
到mN
。对于其中一些方法,我会用标准的定义方式,就像下面的m1
那样。但对于其他一些方法,直接把方法名赋值给它们会更方便,就像我对m2
和m3
所做的那样。
import os
def myfun(x, y):
return x + y
class Foo():
def m1(self, y, z):
return y + z + 42
m2 = os.access
m3 = myfun
f = Foo()
print f.m1(1, 2)
print f.m2("/", os.R_OK)
print f.m3(3, 4)
我知道os.access
这个函数似乎不需要self
参数。而且它在这种赋值方式下也没有问题。不过,我不能对我自己的模块这样做(想象一下myfun
是在mymodule.myfun
里定义的)。运行上面的代码会得到以下输出:
3
True
Traceback (most recent call last):
File "foo.py", line 16, in <module>
print f.m3(3, 4)
TypeError: myfun() takes exactly 2 arguments (3 given)
问题是,由于我工作的框架,我至少需要有一个Foo
类。但我不想把我的mymodule
的东西放在一个无用的类里。为了做到这一点,我需要做一些类似于
def m3(self,a1, a2):
return mymodule.myfun(a1,a2)
这样的方法,这样做在有20个方法的时候就显得特别冗余。所以,我想问的是,要么我该如何用一种完全不同且明显更聪明的方式来实现,要么我该如何让我的模块像内置的模块那样工作,这样它就不会抱怨多了一个参数。
5 个回答
我觉得你在找的是staticmethod()。你可以在文档中查看相关内容,点击这里。
m2 = staticmethod(os.access)
m3 = staticmethod(myfun)
至于为什么在你的例子中m2可以用而m3不行,这点我也不太清楚。在你原来的例子中,打印f.m2和f.m3可以发现,f.m2直接指向了内置函数os.access,而f.m3则是一个绑定的方法(绑定到了实例f上)。
os.access()
是一个内置函数,意思是它是用C语言写的一个扩展模块的一部分。当这个类被定义时,Python并不把 m2
识别为一个方法,因为它的类型不对——方法应该是Python函数,而不是C函数。不过,m3
是一个Python函数,所以它被识别为方法,并且会按照方法来处理。
换句话说,这里特别的其实是 m2
,而不是 m3
。
想要实现你想要的功能,一个简单的方法是把 m3
定义为静态方法:
m3 = staticmethod(myfun)
这样一来,解释器就知道在调用 m3
作为 Foo 对象的方法时,永远不需要给 myfun()
传递一个 self
参数。
我想补充一点,行为并不是不一致的,正如卢克之前提到的。
你可以试试下面的代码:
print Foo.__dict__
{'__doc__': None,
'__module__': '__main__',
'm1': <function m1 at 0x02861630>,
'm2': <built-in function access>,
'm3': <function myfun at 0x028616F0>}
在这里你可以看到,Python无法区分m1和m2。这就是为什么它们都被评估为一个绑定方法。
绑定方法就像是一个方法,它多了一个指向对象的第一个参数:self.m(1, 2) -> m(self, 1, 2)
这种绑定行为只适用于用户定义的方法。这就解释了为什么self.m2("/", os.R_OK)
没有被评估为m2(self, "/", os.R_OK)
。
最后再演示一下:
print Foo.m1
<unbound method Foo.m1>
print Foo.m2
<built-in function access>
print f.m1
<bound method Foo.m1 of <__main__.Foo instance at 0x02324418>>
print f.m2
<built-in function access>
关于不同函数类型的更多信息可以在这里找到:
http://docs.python.org/reference/datamodel.html
正如之前提到的,这种绑定机制也可以通过使用静态方法描述符来避免: