创建既是类方法又是实例方法的方法

时间:2018-02-15 13:46:43

标签: python decorator class-method

Python中是否有一种方法可以定义一个既是类方法又是实例方法的方法,这样clsself都是接收者。特别是,我正在寻找一种方法:(1)在类(Foo.method(value))上调用时知道调用哪个类,(2)知道在实例上调用时调用了哪个实例( foo.method())。

例如,我想象一下这样的事情:

class Foo:
    def __str__(self): return 'Foo()'

    @classinstancemethod
    def method(cls, self):
        print(cls, self)

class Bar(Foo):
    def __str__(self): return 'Bar()'

Foo().method()     # <class '__main__.Foo'> Foo()
Bar().method()     # <class '__main__.Bar'> Bar()
Foo.method(Foo())  # <class '__main__.Foo'> Foo()
Foo.method(Bar())  # <class '__main__.Foo'> Bar()
Bar.method(Foo())  # <class '__main__.Bar'> Foo()

请注意,我知道可以像Foo.foo(value)那样调用未修饰的方法,但这不是我想要的,因为它没有获得cls变量。如果没有cls变量,该方法就不知道它刚刚调用了哪个类。它可能被称为Bar.method(value),现在可以知道如果valueFoo的实例。未修饰的方法更像是静态方法和实例方法,而不是类方法和实例方法。

2 个答案:

答案 0 :(得分:2)

你不需要装饰师。这就是方法已经有效的方法;实例作为第一个参数传递 - 隐式地,当从实例调用方法并显式地从类中调用 - 实例可用时,您可以通过调用实例上的type来检索类:

class Foo(object):
    def __repr__(self): return 'Foo()'

    def method(self):
        print((type(self), self)) 

class Bar(Foo):
    def __repr__(self): return 'Bar()'

另一方面,在__str__(或__repr__)特殊方法中,您应该返回一个字符串,而不是打印。

我使用了__repr__,因为在容器对象(这里的元组)内部没有调用__str__来打印实例。

<强>更新

考虑到上述方法中类/实例打印的问题,您可以使用描述符来正确管理方法中正确的类和实例选择:

class classinstancemethod():

  def __get__(self, obj, cls):
     def method(inst=None):
        print(cls, inst if inst else obj)
     return method

class Foo(object):
    method = classinstancemethod()

    def __str__(self): return 'Foo()'


class Bar(Foo):
    def __str__(self): return 'Bar()'

答案 1 :(得分:1)

这可以通过将classinstancemethod实施为自定义descriptor来解决。

简而言之,描述符必须定义__get__方法,当它作为属性(如Foo.methodFoo().method)进行访问时将被调用。此方法将作为参数传递实例和类,并返回绑定方法(即,它返回一个带有clsself参数的方法。调用此绑定方法时,它会将已烘焙的clsself参数转发给实际方法。

class classinstancemethod:
    def __init__(self, method, instance=None, owner=None):
        self.method = method
        self.instance = instance
        self.owner = owner

    def __get__(self, instance, owner=None):
        return type(self)(self.method, instance, owner)

    def __call__(self, *args, **kwargs):
        instance = self.instance
        if instance is None:
            if not args:
                raise TypeError('missing required parameter "self"')
            instance, args = args[0], args[1:]

        cls = self.owner
        return self.method(cls, instance, *args, **kwargs)

结果:

class Foo:
    def __repr__(self): return 'Foo()'

    @classinstancemethod
    def method(cls, self):
        print((cls, self))


class Bar(Foo):
    def __repr__(self): return 'Bar()'


Foo().method()     # (<class '__main__.Foo'>, 'Foo()')
Bar().method()     # (<class '__main__.Bar'>, 'Bar()')
Foo.method(Foo())  # (<class '__main__.Foo'>, 'Foo()')
Foo.method(Bar())  # (<class '__main__.Foo'>, 'Bar()')
Bar.method(Foo())  # (<class '__main__.Bar'>, 'Foo()')
相关问题