It is possible to inject a function into a class like this:
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)
Furthermore it is possible to make an object callable like this
class SimpleCallableClass:
def __call__(self, arg: str) -> None:
print(f"SimpleCallableClass()({arg})")
simple_callable_object = SimpleCallableClass()
simple_callable_object("arg")
# outputs: SimpleCallableClass()(arg)
I now want to combine these two things and inject a callable class/object as a function into another class while keeping access to object variables and methods of both the CallableClass as well as the MainClass. (Internally I want to use this to effectively implement method inheritance and inject those methods into a class from another file)
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'
Why does the second self not get correctly stripped in case of the callable object? Is there some way of achieving this?
When methods of an instance are accessed, Python performs "binding", i.e. it creates a bound method. See here:
The binding is done because methods are implemented as descriptors.
You can also implement your callable as a descriptor if you want to have that behaviour.
In short, you would have to implement a class with at least a
__get__method. That__get__method will be called when eitherClass.methodorinstance.methodis evaluated. It should return the callable (which should be a different one depending on whether there is an instance or not).BTW, to actually bind a method to an instance, it is simplest to use
functors.partial:All summed up:
And then: