类和实例方法位于同一名称空间中,不能重复使用这样的名称;在这种情况下,id
的最后一个定义将获胜.
类方法将继续在实例上工作,但是,有no need个实例方法可以创建一个单独的实例;只需使用:
class X:
@classmethod
def id(cls):
return cls.__name__
因为方法继续绑定到类:
>>> class X:
... @classmethod
... def id(cls):
... return cls.__name__
...
>>> X.id()
'X'
>>> X().id()
'X'
这是明确记录的:
它既可以在类(如C.f()
)上调用,也可以在实例(如C().f()
)上调用.实例被忽略,但其类除外.
如果确实需要区分绑定到类和实例
如果你需要一种不同的方法,根据它在哪里被使用;在类上访问时绑定到类,在实例上访问时绑定到实例,您需要创建一个自定义的descriptor object.
descriptor API是Python如何将函数绑定为方法,并将classmethod
个对象绑定到类;见descriptor howto.
通过创建一个包含__get__
个方法的对象,可以为方法提供自己的描述符.下面是一个基于上下文切换方法绑定内容的简单方法,如果__get__
的第一个参数是None
,那么描述符绑定到一个类,否则它绑定到一个实例:
class class_or_instancemethod(classmethod):
def __get__(self, instance, type_):
descr_get = super().__get__ if instance is None else self.__func__.__get__
return descr_get(instance, type_)
这将重新使用classmethod
,并且只重新定义它如何处理绑定,将原始实现委托给instance is None
,否则将委托给标准函数__get__
实现.
请注意,在方法本身中,您可能需要测试它绑定的内容.isinstance(firstargument, type)
是一个很好的测试:
>>> class X:
... @class_or_instancemethod
... def foo(self_or_cls):
... if isinstance(self_or_cls, type):
... return f"bound to the class, {self_or_cls}"
... else:
... return f"bound to the instance, {self_or_cls"
...
>>> X.foo()
"bound to the class, <class '__main__.X'>"
>>> X().foo()
'bound to the instance, <__main__.X object at 0x10ac7d580>'
另一种实现可以使用two个函数,一个用于绑定到类,另一个用于绑定到实例:
class hybridmethod:
def __init__(self, fclass, finstance=None, doc=None):
self.fclass = fclass
self.finstance = finstance
self.__doc__ = doc or fclass.__doc__
# support use on abstract base classes
self.__isabstractmethod__ = bool(
getattr(fclass, '__isabstractmethod__', False)
)
def classmethod(self, fclass):
return type(self)(fclass, self.finstance, None)
def instancemethod(self, finstance):
return type(self)(self.fclass, finstance, self.__doc__)
def __get__(self, instance, cls):
if instance is None or self.finstance is None:
# either bound to the class, or no instance method available
return self.fclass.__get__(cls, None)
return self.finstance.__get__(instance, cls)
这是一个带有可选实例方法的classmethod.像使用property
个物体一样使用它;用@<name>.instancemethod
装饰实例方法:
>>> class X:
... @hybridmethod
... def bar(cls):
... return f"bound to the class, {cls}"
... @bar.instancemethod
... def bar(self):
... return f"bound to the instance, {self}"
...
>>> X.bar()
"bound to the class, <class '__main__.X'>"
>>> X().bar()
'bound to the instance, <__main__.X object at 0x10a010f70>'
就我个人而言,我的建议是谨慎使用;基于上下文改变行为的完全相同的方法使用起来可能会令人困惑.然而,也有这样的用例,比如SQLAlchemy在SQL对象和SQL值之间的区别,其中模型中的列对象会像这样切换行为;看看他们的Hybrid Attributes documentation.它的实现遵循与上面我的hybridmethod
类完全相同的模式.