【问题标题】:How to check if an object is a method definition?如何检查对象是否是方法定义?
【发布时间】:2019-11-02 08:42:11
【问题描述】:

我想编写一个装饰器,当它获取一个函数或一个方法时,它会做一些不同的事情。 例如,我想编写一个缓存装饰器,但如果它是一种方法,我不想将 self 作为键的一部分。

def cached(f):
    def _internal(*args, **kwargs):
        if ismethod(f):
            key = create_key(*args[1:], **kwargs) # ignore self from args
        else: # this is a regular function
            key = create_key(*args, **kwargs)
        return actual_cache_mechanism(key, f, *args, **kwargs)
    return _internal

class A:
    @cached
    def b(self, something):
       ...

@cached
def c(something):
    ...

问题是当@cached 被调用时,它无法区分方法和函数,因为它们都是function 类型。 甚至可以做到吗?当我想到它时,我觉得实际上方法不知道它们在其中被定义的上下文......

谢谢!

【问题讨论】:

  • 需要知道函数和方法的区别吗?装饰者不需要知道它们的区别。无论如何,只要通过*args。方法将传递一个类实例 (self) 作为参数。一个函数不会。无论哪种情况,装饰器都可以传递*args。
  • 你可以让你的装饰器返回一个可调用的描述符(就像一个函数一样)并对产生绑定方法的查找做出反应。这种方法可以接受吗?
  • 我只是想知道为什么你需要忽略self。如果您可以在不破坏任何内容的情况下从缓存键中省略self,这意味着结果不依赖于它,并且根本不需要self。此类方法应定义为staticmethod 或classmethod(问题已解决)。
  • @VPfb 这并不完全正确。例如,self 可能包含瞬态数据(如套接字),但其当前状态与未来状态没有本质区别。所以需要self,但不属于缓存键。 @davis-herring 你能详细说明一下吗?不确定我是否理解您的方法...
  • @Nadav。是的,您对套接字是正确的(例如)。实际上,我自己在lru_cache 中发布了一个关于忽略此类论点的问题。然而,在这里,OP 要求一个装饰器在没有套接字(即 self)的情况下完全工作。

标签: python python-decorators


【解决方案1】:

这是一种丑陋的 hack,但您可以使用 obj.__qualname__ 来查看 obj 是否在类中定义,方法是检查它是否有句点

if "." in obj.__qualname__":
   #obj is a member of an object, so it is a method

我不确定它是否适用于装饰器,因为要让它工作,需要在类中定义方法。

【讨论】:

  • 这个有点意思..你可以使用module和name来检查对象的“路径”是否是一个类...
【解决方案2】:

我认为最好避免以良好的 Python 风格的名义使用这种自省的装饰器。

您总是可以将要缓存的函数分解为只接受所需的参数:

@cached
def func(something):
    return ...

class A:
    def b(self, something):
        self.bvalue = func(something)

对于cmets中提到的情况(需要一个对象来获取结果,但它的值不影响它,例如一个socket),请参考这些问题:How to ignore a parameter in functools. lru_cache?和Make @lru_cache ignore some of the function arguments

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 2011-03-01
    • 2019-10-14
    • 2017-08-02
    • 2011-05-17
    • 1970-01-01
    相关资源
    最近更新 更多