在装饰器中从其名称获取装饰类?

时间:2019-09-16 20:36:08

标签: python python-decorators introspection

我用@bot_thinking装饰了一些方法,该方法将有关装饰方法的一些信息存储在functions属性中。 其中一条信息是“ class_name”,但是我的程序需要将类类型作为变量,例如RandomBot。我想上这堂课。

以下是一些示例代码:

class DepthPrunedMinimaxAgent(Agent):
    @bot_thinking(associated_name="minimax profondeur")
    def select_move(self, game_state: GameState):

上面是代码的修饰部分。

装饰器:

functions = {}

def bot_thinking(associated_name, active=True):
    def _(func):
        if active:
            class_name = func.__qualname__.rsplit('.')[-2]
            import sys
            # class_name_2=getattr(sys.modules[__name__], class_name)
            # module=importlib.import_module('sources.agent')

            functions[associated_name] = (associated_name, class_name,
                                          globals()[class_name], func)
        else:
            functions.pop(associated_name)

    return _

bot_thinking不是真正的装饰器,它是装饰器工厂。 从func函数中,我得到了class_name,但是我不能使用@ m.kocikowski的accepted answer来查找正确的类,因为该类已被修饰,所以它已经会导入注释模块,因此从注释模块中导入已注释的模块将导致循环导入,而python似乎不允许这样做。

您看到从名称中获取类的方法吗?

ps: ps: 更清楚一点:代码的注释部分需要导入到带注释的类(以从其名称中检索类),还需要导入注释(以使注释起作用)。

2 个答案:

答案 0 :(得分:1)

问题是在执行bot_thinking()装饰器工厂(和装饰器本身)时尚未定义该类。我能想到的唯一解决方法是在定义了类后 进行修补,如下所示:

from pprint import pprint, pformat

functions = {}

def bot_thinking(associated_name, active=True):
    def _(func):
        if active:
            class_name = func.__qualname__.split(".")[-2]
            functions[associated_name] = (associated_name, class_name, class_name, func)
        else:
            functions.pop(associated_name, None)

        return func # Decorators must return a callable.

    return _


class Agent: pass
class GameState: pass

class DepthPrunedMinimaxAgent(Agent):
    @bot_thinking(associated_name="minimax profondeur")
    def select_move(self, game_state: GameState):
        pass

# After class is defined, update data put into functions dictionary.
for associated_name, info in functions.items():
    functions[associated_name] = (info[0], info[1], globals()[info[2]], info[3])

pprint(functions)

输出:

{'minimax profondeur': ('minimax profondeur',
                        'DepthPrunedMinimaxAgent',
                        <class '__main__.DepthPrunedMinimaxAgent'>,
                        <function DepthPrunedMinimaxAgent.select_move at 0x00F158A0>)}

答案 1 :(得分:1)

至少使用Python 3.6或更高版本时,如果使用描述符类而不是函数作为修饰符,则可以执行所需的操作。这是因为在描述符协议__set_name__中添加了一个新方法。当描述符对象另存为类变量时,它将被调用。虽然大多数描述符将使用它来记录将其另存为的名称,但是您可以使用它来获取您所在的类。

您确实需要使装饰器对象包装真实函数(实现调用和描述符查找方法),而不是能够返回您正在装饰的未修改函数。这是我尝试快速而肮脏的实现。我不太了解您在functions的工作中所处的位置,因此我可能没有在其中放入正确的数据,但应该足够接近以使您理解(owner是方法存储在其中)。

functions = {}

def bot_thinking(associated_name, active=True):
    class decorator:
        def __init__(self, func):
            self.func = func

        def __set_name__(self, owner, name):
            if active:
                 functions[associated_name] = (associated_name, owner.__name__,
                                               owner, self.func)
            else:
                 functions.pop(associated_name)

        def __get__(self, obj, owner):
            return self.func.__get__(obj, owner)

        def __call__(self, *args, **kwargs):
            return self.func(*args, **kwargs)

    return decorator