Этот ответ является описанием того, как я нашел разумное решение, поэтому, пожалуйста, потерпите меня или просто прокрутите до декоратора TL; DR'd в конце.
Давайте начнем с того, что help
есть и делает. help
добавляется во встроенное пространство имен с помощью site
. Реализация по умолчанию на моей машине с Ubuntu перенаправляет все на pydoc.help
. Это в свою очередь использует inspect
для получения подписей и описаний. Вас интересуют только функции, а точнее __init__
. Кроме того, вы заботитесь только о подписи, а не об остальных документах. Это должно упростить ситуацию.
Мы можем с уверенностью предположить, что подпись, которую вы видите в help
/ pydoc
, генерируется inspect.signature
. Посмотрите на исходный код этой функции для Python 3.8.2 и проследите через inspect.Signature.from_callable
-> inspect._signature_from_callable
-> В строке 2246 мы видим возможное решение.
Суть в том, что если у объекта функции есть атрибут __signature__
, и этот атрибут является экземпляром inspect.Signature
, она будет использоваться в качестве сигнатуры функции, без пересчета ее из обычного осмотра объектов __code__
и __annotation__
.
Еще один момент в нашу пользу это что функции являются первоклассными объектами с атрибутом __dict__
, которым могут быть назначены произвольные ключи. Назначение __signature__
вашей функции не повлияет на ее выполнение, поскольку она используется только для проверки. Фактическая сигнатура времени выполнения определяется в объекте __code__
с помощью таких атрибутов, как co_argcount
, co_kwonlyargcount
, co_varnames
, et c.
. Поэтому вы можете просто сделать:
import inspect
Child2.__init__.__signature__ = inspect.signature(Child1.__init__)
Результат:
>>> help(Child1)
Help on class Child1 in module __main__:
class Child1(Base)
| Child1(arg1, arg2)
|
| Method resolution order:
| Child1
| Base
| builtins.object
|
| Methods defined here:
|
| foo(self)
|
| ----------------------------------------------------------------------
| Methods inherited from Base:
|
| __init__(self, arg1, arg2)
| Initialize self. See help(type(self)) for accurate signature.
|
| ----------------------------------------------------------------------
| Data descriptors inherited from Base:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
>>> help(Child2)
Help on class Child2 in module __main__:
class Child2(Base)
| Child2(arg1, arg2)
|
| Method resolution order:
| Child2
| Base
| builtins.object
|
| Methods defined here:
|
| __init__(self, arg1, arg2)
| Initialize self. See help(type(self)) for accurate signature.
|
| foo(self)
|
| ----------------------------------------------------------------------
| Data descriptors inherited from Base:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Оба конструктора продолжают работать как обычно, кроме этого.
Поскольку это не изменяет объект кода или даже аннотации, изменение вряд ли повлияет все, что касается работы функции.
TL; DR
Вот декоратор, который можно использовать для копирования сигнатур функций без вмешательства в функцию в любом Другой способ:
import inspect
def copy_signature(base):
def decorator(func):
func.__signature__ = inspect.signature(base)
return func
return decorator
И вы можете переписать Child2
как
class Child2:
@copy_signature(Child1.__init__)
def __init__(self, *args, **kwargs):
...