Python中是否有一种方法可以定义一个既是类方法又是实例方法的方法,这样cls
和self
都是接收者。特别是,我正在寻找一种方法:(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)
,现在可以知道如果value
是Foo
的实例。未修饰的方法更像是静态方法和实例方法,而不是类方法和实例方法。
答案 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.method
或Foo().method
)进行访问时将被调用。此方法将作为参数传递实例和类,并返回绑定方法(即,它返回一个带有cls
和self
参数的方法。调用此绑定方法时,它会将已烘焙的cls
和self
参数转发给实际方法。
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()')