У меня есть функция foobar
, которая ожидает, что ее параметр baz
будет любым объектом, реализующим __gt__
и __eq__
.
def foobar(baz, qux):
"""
:type baz: Any object that implements __gt__ and __eq__
"""
if baz >= qux:
return 'nice!'
return 'ouch!'
Существуют ли какие-либо соглашения о том, как следует документировать эти типы параметров? Я использую Python 3.5, если это важно.
Также проверьте Сопоставимые типы с mypy.
object
имеет атрибуты __eq__
и __gt__
, так что разве ваш критерий не будет верным, пока вы достаточно глубоко погружаетесь в грязь?
@timgeb: Честно говоря, не подумал об этом, хороший момент. Вопрос действительно в том, чтобы задокументировать этот тип утиного набора текста, так что давайте представим, что это не так? :)
Вы добились некоторого прогресса?
@timgeb: Боюсь, что нет, я ценю, что вы нашли время ответить, но я не уверен, как это связано с вопросом о документировании типа параметра?
@damd, возможно, вы пропустили подсказку типа в конце кода. Теперь вы можете делать def foo(arg: FooAndBar): ...
. Средства проверки типов должны иметь возможность информировать вас, когда вы передаете аргумент без требуемых методов.
Между прочим, ответ - небольшой хардкод, что нормально, если предположить, что существует всего несколько подходящих методов. Если вы хотите продолжить абстрагирование, вы можете написать себе функцию, которая принимает список имен методов и динамически создает ABC с указанными методами и соответствующим подклассом.
Это немного бессмысленно для __gt__
и __eq__
, потому что object
имеет эти атрибуты.
@timgeb: Didn't think of that to be honest, good point. The question is really about documenting this type of duck-typing, so let's pretend that's not the case? :)
В общем случае, я полагаю, вы можете написать себе ABC, который реализует __subclasshook__
, а затем напечатать этот класс.
from abc import ABCMeta, abstractmethod
class FooAndBar(metaclass=ABCMeta):
@abstractmethod
def foo(self):
raise NotImplementedError
@abstractmethod
def bar(self):
raise NotImplementedError
@classmethod
def __subclasshook__(cls, C):
if cls is FooAndBar:
has_foo = any('foo' in X.__dict__ for X in C.__mro__)
has_bar = any('bar' in X.__dict__ for X in C.__mro__)
if has_foo and has_bar:
return True
return NotImplemented
class Foo:
def foo(self):
pass
class FooBar:
def foo(self):
pass
def bar(self):
pass
print(isinstance(Foo(), FooAndBar)) # False
print(issubclass(Foo, FooAndBar)) # False
print(isinstance(FooBar(), FooAndBar)) # True
print(issubclass(FooBar, FooAndBar)) # True
def func(arg: FooAndBar):
pass
Обратите внимание, что определение foo
и bar
в FooAndBar
не требуется для вашей конкретной цели (в любом случае срабатывает крючок подкласса), но пропуск методов выглядел бы очень странно для меня и, вероятно, любого читателя кода.
Вы можете добавить блок try-except для обработки ситуаций, когда вы не можете сравнивать объекты параметров и добавлять описание в документацию.