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

将可调用对象作为方法注入类

  •  0
  • Hans  · 技术社区  · 4 年前

    可以将函数注入类中,如下所示:

    class MainClass:
        ...
    
    
    def simple_injected_func(self: MainClass, arg: str) -> None:
        print(f"simple_injected_func({arg})")
    
    
    MainClass.simple_injected_func = simple_injected_func
    main_object = MainClass()
    
    main_object.simple_injected_func("arg")
    # outputs: simple_injected_func(arg)
    

    此外,可以使对象像这样可调用

    class SimpleCallableClass:
        def __call__(self, arg: str) -> None:
            print(f"SimpleCallableClass()({arg})")
    
    
    simple_callable_object = SimpleCallableClass()
    simple_callable_object("arg")
    # outputs: SimpleCallableClass()(arg)
    

    我现在想将这两件事结合起来,将一个可调用的类/对象作为函数注入另一个类,同时保留对这两个类的对象变量和方法的访问 CallableClass 以及 MainClass 。(在内部,我想使用它来有效地实现方法继承,并将这些方法从另一个文件注入到类中)

    from inspect import signature
    
    class CallableClass:
        def __call__(self_, self: MainClass, arg: str) -> None:
            print(f"CallableClass()({arg})")
    
    
    callable_object = CallableClass()
    
    MainClass.callable_object = callable_object
    
    main_object = MainClass()
    
    print(signature(simple_injected_func))
    # outputs: (self: __main__.MainClass, arg: str) -> None
    print(signature(callable_object))
    # outputs: (self: __main__.MainClass, arg: str) -> None
    
    print(signature(main_object.simple_injected_func))
    # outputs: (arg: str) -> None
    print(signature(main_object.callable_object))
    # outputs: (self: __main__.MainClass, arg: str) -> None
    
    main_object.simple_injected_func("my arg")
    # outputs: simple_injected_func(my arg)
    main_object.callable_object("my arg")
    # Traceback (most recent call last):
    #   main_object.callable_object("my arg")
    # TypeError: CallableClass.__call__() missing 1 required positional argument: 'arg'
    

    为什么第二个 self 在可调用对象的情况下没有得到正确的剥离?有没有办法做到这一点?

    0 回复  |  直到 4 年前
        1
  •  2
  •   zvone    4 年前

    当访问实例的方法时,Python执行“绑定”,即创建绑定方法。请参见此处:

    >>> class Class:
    ...     def method(self, x):
    ...         return x
    ... 
    >>> 
    >>> instance = Class()
    >>> Class.method
    <function Class.method at 0x7fa688037158>
    >>> instance.method
    <bound method Class.method of <__main__.Class object at 0x7fa688036278>>
    

    绑定之所以完成,是因为方法是作为描述符实现的。

    你也可以 implement your callable as a descriptor 如果你想有这种行为。

    简而言之,您必须实现一个至少具有 __get__ 方法那个 __得到__ 当 Class.method 或 instance.method 评估。它应该返回可调用的(根据是否有实例,它应该是不同的)。

    顺便说一句,要将方法实际绑定到实例,使用起来最简单 functors.partial :

    bound_method = functors.partial(method, instance)
    

    总结如下:

    class Callable:
        def __call__(self, instance, arg):
            print(f"Callable()(arg)")
    
    
    class Descriptor:
        def __init__(self, callable):
            self._callable = callable
    
        def __get__(self, instance, owner):
            if instance is None:
                return self._callable
            else:
                return functools.partial(self._callable, instance)
    
    
    class Class:
        pass
    
    
    Class.method = Descriptor(Callable())
    

    然后:

    >>> signature(Class.method)
    <Signature (instance, arg)>
    >>> signature(Class().method)
    <Signature (arg)>
    
    推荐文章