Python 方法适用于 instantiated/uninstantiated class
Python method available for both instantiated/uninstantiated class
我有一个 class,如果它已经使用 details
方法用 id
实例化,它会获取详细信息并用信息填充 class。如果它还没有实例化,我希望它使用传递给 details
的参数作为 id
和 return 一个新的实例化对象。类似于以下内容:
f = Foo()
f.id = '123'
f.details()
但也允许:
f = Foo.details(id='123')
我可以使用相同的 details
方法来完成吗?或者我是否需要创建两个单独的方法并将其中一个设为 @classmethod
?如果我将一个声明为 @classmethod
而另一个不声明,它们可以具有相同的名称吗?
您必须创建自己的描述符来处理这个问题;如果没有实例可用,它必须绑定到 class,否则绑定到实例:
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):
# called on a class
else:
# called on an instance
您可以通过返回您自己的类似方法的包装器对象来使它更花哨,该包装器对象传入绑定的关键字参数。
演示:
>>> 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'
函数本身的测试有点乏味;你可以借鉴 how property
objects operate 并附加一个单独的函数来处理 class 绑定的情况:
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__)
这将为 classes 或实例调用初始修饰函数(就像上面描述符的实现一样),但它允许您注册一个可选的、单独的函数来处理绑定到 class 当你使用 @methodname.classmethod
装饰器时:
class Foo(object):
@class_or_instance_method
def details(self):
# called on an instance
@details.classmethod
def details(cls, id):
# called on a class, takes mandatory id argument
这有一个额外的好处,现在您可以为两个方法实现提供不同的参数; 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'
如果你想将 class 和实例方法定义分开,你可以这样做:
class overriding_instance_method(object):
"""
can be used as a decorator: see example below in __main__
"""
def __init__(self, class_method_func, instance_method_func=None):
self.class_method_func = class_method_func
self.instance_method_func = instance_method_func
def __call__(self, instance_method_func):
return type(self)(self.class_method_func,
instance_method_func=instance_method_func)
def __get__(self, instance, cls=None):
if instance is None:
return classmethod(self.class_method_func).__get__(None, cls)
return self.instance_method_func.__get__(instance, cls)
像这样使用它:
class OverridingClassMethodTest(object):
def print_me(cls):
print 'class: {}'.format(cls)
@overriding_instance_method(print_me)
def print_me(self):
print 'instance: {}'.format(self)
OverridingClassMethodTest.print_me()
OverridingClassMethodTest().print_me()
我有一个 class,如果它已经使用 details
方法用 id
实例化,它会获取详细信息并用信息填充 class。如果它还没有实例化,我希望它使用传递给 details
的参数作为 id
和 return 一个新的实例化对象。类似于以下内容:
f = Foo()
f.id = '123'
f.details()
但也允许:
f = Foo.details(id='123')
我可以使用相同的 details
方法来完成吗?或者我是否需要创建两个单独的方法并将其中一个设为 @classmethod
?如果我将一个声明为 @classmethod
而另一个不声明,它们可以具有相同的名称吗?
您必须创建自己的描述符来处理这个问题;如果没有实例可用,它必须绑定到 class,否则绑定到实例:
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):
# called on a class
else:
# called on an instance
您可以通过返回您自己的类似方法的包装器对象来使它更花哨,该包装器对象传入绑定的关键字参数。
演示:
>>> 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'
函数本身的测试有点乏味;你可以借鉴 how property
objects operate 并附加一个单独的函数来处理 class 绑定的情况:
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__)
这将为 classes 或实例调用初始修饰函数(就像上面描述符的实现一样),但它允许您注册一个可选的、单独的函数来处理绑定到 class 当你使用 @methodname.classmethod
装饰器时:
class Foo(object):
@class_or_instance_method
def details(self):
# called on an instance
@details.classmethod
def details(cls, id):
# called on a class, takes mandatory id argument
这有一个额外的好处,现在您可以为两个方法实现提供不同的参数; 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'
如果你想将 class 和实例方法定义分开,你可以这样做:
class overriding_instance_method(object):
"""
can be used as a decorator: see example below in __main__
"""
def __init__(self, class_method_func, instance_method_func=None):
self.class_method_func = class_method_func
self.instance_method_func = instance_method_func
def __call__(self, instance_method_func):
return type(self)(self.class_method_func,
instance_method_func=instance_method_func)
def __get__(self, instance, cls=None):
if instance is None:
return classmethod(self.class_method_func).__get__(None, cls)
return self.instance_method_func.__get__(instance, cls)
像这样使用它:
class OverridingClassMethodTest(object):
def print_me(cls):
print 'class: {}'.format(cls)
@overriding_instance_method(print_me)
def print_me(self):
print 'instance: {}'.format(self)
OverridingClassMethodTest.print_me()
OverridingClassMethodTest().print_me()