函数在类vs实例上表现不同

Dil*_*rix 4 python methods class function python-descriptors

我希望一个特定的函数可以作为类方法调用,并且当它在一个实例上调用时表现不同.

例如,如果我有一个class Thing,我想Thing.get_other_thing()工作,但也thing = Thing(); thing.get_other_thing()表现不同.

我认为覆盖get_other_thing初始化方法应该有效(见下文),但这看起来有点hacky.有没有更好的办法?

class Thing:

    def __init__(self):
        self.get_other_thing = self._get_other_thing_inst()

    @classmethod
    def get_other_thing(cls):
        # do something...

    def _get_other_thing_inst(self):
        # do something else
Run Code Online (Sandbox Code Playgroud)

Bha*_*rel 6

好问题!您使用描述符可以轻松完成您所寻求的内容.

描述符是实现描述符协议的 Python对象,通常以__get__().

它们主要存在于不同类中的类属性.在访问它们时,将__get__()调用它们的方法,并传入实例和所有者类.

class DifferentFunc:
    """Deploys a different function accroding to attribute access

    I am a descriptor.
    """

    def __init__(self, clsfunc, instfunc):
        # Set our functions
        self.clsfunc = clsfunc
        self.instfunc = instfunc

    def __get__(self, inst, owner):
        # Accessed from class
        if inst is None:
            return self.clsfunc.__get__(None, owner)

        # Accessed from instance
        return self.instfunc.__get__(inst, owner)


class Test:
    @classmethod
    def _get_other_thing(cls):
        print("Accessed through class")

    def _get_other_thing_inst(inst):
        print("Accessed through instance")

    get_other_thing = DifferentFunc(_get_other_thing,
                                    _get_other_thing_inst)
Run Code Online (Sandbox Code Playgroud)

现在结果如下:

>>> Test.get_other_thing()
Accessed through class
>>> Test().get_other_thing()
Accessed through instance
Run Code Online (Sandbox Code Playgroud)

那很简单!

顺便问一下,你注意到我__get__在类和实例函数上使用了吗?你猜怎么着?函数也是描述符,这就是它们的工作方式!

>>> def func(self):
...   pass
...
>>> func.__get__(object(), object)
<bound method func of <object object at 0x000000000046E100>>
Run Code Online (Sandbox Code Playgroud)

在访问一个函数属性时,它__get__被调用,这就是你如何获得函数绑定.

有关更多信息,我强烈建议阅读Python手册和上面链接的"操作方法".描述符是Python最强大的功能之一,几乎不为人所知.


为什么不在实例化时设置功能?

或者为什么不在self.func = self._func里面__init__

在实例化时设置函数会带来很多问题:

  1. self.func = self._func导致循环引用.该实例存储在返回的函数对象内self._func.另一方面,这在分配期间存储在实例上.最终结果是实例引用自身并以更慢和更重的方式清理.
  2. 与您的类交互的其他代码可能会尝试直接从该类中获取该函数,并使用__get__()(这是通常的预期方法)来绑定它.他们将收到错误的功能.
  3. 不会合作__slots__.
  4. 虽然使用描述符需要了解机制,但将其设置__init__为不干净并且需要设置多个函数__init__.
  5. 需要更多记忆.您可以为每个实例存储绑定函数,而不是存储单个函数.
  6. 不适用于属性.

随着列表不断增加,我还没有添加更多内容.