Я пишу пакет Python, который использует декораторы для удаления стандартного кода, одновременно анализируя функцию, чтобы получить имя функции по умолчанию. Примерно так:
def make_real_instance(func: Optional[Callable] = None,
*, func_name: Optional[str] = None):
def make_wrapper(in_func):
@functools.wraps(in_func)
def wrapper(*args, **kwargs):
nonlocal func_name
# Use func_name if provided
func_name = func_name or in_func.__name__
# Construct params for new object from function and its inputs
stuff = in_func(*args, **kwargs)
# Process stuff commonly
new_object = ObjectConstructor(processed_stuff, func_name)
return new_object
return wrapper
if func:
return make_wrapper(func)
return make_wrapper
Теперь у меня есть такой класс:
class ObjectConstructor:
def __init__(self, ...):
#do stuff
@make_real_instance
def add_foo(self, foo_length: float):
stuff = ... # process the input for this method
return stuff
Декоратор @make_real_instance
вызывает вызов метода add_foo
экземпляра ObjectConstructor
для возврата нового экземпляра. Однако фактический тип возвращаемого значения для неокрашенного метода - кортеж.
Что я хотел бы сделать, так это аннотировать метод, чтобы напечатать подсказку возврата как экземпляр ObjectConstructor
. Т.е. строка:
def add_foo(self, foo_length: float):
станет (импортировав annotations
из __future__
):
def add_foo(self, foo_length: float): -> ObjectConstructor
Тем не менее, моя IDE (PyCharm, хотя я не обязательно считаю это материалом в данном случае) жалуется (справедливо, я полагаю), что выходные данные этого метода не совпадают с подсказкой возвращаемого типа.
Наконец, мой вопрос: есть ли способ правильно напечатать подсказку типа возврата вызова функции, который вы изменяете в результате использования декоратора? Или мой единственный выход - заменить декоратор на функцию, которую я вызываю в каждой декорированной функции, и каждый раз явно передаю свой параметр func_name
? Под этим я подразумеваю что-то вроде:
def make_obj_from_stuff(stuff, func_name) -> ObjectConstructor:
# process stuff commonly
new_object = new_object = ObjectConstructor(processed_stuff, func_name)
return new_object
class ObjectConstructor:
def __init__(self, ...):
#do stuff
def add_foo(self, foo_length: float) -> ObjectConstructor:
stuff = ... # process the input
new_obj = make_obj_from_stuff(stuff, "add_foo")
return new_obj
Если я хочу сохранить созданный мной интерфейс декоратора, нужно ли мне просто признать, что я не смогу правильно ввести подсказку, не пожаловавшись в мою среду IDE?