一个类方法在作为实例方法调用时表现不同吗?

26 投票
5 回答
3943 浏览
提问于 2025-04-15 11:34

我在想,是否可以创建一个方法,让它在作为类方法调用时和作为实例方法调用时表现得不一样。

举个例子,作为一个提升技能的项目,我正在写一个 Matrix 类(是的,我知道市面上已经有很好的矩阵类了)。我为这个类创建了一个叫 identity 的类方法,它可以返回一个指定大小的单位矩阵。

现在,当这个方法在一个 Matrix 的实例上被调用时,我觉得没必要指定大小;它应该返回一个和被调用的 Matrix 实例大小相同的单位矩阵。

换句话说,我想定义一个方法,能够判断它是通过实例调用的,如果是的话,就能访问那个实例的属性。不幸的是,即使我翻阅了文档和做了一些谷歌搜索,我也没有找到任何表明这可能的线索。有没有人知道其他的情况呢?

编辑:

哇!显然,我还不太习惯一等函数。最后我得到了这个结果——感谢 Unknown 提供的关键!

class Foo(object):
    def __init__(self, bar):
        self.baz = bar
        self.bar = MethodType(lambda self: self.__class__.bar(self.baz), self, self.__class__)

    @classmethod
    def bar(cls, baz):
        return 5 * baz

Foo.bar(3) # returns 15

foo = Foo(7)
foo.bar() # returns 35

编辑 2:

顺便说一句,这种技术(以及下面大部分介绍的技术)在定义了 __slots__ 的类上是行不通的,因为你不能重新分配这个方法。

5 个回答

7

@Unknown 你这个和这个有什么区别:

class Foo(object):

    def _bar(self, baz):
        print "_bar, baz:", baz

    def __init__(self, bar):
        self.bar = self._bar
        self.baz = bar

    @classmethod
    def bar(cls, baz):
        print "bar, baz:", baz

In [1]: import foo

In [2]: f = foo.Foo(42)

In [3]: f.bar(1)
_bar, baz: 1

In [4]: foo.Foo.bar(1)
bar, baz: 1

In [5]: f.__class__.bar(1)
bar, baz: 1
7

[编辑:使用属性来更直接地回答;请参阅John Fouhy的有用评论]

你可以使用描述符来实现你想要的效果:

class cls_or_inst_method(object):
    def __init__(self, class_method, instance_method):
        self.class_method = class_method
        self.instance_method = instance_method

    def __get__(self, obj, objtype):
        if obj is None:
            return self.class_method
        else:
            return lambda: self.instance_method(obj)

def my_class_method(baz):
    return baz + 1

def my_instance_method(self):
    return self.baz * 2

class Foo(object):
    baz = 10
    bar = cls_or_inst_method(my_class_method, my_instance_method)

使用上面的内容:

>>> print Foo.bar(5)
6
>>> my_foo = Foo()
>>> print my_foo.bar()
20
40

我擅长一些看起来有点奇怪但其实有用的Python小技巧。

from types import *

class Foo(object):
    def __init__(self):
        self.bar = methodize(bar, self)
        self.baz = 999

    @classmethod
    def bar(cls, baz):
        return 2 * baz


def methodize(func, instance):
    return MethodType(func, instance, instance.__class__)

def bar(self):
    return 4*self.baz


>>> Foo.bar(5)
10
>>> a=Foo()
>>> a.bar()
3996

撰写回答