创建一个既是 class 方法又是实例方法的方法

Make a method that is both a class method and an instance method

有没有办法在Python中定义一个既是class方法又是实例方法的方法,这样clsself都是接收者.特别是,我希望制作一种方法,(1) 知道在 class (Foo.method(value)) 上调用时调用了哪个 class 并且 (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 变量,该方法不知道它刚刚调用了哪个 class。它可能被称为 Bar.method(value),现在可以知道 value 是否是 Foo 的实例。未修饰的方法更像是静态方法和实例方法,而不是 class 方法和实例方法。

您不需要装饰器。这就是方法已经起作用的方式;实例作为第一个参数传递——当从实例调用方法时隐式传递,从 class 显式传递——在实例可用的情况下,您可以通过在实例上调用 type 来检索 class:

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__)特殊方法中,你应该return一个字符串,而不是打印。

我使用了 __repr__,因为在容器对象(此处为元组)内部时,不会调用 __str__ 来打印实例。

更新:

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

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()'

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

简而言之,描述符必须定义一个 __get__ 方法,该方法将在作为属性访问时调用(如 Foo.methodFoo().method)。此方法将传递实例和 class 作为参数和 returns 一个 绑定 方法(即它 returns 一个带有 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()')