您需要创建自己的描述符来处理此问题;如果没有实例可用,它必须绑定到类,否则绑定到实例:
class class_or_instance_method(object):
def __init__(self, func, doc=None):
self.func = func
self.cmdescriptor = classmethod(func)
if doc is None:
doc = func.__doc__
self.__doc__ = doc
def __get__(self, instance, cls=None):
if instance is None:
return self.cmdescriptor.__get__(None, cls)
return self.func.__get__(instance, cls)
如果没有实例可用,此描述符将委托给classmethod()
对象以产生正确的绑定。
请像这样使用:
class Foo(object):
@class_or_instance_method
def details(cls_or_self, id=None):
if isinstance(cls_or_self, type):
else:
你可以通过返回自己的类似方法的包装对象,并传递绑定的关键字参数,使其变得更加华丽。
演示:
Demo:
>>> class Foo(object):
... @class_or_instance_method
... def details(cls_or_self, id=None):
... if isinstance(cls_or_self, type):
... return 'Class method with id {}'.format(id)
... else:
... return 'Instance method with id {}'.format(cls_or_self.id)
...
>>> Foo.details(42)
'Class method with id 42'
>>> f = Foo()
>>> f.id = 42
>>> f.details()
'Instance method with id 42'
函数本身中的测试有些繁琐; 你可以从属性装饰器是如何工作的中借鉴一下,附加一个单独的函数来处理类绑定的情况:
class class_or_instance_method(object):
def __init__(self, instf, clsf=None, doc=None):
self.instf = instf
self.clsf = clsf
self.cmdescriptor = classmethod(clsf or instf)
if doc is None:
doc = instf.__doc__
self.__doc__ = doc
def __get__(self, instance, cls=None):
if instance is None:
return self.cmdescriptor.__get__(None, cls)
return self.instf.__get__(instance, cls)
def classmethod(self, clsf):
return type(self)(self.instf, clsf, doc=self.__doc__)
def instancemethod(self, instf):
return type(self)(instf, self.clsf, doc=self.__doc__)
这将为两个类或实例都调用最初装饰的函数(就像上面描述符的实现一样),但它允许您注册一个可选的、单独的函数来处理当您使用@methodname.classmethod
装饰器时绑定到一个类:
class Foo(object):
@class_or_instance_method
def details(self):
@details.classmethod
def details(cls, id):
这样做的额外优势是现在你可以为这两种方法实现提供不同的参数;Foo.details()
接受上述的 id
参数,而 instance.details()
则不包括:
>>> class Foo(object):
... @class_or_instance_method
... def details(self):
... return 'Instance method with id {}'.format(self.id)
... @details.classmethod
... def details(self, id):
... return 'Class method with id {}'.format(id)
...
>>> Foo.details(42)
'Class method with id 42'
>>> f = Foo()
>>> f.id = 42
>>> f.details()
'Instance method with id 42'