Python和self参数

4 投票
5 回答
4263 浏览
提问于 2025-04-16 09:14

我在使用Python的时候遇到了一些关于self参数的问题,这让我感到有点烦恼,所以我想问问懂行的人。我有一个类,叫做Foo。这个类里面会有很多方法,比如m1mN。对于其中一些方法,我会用标准的定义方式,就像下面的m1那样。但对于其他一些方法,直接把方法名赋值给它们会更方便,就像我对m2m3所做的那样。

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 个回答

2

我觉得你在找的是staticmethod()。你可以在文档中查看相关内容,点击这里

m2 = staticmethod(os.access)
m3 = staticmethod(myfun)

至于为什么在你的例子中m2可以用而m3不行,这点我也不太清楚。在你原来的例子中,打印f.m2和f.m3可以发现,f.m2直接指向了内置函数os.access,而f.m3则是一个绑定的方法(绑定到了实例f上)。

4

os.access() 是一个内置函数,意思是它是用C语言写的一个扩展模块的一部分。当这个类被定义时,Python并不把 m2 识别为一个方法,因为它的类型不对——方法应该是Python函数,而不是C函数。不过,m3 是一个Python函数,所以它被识别为方法,并且会按照方法来处理。

换句话说,这里特别的其实是 m2,而不是 m3

想要实现你想要的功能,一个简单的方法是把 m3 定义为静态方法:

m3 = staticmethod(myfun)

这样一来,解释器就知道在调用 m3 作为 Foo 对象的方法时,永远不需要给 myfun() 传递一个 self 参数。

2

我想补充一点,行为并不是不一致的,正如卢克之前提到的。

你可以试试下面的代码:

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

正如之前提到的,这种绑定机制也可以通过使用静态方法描述符来避免:

http://docs.python.org/library/functions.html#staticmethod

撰写回答