Python 方法查找:静态与实例
直到大约一个小时前,我一直认为在Python中,Foo().bar()
不过是Foo.bar(Foo())
的简写,也就是把实例作为第一个参数传递。在这个例子中,最后两行代码看起来做的事情是一样的:
class Foo (object):
def bar (self): print "baz"
qux = Foo ()
qux.bar ()
Foo.bar (qux)
但是现在我有一个名为Animal的类,它有一个静态方法populate(),这个方法返回一个人类已知的所有动物的列表。而且每个Animal的实例都有一个populate()方法,用来给实例的属性填充随机值。
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import random
animals = [ ("Bella", "cow"), ("Spike", "dog"), ("José", "iguana"), ("Tux", "penguin") ]
class Animal (object):
@staticmethod
def populate (*args): return map (lambda x: Animal (*x), animals)
def __init__ (self, name = None, species = None):
def bar (): self.name, self.species = random.choice (animals)
self.name = name
self.species = species
self.populate = bar
def __repr__ (self): return "%s of species %s" % (self.name, self.species)
print Animal.populate ()
print Animal ("Pinky", "mouse")
qux = Animal ()
qux.populate ()
print qux
代码运行得很好,但让我产生怀疑的是,print Animal.populate(qux)
调用了静态的populate方法(因此返回了一个列表,而没有填充可怜的qux)。所以显然,我之前认为Foo().bar()
只是Foo.bar(Foo())
的简写这个想法是错的。
这让我产生了几个问题:
- 当我调用
Foo().bar()
时,会发生什么? - 当我调用
Foo.bar(Foo())
时,会发生什么? - 这两者之间有内部差别吗?
- 我是不是漏掉了Python的一些基本概念?
- 如果你要写一个类,它的静态populate方法和这个类的实例调用的populate方法做的事情不一样,那应该怎么做?
(是的,它们必须同名。)
3 个回答
qnx.populate()
首先会在实例 qnx
中查找 populate
这个方法。如果找不到,就会沿着 __mro__
的顺序继续查找,直到找到一个叫 populate
的方法为止。
Animals.populate(qnx)
则跳过了上面查找的第一步。
静态方法和类方法是一些特别的描述符。因为描述符的 __get__()
方法的参数包括类和相关的实例,所以它们可以随意修改方法的参数。
关于 Foo().bar()、Foo.bar(Foo()) 和 Foo.bar() 之间的区别(我昨天刚注册,所以不能评论,所以写这个作为回答)——这是因为 Python(<3.0)对“绑定”和“未绑定”方法的概念。它严格要求,除了使用 @staticmethod 或 @classmethod 的情况,方法调用必须和一个实例关联在一起。其实没有更简单的解释,只能记住这个规则。
幸运的是,这在 Python 3 中发生了变化——“绑定”和“未绑定”方法作为两个不同的概念已经不再存在,Foo.bar() 在你的例子中可以正常工作。