Python 方法查找:静态与实例

9 投票
3 回答
1302 浏览
提问于 2025-04-16 18:47

直到大约一个小时前,我一直认为在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())的简写这个想法是错的。

这让我产生了几个问题:

  1. 当我调用Foo().bar()时,会发生什么?
  2. 当我调用Foo.bar(Foo())时,会发生什么?
  3. 这两者之间有内部差别吗?
  4. 我是不是漏掉了Python的一些基本概念?
  5. 如果你要写一个类,它的静态populate方法和这个类的实例调用的populate方法做的事情不一样,那应该怎么做?

(是的,它们必须同名。)

3 个回答

0

qnx.populate() 首先会在实例 qnx 中查找 populate 这个方法。如果找不到,就会沿着 __mro__ 的顺序继续查找,直到找到一个叫 populate 的方法为止。

Animals.populate(qnx) 则跳过了上面查找的第一步。

2

静态方法和类方法是一些特别的描述符。因为描述符的 __get__() 方法的参数包括类和相关的实例,所以它们可以随意修改方法的参数。

2

关于 Foo().bar()、Foo.bar(Foo()) 和 Foo.bar() 之间的区别(我昨天刚注册,所以不能评论,所以写这个作为回答)——这是因为 Python(<3.0)对“绑定”和“未绑定”方法的概念。它严格要求,除了使用 @staticmethod 或 @classmethod 的情况,方法调用必须和一个实例关联在一起。其实没有更简单的解释,只能记住这个规则。
幸运的是,这在 Python 3 中发生了变化——“绑定”和“未绑定”方法作为两个不同的概念已经不再存在,Foo.bar() 在你的例子中可以正常工作。

撰写回答