以下类方法有什么区别?
是不是一个是静态的,另一个不是?
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()
以下类方法有什么区别?
是不是一个是静态的,另一个不是?
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()
在 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
一旦您了解了描述符系统的基础知识,Python 中的方法就是一件非常非常简单的事情。想象一下下面的类:
class C(object):
def foo(self):
pass
现在让我们看看 shell 中的那个类:
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
正如您所看到的,如果您访问foo
类上的属性,您将获得一个未绑定的方法,但是在类存储(dict)内部有一个函数。为什么?这样做的原因是您的类的类实现了__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>
希望能解释一下。
当你调用一个类成员时,Python 会自动使用对该对象的引用作为第一个参数。该变量self
实际上没有任何意义,它只是一个编码约定。如果你愿意,你可以打电话给它gargaloo
。也就是说,调用method_two
会引发 a TypeError
,因为 Python 会自动尝试将参数(对其父对象的引用)传递给定义为没有参数的方法。
要使其真正起作用,您可以将其附加到您的类定义中:
method_two = staticmethod(method_two)
或者你可以使用@staticmethod
函数 decorator。
>>> 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
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”。
上面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__
字典属性保存对类对象的所有属性和方法的引用,因此
>>> 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(它可以是列表、str、函数等的实例)并且由运行时提供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__'
这是因为列表类没有实现描述符协议。
因此,参数 self inc.foo(self)
是必需的,因为它的方法签名实际上是 this 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>
静态方法的用例更多的是命名空间和代码可维护性(将其从类中取出并使其在整个模块中可用等)。
尽可能编写静态方法而不是实例方法可能会更好,除非您当然需要对方法进行上下文化(如访问实例变量、类变量等)。一个原因是通过不保留对对象的不需要的引用来简化垃圾收集。
对 method_two 的调用将抛出一个异常,因为 Python 运行时将自动传递它不接受 self 参数。
如果要在 Python 类中创建静态方法,请使用staticmethod decorator
.
Class Test(Object):
@staticmethod
def method_two():
print "Called method_two"
Test.method_two()
这是一个错误。
首先,第一行应该是这样的(注意大写)
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)
第二个不起作用,因为当你像那样调用它时,python 在内部尝试使用 a_test 实例作为第一个参数来调用它,但是你的 method_two 不接受任何参数,所以它不起作用,你会得到一个运行时错误。如果您想要等效于静态方法,则可以使用类方法。与 Java 或 C# 等语言中的静态方法相比,Python 中对类方法的需求要少得多。大多数情况下,最好的解决方案是在模块中使用类定义之外的方法,这些方法比类方法更有效。
请阅读 Guido First Class 中的所有文档,清楚地解释了 Unbound, Bound 方法是如何诞生的。
的定义method_two
无效。当你打电话时method_two
,你会TypeError: method_two() takes 0 positional arguments but 1 was given
从口译员那里得到。
当您调用实例方法时,实例方法是一个有界函数a_test.method_two()
。它自动接受self
指向 的实例的Test
,作为它的第一个参数。通过self
参数,实例方法可以在同一个对象上自由地访问属性和修改属性。
未绑定方法
未绑定方法是尚未绑定到任何特定类实例的方法。
绑定方法
绑定方法是绑定到类的特定实例的方法。
正如其在此处记录的那样,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)
,这将引发 aTypeError
因为实例方法接受一个参数并且您无意中尝试传递两个参数。
然后,你可能会说,“如果我可以通过实例和类调用静态方法,MyClass.some_static_method
并且MyClass().some_static_method
应该是相同的方法。” 是的!
绑定方法=实例方法
未绑定方法 = 静态方法。