问题: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
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_method
而MyClass().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.