问题: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()

What is the difference between the following class methods?

Is it that one is static and the other is not?

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()

回答 0

在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

In Python, there is a distinction between bound and unbound methods.

Basically, a call to a member function (like method_one), a bound function

a_test.method_one()

is translated to

Test.method_one(a_test)

i.e. a call to an unbound method. Because of that, a call to your version of method_two will fail with a 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) 

You can change the behavior of a method using a decorator

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

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

The decorator tells the built-in default metaclass type (the class of a class, cf. this question) to not create bound methods for method_two.

Now, you can invoke static method both on an instance or on the class directly:

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

回答 1

一旦您了解了描述符系统的基础知识,Python中的方法就非常简单。想象一下以下类:

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

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

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

如您所见,如果您访问foo类的属性,则会返回一个未绑定的方法,但是在类存储(字典)中有一个函数。为什么?这样做的原因是您的类的类实现了__getattribute__解析描述符的a。听起来很复杂,但事实并非如此。 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会这样做?因为方法对象将函数的第一个参数绑定到类的实例。那就是自我的来源。现在有时候您不希望您的类使函数成为方法,而这正是其中的staticmethod作用:

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

staticmethod装饰包装类并实现了虚拟__get__返回包装的功能函数而不是作为一个方法:

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

希望能解释一下。

Methods in Python are a very, very simple thing once you understood the basics of the descriptor system. Imagine the following class:

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

Now let’s have a look at that class in the shell:

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

As you can see if you access the foo attribute on the class you get back an unbound method, however inside the class storage (the dict) there is a function. Why’s that? The reason for this is that the class of your class implements a __getattribute__ that resolves descriptors. Sounds complex, but is not. C.foo is roughly equivalent to this code in that special case:

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

That’s because functions have a __get__ method which makes them descriptors. If you have an instance of a class it’s nearly the same, just that None is the class instance:

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

Now why does Python do that? Because the method object binds the first parameter of a function to the instance of the class. That’s where self comes from. Now sometimes you don’t want your class to make a function a method, that’s where staticmethod comes into play:

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

The staticmethod decorator wraps your class and implements a dummy __get__ that returns the wrapped function as function and not as a method:

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

Hope that explains it.


回答 2

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

为了使它真正起作用,可以将其附加到类定义中:

method_two = staticmethod(method_two)

或者您可以使用@staticmethod 功能装饰器

When you call a class member, Python automatically uses a reference to the object as the first parameter. The variable self actually means nothing, it’s just a coding convention. You could call it gargaloo if you wanted. That said, the call to method_two would raise a TypeError, because Python is automatically trying to pass a parameter (the reference to its parent object) to a method that was defined as having no parameters.

To actually make it work, you could append this to your class definition:

method_two = staticmethod(method_two)

or you could use the @staticmethod function decorator.


回答 3

>>> 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
>>> 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

回答 4

method_two将不起作用,因为您正在定义成员函数,但没有告诉它该函数属于哪个成员。如果执行最后一行,则会得到:

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

如果要为类定义成员函数,则第一个参数必须始终为“ self”。

method_two won’t work because you’re defining a member function but not telling it what the function is a member of. If you execute the last line you’ll get:

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

If you’re defining member functions for a class the first argument must always be ‘self’.


回答 5

上面的阿明·罗纳彻(Armin Ronacher)的准确解释,扩展了他的答案,以便像我这样的初学者很好地理解:

在类中定义的方法的不同之处在于,无论是静态方法还是实例方法(还有另一种类型-类方法-此处未讨论,因此将其略过)都在于它们是否以某种方式绑定到类实例的事实。例如,说该方法在运行时是否收到对类实例的引用

class C:
    a = [] 
    def foo(self):
        pass

C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C() 
c # this is the class instance

__dict__类对象的dictionary属性保存对类对象的所有属性和方法的引用,因此

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

可以通过上面的方法访问foo方法。这里要注意的重要一点是python中的所有内容都是一个对象,因此上面字典中的引用本身指向其他对象。让我称它们为“类属性对象”-或简称为CPO。

如果CPO是描述符,则python解释器将调用__get__()CPO 的方法以访问其包含的值。

为了确定CPO是否是描述符,python解释器检查它是否实现了描述符协议。实现描述符协议就是实现3种方法

def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)

例如

>>> C.__dict__['foo'].__get__(c, C)

哪里

  • self 是CPO(可以是list,str,function等的实例),由运行时提供
  • instance 是定义此CPO的类的实例(上面的对象’c’),需要由我们明确提供
  • owner是定义此CPO的类(上面的类对象’C’),需要由我们提供。但这是因为我们在CPO上调用它。当我们在实例上调用它时,我们不需要提供它,因为运行时可以提供实例或其类(多态)
  • value 是CPO的预期值,需要由我们提供

并非所有的CPO都是描述符。例如

>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510> 
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'

这是因为列表类未实现描述符协议。

因此自变量c.foo(self)是必需的,因为它的方法签名实际上就是这个C.__dict__['foo'].__get__(c, C)(如上所述,不需要C,因为它可以被发现或多态),这也是为什么如果不传递所需的实例参数就会得到TypeError的原因。

如果您注意到该方法仍通过类Object C进行引用,则通过将实例对象形式的上下文传递给该函数来实现与类实例的绑定。

这非常棒,因为如果您选择不保留上下文或不绑定到实例,则所需要做的只是编写一个类来包装描述符CPO并重写其__get__()方法以不需要上下文。这个新类称为装饰器,通过关键字应用@staticmethod

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

新包装的CPO中foo没有上下文不会引发错误,可以通过以下方式进行验证:

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

静态方法的用例更多是一种命名空间和代码可维护性(将其从类中取出并在整个模块中使用)。

只要有可能,最好编写静态方法而不是实例方法,除非您当然需要使方法复杂化(例如访问实例变量,类变量等)。原因之一是通过不保留对对象的不必要引用来简化垃圾回收。

Accurate explanation from Armin Ronacher above, expanding on his answers so that beginners like me understand it well:

Difference in the methods defined in a class, whether static or instance method(there is yet another type – class method – not discussed here so skipping it), lay in the fact whether they are somehow bound to the class instance or not. For example, say whether the method receives a reference to the class instance during runtime

class C:
    a = [] 
    def foo(self):
        pass

C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C() 
c # this is the class instance

The __dict__ dictionary property of the class object holds the reference to all the properties and methods of a class object and thus

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

the method foo is accessible as above. An important point to note here is that everything in python is an object and so references in the dictionary above are themselves pointing to other objects. Let me call them Class Property Objects – or as CPO within the scope of my answer for brevity.

If a CPO is a descriptor, then python interpretor calls the __get__() method of the CPO to access the value it contains.

In order to determine if a CPO is a descriptor, python interpretor checks if it implements the descriptor protocol. To implement descriptor protocol is to implement 3 methods

def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)

for e.g.

>>> C.__dict__['foo'].__get__(c, C)

where

  • self is the CPO (it could be an instance of list, str, function etc) and is supplied by the runtime
  • instance is the instance of the class where this CPO is defined (the object ‘c’ above) and needs to be explicity supplied by us
  • owner is the class where this CPO is defined(the class object ‘C’ above) and needs to be supplied by us. However this is because we are calling it on the CPO. when we call it on the instance, we dont need to supply this since the runtime can supply the instance or its class(polymorphism)
  • value is the intended value for the CPO and needs to be supplied by us

Not all CPO are descriptors. For example

>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510> 
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'

This is because the list class doesnt implement the descriptor protocol.

Thus the argument self in c.foo(self) is required because its method signature is actually this C.__dict__['foo'].__get__(c, C) (as explained above, C is not needed as it can be found out or polymorphed) And this is also why you get a TypeError if you dont pass that required instance argument.

If you notice the method is still referenced via the class Object C and the binding with the class instance is achieved via passing a context in the form of the instance object into this function.

This is pretty awesome since if you chose to keep no context or no binding to the instance, all that was needed was to write a class to wrap the descriptor CPO and override its __get__() method to require no context. This new class is what we call a decorator and is applied via the keyword @staticmethod

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

The absence of context in the new wrapped CPO foo doesnt throw an error and can be verified as follows:

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

Use case of a static method is more of a namespacing and code maintainability one(taking it out of a class and making it available throughout the module etc).

It maybe better to write static methods rather than instance methods whenever possible, unless ofcourse you need to contexualise the methods(like access instance variables, class variables etc). One reason is to ease garbage collection by not keeping unwanted reference to objects.


回答 6

那是一个错误。

首先,第一行应该是这样的(注意大写)

class Test(object):

每当您调用类的方法时,它都会将其自身作为第一个参数(因此命名为self),并且method_two给出此错误

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

that is an error.

first of all, first line should be like this (be careful of capitals)

class Test(object):

Whenever you call a method of a class, it gets itself as the first argument (hence the name self) and method_two gives this error

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

回答 7

第二个不起作用,因为当您像这样调用它时,python内部尝试使用a_test实例作为第一个参数来调用它,但是您的method_two不接受任何参数,因此它将不起作用,您将获得运行时错误。如果要使用静态方法的等效项,则可以使用类方法。与像Java或C#这样的语言中的静态方法相比,Python中对类方法的需求要少得多。通常,最好的解决方案是在模块中使用类定义之外的方法,这些方法比类方法更有效。

The second one won’t work because when you call it like that python internally tries to call it with the a_test instance as the first argument, but your method_two doesn’t accept any arguments, so it wont work, you’ll get a runtime error. If you want the equivalent of a static method you can use a class method. There’s much less need for class methods in Python than static methods in languages like Java or C#. Most often the best solution is to use a method in the module, outside a class definition, those work more efficiently than class methods.


回答 8

对method_two的调用将因不接受self参数而引发异常,Python运行时将自动传递该参数。

如果要在Python类中创建静态方法,请使用修饰它staticmethod decorator

Class Test(Object):
  @staticmethod
  def method_two():
    print "Called method_two"

Test.method_two()

The call to method_two will throw an exception for not accepting the self parameter the Python runtime will automatically pass it.

If you want to create a static method in a Python class, decorate it with the staticmethod decorator.

Class Test(Object):
  @staticmethod
  def method_two():
    print "Called method_two"

Test.method_two()

回答 9

请从Guido First Class阅读此文档,一切都清楚地说明了Unbound,Bound方法是如何诞生的。

Please read this docs from the Guido First Class everything Clearly explained how Unbound, Bound methods are born.


回答 10

的定义method_two无效。调用时method_two,您将获得TypeError: method_two() takes 0 positional arguments but 1 was given翻译服务。

当实例方法调用为时,实例方法是有界函数a_test.method_two()。它会自动接受self,它指向的实例Test,作为其第一个参数。通过该self参数,实例方法可以自由访问属性并在同一对象上对其进行修改。

The definition of method_two is invalid. When you call method_two, you’ll get TypeError: method_two() takes 0 positional arguments but 1 was given from the interpreter.

An instance method is a bounded function when you call it like a_test.method_two(). It automatically accepts self, which points to an instance of Test, as its first parameter. Through the self parameter, an instance method can freely access attributes and modify them on the same object.


回答 11

未绑定方法

未绑定方法是尚未绑定到任何特定类实例的方法。

绑定方法

绑定方法是绑定到类的特定实例的方法。

如此处所述,self可以根据函数是绑定,未绑定还是静态来引用不同的事物。

看下面的例子:

class MyClass:    
    def some_method(self):
        return self  # For the sake of the example

>>> MyClass().some_method()
<__main__.MyClass object at 0x10e8e43a0># This can also be written as:>>> obj = MyClass()

>>> obj.some_method()
<__main__.MyClass object at 0x10ea12bb0>

# Bound method call:
>>> obj.some_method(10)
TypeError: some_method() takes 1 positional argument but 2 were given

# WHY IT DIDN'T WORK?
# obj.some_method(10) bound call translated as
# MyClass.some_method(obj, 10) unbound method and it takes 2 
# arguments now instead of 1 

# ----- USING THE UNBOUND METHOD ------
>>> MyClass.some_method(10)
10

由于我们在obj上一次调用中没有使用类实例,因此我们可以说它看起来像一个静态方法。

如果是这样,则MyClass.some_method(10)调用与使用@staticmethod装饰器装饰的静态函数的调用之间有什么区别?

通过使用装饰器,我们明确表示将使用该方法,而无需先为其创建实例。通常,人们不会期望在没有实例的情况下使用类成员方法,而根据方法的结构,访问它们可能导致可能的错误。

同样,通过添加@staticmethod装饰器,我们也可以通过一个对象来访问它。

class MyClass:    
    def some_method(self):
        return self    

    @staticmethod
    def some_static_method(number):
        return number

>>> MyClass.some_static_method(10)   # without an instance
10
>>> MyClass().some_static_method(10)   # Calling through an instance
10

您不能使用实例方法来执行上述示例。您可以在第一个参数中幸存下来(就像我们之前所做的那样),但是第二个参数将被转换为一个未绑定的调用MyClass.some_method(obj, 10),这将引发一个,TypeError因为实例方法接受一个参数,而您无意间尝试传递两个参数。

然后,你可能会说,“如果我可以调用通过两个实例和一个类的静态方法,MyClass.some_static_methodMyClass().some_static_method应该是相同的方法。” 是!

Unbound Methods

Unbound methods are methods that are not bound to any particular class instance yet.

Bound Methods

Bound methods are the ones which are bound to a specific instance of a class.

As its documented here, self can refer to different things depending on the function is bound, unbound or static.

Take a look at the following example:

class MyClass:    
    def some_method(self):
        return self  # For the sake of the example

>>> MyClass().some_method()
<__main__.MyClass object at 0x10e8e43a0># This can also be written as:>>> obj = MyClass()

>>> obj.some_method()
<__main__.MyClass object at 0x10ea12bb0>

# Bound method call:
>>> obj.some_method(10)
TypeError: some_method() takes 1 positional argument but 2 were given

# WHY IT DIDN'T WORK?
# obj.some_method(10) bound call translated as
# MyClass.some_method(obj, 10) unbound method and it takes 2 
# arguments now instead of 1 

# ----- USING THE UNBOUND METHOD ------
>>> MyClass.some_method(10)
10

Since we did not use the class instance — obj — on the last call, we can kinda say it looks like a static method.

If so, what is the difference between MyClass.some_method(10) call and a call to a static function decorated with a @staticmethod decorator?

By using the decorator, we explicitly make it clear that the method will be used without creating an instance for it first. Normally one would not expect the class member methods to be used without the instance and accesing them can cause possible errors depending on the structure of the method.

Also, by adding the @staticmethod decorator, we are making it possible to be reached through an object as well.

class MyClass:    
    def some_method(self):
        return self    

    @staticmethod
    def some_static_method(number):
        return number

>>> MyClass.some_static_method(10)   # without an instance
10
>>> MyClass().some_static_method(10)   # Calling through an instance
10

You can’t do the above example with the instance methods. You may survive the first one (as we did before) but the second one will be translated into an unbound call MyClass.some_method(obj, 10) which will raise a TypeError since the instance method takes one argument and you unintentionally tried to pass two.

Then, you might say, “if I can call static methods through both an instance and a class, MyClass.some_static_method and MyClass().some_static_method should be the same methods.” Yes!


回答 12

绑定方法=实例方法

未绑定方法=静态方法。

Bound method = instance method

Unbound method = static method.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。