一种类方法,当作为实例方法调用时表现不同?

时间:2009-05-14 00:26:24

标签: python class methods

我想知道在调用类方法时是否可以创建一个行为与调用实例方法时相比的方法。

例如,作为一项技能提升项目,我正在编写一个Matrix类(是的,我知道那里已经存在非常好的矩阵类)。我为它创建了一个名为identity的类方法,它返回一个指定大小的单位矩阵。

现在,当调用Matrix实例时,似乎逻辑上不需要指定大小;它应该返回一个与调用它的Matrix大小相同的单位矩阵。

换句话说,我想定义一个方法,可以确定它是否是通过实例调用的,如果是,则访问该实例的属性。不幸的是,即使在深入阅读文档和一些Google搜索之后,我也没有找到任何暗示这是可行的。有谁知道不同?

修改

哇!显然,我还不太习惯一流的功能。这就是我最终得到的结果 - 感谢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 个答案:

答案 0 :(得分:38)

有问题的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

答案 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

答案 2 :(得分: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

答案 3 :(得分:3)

我认为更大的问题是你在'Foo'类上重载名称'bar',这是python不允许的。 'bar'的第二个定义是'bar'的第一个定义。

尝试为您的classmethod和instance方法考虑唯一的名称。即。

@classmethod
def create(cls, baz):
   ...

def rubber_stamp(self):
   ...

答案 4 :(得分:2)

您可以使用简短的lambda函数在 init 中重新分配您的身份识别方法:

class Matrix(object):
    def __init__(self):
        self.identity = lambda s=self:s.__class__.identity(s)

        #...whatever initialization code you have...
        self.size = 10        

    @classmethod
    def identity(self, other):
        #...always do you matrix calculations on 'other', not 'self'...
        return other.size


m = Matrix()
print m.identity()
print Matrix.identity(m)

如果你不熟悉lambda,它会创建一个匿名函数。这很少是必要的,但它可以使您的代码更简洁。上面的lambda行可以重写:

    def identity(self):
        self.__class__.indentity(self)
    self.identity = identity