Python中的类方法差异:绑定,未绑定和静态

Fra*_*ard 233 python

以下类方法有什么区别?

是一个是静态而另一个不是?

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()
Run Code Online (Sandbox Code Playgroud)

Tor*_*rek 407

在Python中,绑定未绑定方法之间存在区别.

基本上,调用成员函数(如method_one),绑定函数

a_test.method_one()
Run Code Online (Sandbox Code Playgroud)

被翻译成

Test.method_one(a_test)
Run Code Online (Sandbox Code Playgroud)

即对未绑定方法的调用.因此,对您的版本的调用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) 
Run Code Online (Sandbox Code Playgroud)

您可以使用装饰器更改方法的行为

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

    @staticmethod
    def method_two():
        print "Called method two"
Run Code Online (Sandbox Code Playgroud)

装饰器告诉内置的默认元类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
Run Code Online (Sandbox Code Playgroud)

  • 在python 3中,不推荐使用未绑定的方法.相反,只有一个功能. (23认同)
  • 我支持这个答案,它优于我的.干得好Torsten :) (17认同)

Arm*_*her 189

一旦理解了描述符系统的基础知识,Python中的方法就变得非常非常简单.想象一下以下课程:

class C(object):
    def foo(self):
        pass
Run Code Online (Sandbox Code Playgroud)

现在让我们看一下shell中的那个类:

>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,如果您访问foo该类的属性,则会返回一个未绑定的方法,但是在类存储(dict)中有一个函数.为什么?这样做的原因是你的类的类实现了一个__getattribute__解析描述符.听起来很复杂,但事实并非如此. C.foo在这种特殊情况下大致相当于此代码:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
Run Code Online (Sandbox Code Playgroud)

那是因为函数有一个__get__使它们成为描述符的方法.如果你有一个类的实例几乎相同,那None就是类实例:

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

现在为什么Python会这样做?因为方法对象将函数的第一个参数绑定到类的实例.这就是自我的来源.现在,有时你不希望你的类将函数作为一个方法,这就是staticmethod发挥作用的地方:

 class C(object):
  @staticmethod
  def foo():
   pass
Run Code Online (Sandbox Code Playgroud)

staticmethod装饰包装类,并实现虚拟__get__返回的包裹功能功能,而不是作为一种方法:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Run Code Online (Sandbox Code Playgroud)

希望能够解释它.

  • *`staticmethod`装饰器包装你的类(...)*这个短语有点误导,因为被包装的类是**方法`foo`的类**而不是`foo`的类被定义为. (12认同)
  • 在 Python 3 中,`C.foo 是 C.__dict__['foo']`。来自 [Python 3.0 发行说明](https://docs.python.org/3/whatsnew/3.0.html#operators-and-special-methods):“‘未绑定方法’的概念已从语言中删除.当引用一个方法作为类属性时,您现在得到一个普通的函数对象。” (2认同)

Jus*_*iey 11

当您调用类成员时,Python会自动使用对象的引用作为第一个参数.变量self实际上没有任何意义,它只是一种编码约定.gargaloo如果你愿意,你可以打电话给它.也就是说,调用method_two会引发一个TypeError,因为Python会自动尝试将参数(对其父对象的引用)传递给定义为没有参数的方法.

要实际使其工作,您可以将其附加到您的类定义:

method_two = staticmethod(method_two)
Run Code Online (Sandbox Code Playgroud)

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

  • 你的意思是"@staticmethod函数装饰器语法". (4认同)

kzh*_*kzh 11

>>> class Class(object):
...     def __init__(self):
...         self.i = 0
...     def instance_method(self):
...         self.i += 1
...         print self.i
...     c = 0
...     @classmethod
...     def class_method(cls):
...         cls.c += 1
...         print cls.c
...     @staticmethod
...     def static_method(s):
...         s += 1
...         print s
... 
>>> a = Class()
>>> a.class_method()
1
>>> Class.class_method()    # The class shares this value across instances
2
>>> a.instance_method()
1
>>> Class.instance_method() # The class cannot use an instance method
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method instance_method() must be called with Class instance as first argument (got nothing instead)
>>> Class.instance_method(a)
2
>>> b = 0
>>> a.static_method(b)
1
>>> a.static_method(a.c) # Static method does not have direct access to 
>>>                      # class or instance properties.
3
>>> Class.c        # a.c above was passed by value and not by reference.
2
>>> a.c
2
>>> a.c = 5        # The connection between the instance
>>> Class.c        # and its class is weak as seen here.
2
>>> Class.class_method()
3
>>> a.c
5
Run Code Online (Sandbox Code Playgroud)

  • `Class.instance_method()#该类不能使用它可以使用的实例方法.只需手动传递实例:`Class.instance_method(a)` (2认同)