方法在Python中是如何工作的
方法就是一个函数,它作为一个类属性而存在,你可以用如下方式来声明、访问一个函数:
>>> class Pizza(object): ... def __init__(self, size): ... self.size = size ... def get_size(self): ... return self.size ... >>> Pizza.get_size <unbound method Pizza.get_size>Python在告诉你,属性get_size是类Pizza的一个未绑定方法。这是什么意思呢?很快我们就会知道答案:
>>> Pizza.get_size() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method get_size() must be called with Pizza instance as first argument (got nothing instead)我们不能这么调用,因为它还没有绑定到Pizza类的任何实例上,它需要一个实例作为第一个参数传递进去(Python2必须是该类的实例,Python中可以是任何东西),尝试一下:
>>> Pizza.get_size(Pizza(42)) 42太棒了,现在用一个实例作为它的的第一个参数来调用,整个世界都清静了,如果我说这种调用方式还不是最方便的,你也会这么认为的;没错,现在每次调用这个方法的时候我们都不得不引用这个类,如果不知道哪个类是我们的对象,长期看来这种方式是行不通的。
那么Python为我们做了什么呢,它绑定了所有来自类Pizza的方法以及该类的任何一个实例的方法。也就意味着现在属性get_size是Pizza的一个实例对象的绑定方法,这个方法的第一个参数就是该实例本身。
>>> Pizza(42).get_size <bound method Pizza.get_size of <__main__.Pizza object at 0x7f3138827910>> >>> Pizza(42).get_size() 42和我们预期的一样,现在不再需要提供任何参数给get_size,因为它已经是绑定的,它的self参数会自动地设置给Pizza实例,下面代码是最好的证明:
>>> m = Pizza(42).get_size >>> m() 42更有甚者,你都没必要使用持有Pizza对象的引用了,因为该方法已经绑定到了这个对象,所以这个方法对它自己来说是已经足够了。
也许,如果你想知道这个绑定的方法是绑定在哪个对象上,下面这种手段就能得知:
>>> m = Pizza(42).get_size >>> m.__self__ <__main__.Pizza object at 0x7f3138827910> >>> # You could guess, look at this: ... >>> m == m.__self__.get_size True显然,该对象仍然有一个引用存在,只要你愿意你还是可以把它找回来。
在Python3中,依附在类上的函数不再当作是未绑定的方法,而是把它当作一个简单地函数,如果有必要它会绑定到一个对象身上去,原则依然和Python2保持一致,但是模块更简洁:
>>> class Pizza(object): ... def __init__(self, size): ... self.size = size ... def get_size(self): ... return self.size ... >>> Pizza.get_size <function Pizza.get_size at 0x7f307f984dd0> 静态方法静态方法是一类特殊的方法,有时你可能需要写一个属于这个类的方法,但是这些代码完全不会使用到实例对象本身,例如:
class Pizza(object): @staticmethod def mix_ingredients(x, y): return x + y def cook(self): return self.mix_ingredients(self.cheese, self.vegetables)这个例子中,如果把mix_ingredients作为非静态方法同样可以运行,但是它要提供self参数,而这个参数在方法中根本不会被使用到。这里的@staticmethod装饰器可以给我们带来一些好处:
Python不再需要为Pizza对象实例初始化一个绑定方法,绑定方法同样是对象,但是创建他们需要成本,而静态方法就可以避免这些。
>>> Pizza().cook is Pizza().cook False >>> Pizza().mix_ingredients is Pizza.mix_ingredients True >>> Pizza().mix_ingredients is Pizza().mix_ingredients True
可读性更好的代码,看到@staticmethod我们就知道这个方法并不需要依赖对象本身的状态。
可以在子类中被覆盖,如果是把mix_ingredients作为模块的顶层函数,那么继承自Pizza的子类就没法改变pizza的mix_ingredients了如果不覆盖cook的话。
类方法话虽如此,什么是类方法呢?类方法不是绑定到对象上,而是绑定在类上的方法。
>>> class Pizza(object): ... radius = 42 ... @classmethod ... def get_radius(cls): ... return cls.radius ... >>> >>> Pizza.get_radius <bound method type.get_radius of <class '__main__.Pizza'>> >>> Pizza().get_radius <bound method type.get_radius of <class '__main__.Pizza'>> >>> Pizza.get_radius is Pizza().get_radius True >>> Pizza.get_radius() 42无论你用哪种方式访问这个方法,它总是绑定到了这个类身上,它的第一个参数是这个类本身(记住:类也是对象)。
什么时候使用这种方法呢?类方法通常在以下两种场景是非常有用的: