classmethod和instancemethod的名称相同吗? [英] Same name for classmethod and instancemethod?

查看:87
本文介绍了classmethod和instancemethod的名称相同吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做这样的事情:

class X:

    @classmethod
    def id(cls):
        return cls.__name__

    def id(self):
        return self.__class__.__name__

现在为类或它的实例调用id():

And now call id() for either the class or an instance of it:

>>> X.id()
'X'
>>> X().id()
'X'

显然,此确切的代码不起作用,但是有类似的方法可以使它起作用吗? 还是任何其他解决方法都可以在没有太多骇客"内容的情况下获得这种行为?

Obviously this exact code doesn't work, but is there a similar way to make it work? Or any other workarounds to get such behavior without too much "hacky" stuff?

推荐答案

类和实例方法位于同一个命名空间中,您不能重用这样的名称.在这种情况下,id的最后一个定义将获胜.

Class and instance methods live in the same namespace and you cannot reuse names like that; the last definition of id will win in that case.

类方法将继续在实例上运行,但是,无需创建单独的实例方法;只需使用:

The class method will continue to work on instances however, there is no need to create a separate instance method; just use:

class X:
    @classmethod
    def id(cls):
        return cls.__name__

因为该方法继续绑定到该类:

because the method continues to be bound to the class:

>>> class X:
...     @classmethod
...     def id(cls):
...         return cls.__name__
... 
>>> X.id()
'X'
>>> X().id()
'X'

这是明确记录的:

可以在类(例如C.f())或实例(例如C().f())上调用它.该实例除其类外均被忽略.

It can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class.

如果您确实需要区分绑定到类和实例

如果您需要一种方法,使其根据使用的位置而有所不同;在类上访问时绑定到一个类,在实例上访问时绑定到该实例,您将需要创建一个自定义的 descriptor对象.

描述符API 是Python如何使函数成为绑定为方法,并将classmethod对象绑定到该类;请参见描述方法.

The descriptor API is how Python causes functions to be bound as methods, and bind classmethod objects to the class; see the descriptor howto.

您可以通过创建具有__get__方法的对象来为方法提供自己的描述符.这是一个简单的方法,可根据上下文切换方法绑定的对象,如果__get__的第一个参数是None,则描述符将绑定到一个类,否则它将绑定到一个实例:

You can provide your own descriptor for methods by creating an object that has a __get__ method. Here is a simple one that switches what the method is bound to based on context, if the first argument to __get__ is None, then the descriptor is being bound to a class, otherwise it is being bound to an instance:

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__.

This re-uses classmethod and only re-defines how it handles binding, delegating the original implementation for instance is None, and to the standard function __get__ implementation otherwise.

请注意,在方法本身中,您可能随后必须测试它所绑定的对象. isinstance(firstargument, type)对此是一个很好的测试:

Note that in the method itself, you may then have to test, what it is bound to. isinstance(firstargument, type) is a good test for this:

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

另一种实现可以使用两个函数,一个函数绑定到类时,另一个函数绑定到实例时:

An alternative implementation could use two functions, one for when bound to a class, the other when bound to an instance:

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)

这是带有可选实例方法的类方法.像使用property对象一样使用它;用@<name>.instancemethod装饰实例方法:

This then is a classmethod with an optional instance method. Use it like you'd use a property object; decorate the instance method with @<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值之间的区分,其中模型中的列对象切换这种行为.参见其 混合属性文档.此实现遵循与我的hybridmethod类完全相同的模式.

Personally, my advice is to be cautious about using this; the exact same method altering behaviour based on the context can be confusing to use. However, there are use-cases for this, such as SQLAlchemy's differentiation between SQL objects and SQL values, where column objects in a model switch behaviour like this; see their Hybrid Attributes documentation. The implementation for this follows the exact same pattern as my hybridmethod class above.

这篇关于classmethod和instancemethod的名称相同吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆