Python中类方法的区别:绑定、未绑定和静态

256 投票
13 回答
162983 浏览
提问于 2025-04-11 09:18

这段话在问以下两种类方法有什么区别。

是不是说一个是静态的,另一个不是呢?

class Test(object):
  def method_one(self):
    print "Called method_one"

  def method_two():
    print "Called method_two"

a_test = Test()
a_test.method_one()
a_test.method_two()

13 个回答

13

当你调用一个类里的成员时,Python会自动把这个对象的引用作为第一个参数传进去。这里的变量self其实没什么特别的意思,它只是个编程习惯。你想叫它gargaloo也可以。不过,调用method_two时会出现TypeError错误,因为Python自动试图把一个参数(指向它父对象的引用)传给一个定义时没有参数的方法。

要让它正常工作,你可以在你的类定义里加上这个:

method_two = staticmethod(method_two)

或者你可以使用@staticmethod这个函数装饰器

206

在Python中,方法其实是个非常简单的概念,只要你理解了描述符系统的基础知识。想象一下下面这个类:

class C(object):
    def foo(self):
        pass

现在我们来看看这个类在命令行中的表现:

>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>

你会发现,当你访问这个类的 foo 属性时,得到的是一个未绑定的方法,但在类的存储(字典)里却有一个函数。为什么会这样呢?原因在于你的类的类实现了一个 __getattribute__ 方法,它会处理描述符。听起来复杂,但其实并不难。C.foo 在这个特殊情况下大致等同于以下代码:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>

这是因为函数有一个 __get__ 方法,使它们成为描述符。如果你有一个类的实例,情况几乎是一样的,只不过 None 是类的实例:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x17bd4d0>>

那么,Python为什么要这样做呢?因为方法对象会将函数的第一个参数绑定到类的实例上。这就是为什么会有 self 这个参数。有时候你可能不想让你的类把一个函数变成方法,这时候 staticmethod 就派上用场了:

 class C(object):
  @staticmethod
  def foo():
   pass

staticmethod 装饰器会包裹你的类,并实现一个虚假的 __get__ 方法,这样返回的就是一个普通的函数,而不是方法:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

希望这样能解释清楚。

437

在Python中,绑定方法未绑定方法是有区别的。

简单来说,当你调用一个成员函数(比如 method_one)时,这个函数是一个绑定方法。

a_test.method_one()

这会被转换成

Test.method_one(a_test)

也就是说,这实际上是调用一个未绑定的方法。因此,如果你调用你自己版本的 method_two,就会出现 TypeError 错误。

>>> a_test = Test() 
>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given) 

你可以通过使用装饰器来改变一个方法的行为。

class Test(object):
    def method_one(self):
        print "Called method_one"

    @staticmethod
    def method_two():
        print "Called method two"

这个装饰器会告诉内置的默认元类 type(类的类,具体可以参考 这个问题),不要为 method_two 创建绑定方法。

现在,你可以直接在实例上或在类上调用静态方法:

>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two

撰写回答