Created
August 6, 2023 09:18
-
-
Save XoseLluis/f902cb16752439c8947bd1d9137a2ef8 to your computer and use it in GitHub Desktop.
Delegation in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Callable | |
import inspect | |
from typing import Protocol | |
from abc import abstractmethod | |
def _create_method(to_method_name: str, to_attr_name: str): | |
# returns a closure that traps the name of the method to invoke and the attribute_name of the object that will act as receiver-self | |
def new_method(self, *args, **kargs): | |
inner_self = getattr(self, to_attr_name) | |
to_method = getattr(inner_self, to_method_name) # bound method (to inner_self) | |
return to_method(*args, **kargs) | |
return new_method | |
# decorator with parameters, so it has to return a function (that will be invoked with the class being decorated) | |
# we don't create a new class, we add functions to the existing class and return it | |
def delegate_methods(method_names: list[str], to_attr_name: str): | |
# decorator with parameters | |
# receives a list of method names to create and delegate from them to the corresponding method in the object indicated by to_attr_name | |
def add_methods(cls): | |
for method_name in method_names: | |
setattr(cls, method_name, _create_method(method_name, to_attr_name)) | |
return cls | |
return add_methods | |
def delegate_interface(interface_like, to_attr_name: str): | |
# decorator with parameters | |
# receives an "interface" for which methods we will create "delegator methods" to delegate from them to the corresponding method in the object indicated by to_attr_name | |
def add_methods(cls): | |
method_names = [name for name, func in inspect.getmembers(interface_like, predicate=inspect.isfunction) if name != "__init__"] | |
for method_name in method_names: | |
setattr(cls, method_name, _create_method(method_name, to_attr_name)) | |
return cls | |
return add_methods | |
######################################### | |
print("- Testing delegate_methods") | |
class Formatter: | |
def short_format(self, txt: str, prepend: str): | |
return f"{prepend}{txt}" | |
def long_format(self, txt: str, wrap: str): | |
return f"{wrap}{txt}{wrap}" | |
@delegate_methods(["short_format", "long_format"], "formatter") | |
class TextHelper: | |
def __init__(self, id_, formatter): | |
self.id = id_ | |
self.formatter = formatter | |
def beautify(self, txt) -> str: | |
return f"beautifying {self}" | |
helper = TextHelper("aa", Formatter()) | |
print(helper.long_format("hi", "||")) | |
print(helper.short_format("hi", "||")) | |
print("##################################") | |
print("- Testing delegate_interface") | |
class Formattable(Protocol): | |
@abstractmethod | |
def short_format(self, txt: str, prepend: str): | |
pass | |
@abstractmethod | |
def long_format(self, txt: str, wrap: str): | |
pass | |
class FormattableImp(Formattable): | |
def short_format(self, txt: str, prepend: str): | |
return f"{prepend}{txt}" | |
def long_format(self, txt: str, wrap: str): | |
return f"{wrap}{txt}{wrap}" | |
@delegate_interface(Formattable, "formatter") | |
class TextHelper2: | |
def __init__(self, id_, formatter): | |
self.id = id_ | |
self.formatter = formatter | |
def beautify(self, txt) -> str: | |
return f"beautifying {self}" | |
helper = TextHelper2("aa", FormattableImp()) | |
print(helper.long_format("hi", "||")) | |
print(helper.short_format("hi", "||")) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment