下面的类方法有什么区别?

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

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

当前回答

上面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解释器调用CPO的__get__()方法来访问它包含的值。

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

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

如。

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

在哪里

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

并非所有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)中的self参数是必需的,因为它的方法签名实际上是这个C.__dict__['foo']。__get__(c, c)(如上所述,c不需要,因为它可以被找到或多态) 这也是为什么如果你不传递所需的实例参数,你会得到一个TypeError。

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

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

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

在新包装的CPO foo中缺少上下文不会抛出错误,可以通过以下方式验证:

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

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

如果可能的话,写静态方法比写实例方法更好,当然除非你需要上下文化这些方法(比如访问实例变量,类变量等)。一个原因是通过不保留不必要的对象引用来简化垃圾收集。

其他回答

未绑定的方法

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

绑定的方法

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

正如这里的文档所述,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应该是相同的方法。”是的!

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

为了让它工作,你可以把这个附加到你的类定义:

method_two = staticmethod(method_two)

或者您可以使用@staticmethod函数装饰器。

一旦你了解了描述符系统的基础知识,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__。听起来很复杂,但事实并非如此。在这种特殊情况下,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>

希望这能解释清楚。

method_two的定义无效。当你调用method_two时,你会得到TypeError: method_two()接受0个位置参数,但1个是由解释器给出的。

当您像a_test.method_two()那样调用实例方法时,它是一个有界函数。它自动接受指向Test实例的self作为第一个参数。通过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解释器调用CPO的__get__()方法来访问它包含的值。

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

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

如。

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

在哪里

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

并非所有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)中的self参数是必需的,因为它的方法签名实际上是这个C.__dict__['foo']。__get__(c, c)(如上所述,c不需要,因为它可以被找到或多态) 这也是为什么如果你不传递所需的实例参数,你会得到一个TypeError。

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

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

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

在新包装的CPO foo中缺少上下文不会抛出错误,可以通过以下方式验证:

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

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

如果可能的话,写静态方法比写实例方法更好,当然除非你需要上下文化这些方法(比如访问实例变量,类变量等)。一个原因是通过不保留不必要的对象引用来简化垃圾收集。