代码之家  ›  专栏  ›  技术社区  ›  DilithiumMatrix

函数在类上的行为与在实例上的行为不同

  •  4
  • DilithiumMatrix  · 技术社区  · 7 年前

    我希望一个特殊的函数可以作为一个类方法被调用,并且在实例上被调用时表现得不同。

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

    我想重写 get_other_thing 初始化的方法应该可以工作(见下面),但这看起来有点糟糕。有更好的方法吗?

    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
    
    2 回复  |  直到 7 年前
        1
  •  6
  •   Bharel    7 年前

    好问题!你所寻找的可以很容易地用 描述符 .

    Descriptors 是实现 描述符协议 ,通常从 __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)
    

    现在的结果是:

    >>> Test.get_other_thing()
    Accessed through class
    >>> Test().get_other_thing()
    Accessed through instance
    

    那很容易!

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

    >>> def func(self):
    ...   pass
    ...
    >>> func.__get__(object(), object)
    <bound method func of <object object at 0x000000000046E100>>
    

    在访问函数属性时, _获取__ 调用,这就是如何获得函数绑定的方法。

    更多信息,我强烈建议阅读 Python manual 以及 "How-To" 链接在上面。描述符是Python最强大的功能之一,甚至鲜为人知。


    为什么不在实例化时设置函数?

    为什么不设置 self.func = self._func 里面 __init__ 是吗?

    在实例化时设置函数会遇到很多问题:

    1. self.func=自我。 导致循环引用。实例存储在由返回的函数对象中 self._func .另一方面,它在分配期间存储在实例上。最终的结果是,实例引用自身,并以更慢、更重的方式进行清理。
    2. 与类交互的其他代码可能会试图直接从类中取出函数,并使用 _获取 这是通常期望的方法,用于绑定它。他们将收到错误的功能。
    3. 不与合作 __slots__ .
    4. 尽管对于描述符,您需要了解其机制,但是将其设置为 _初始化__ 不是很干净,需要打开多个功能 _初始化__ .
    5. 占用更多内存。不存储单个函数,而是为每个实例存储一个绑定函数。
    6. 不与合作 properties .

    随着列表的不断增加,还有很多东西我没有添加。

        2
  •  1
  •   Eugene Primako    7 年前

    这里有一个有点黑客的解决方案:

    class Thing(object):
        @staticmethod
        def get_other_thing():
            return 1
    
        def __getattribute__(self, name):
            if name == 'get_other_thing':
                return lambda: 2
            return super(Thing, self).__getattribute__(name)
    
    print Thing.get_other_thing()  # 1
    print Thing().get_other_thing()  # 2
    

    如果我们在类上,则会执行StaticMethod。如果我们有机会, __getattribute__ 是第一个被执行的,所以我们可以不返回 Thing.get_other_thing 但是其他功能( lambda 在我的情况下)