Как структурировать добавление методов в класс в Python для их импорта

#python

Вопрос:

Новичок в Python здесь — из Perl, так что помилуйте, если я спрашиваю о вещах, связанных с perl.

Я пытаюсь расширить функциональность существующего класса Python (svgpathtools) с помощью дополнительного модуля.

Другими словами, я хотел бы иметь где-нибудь в одном файле (он же модуль, предоставляющий расширение):

 def foo(path_a, path_b):
    d=0
    for t in range(0, 101, 1):
        d = d   complex_distance(path_a.point(t / 100), path_b.point(t / 100))
    return d / 100

setattr(svgpathtools.Path, 'foo', foo)
 

а затем в файле, использующем расширение

уметь делать что-то подобное:

 import svgpathtools.foo

some_path.foo(some_other_path)
 

Я хотел бы знать:

  • как назвать/где сохранить файл, расширяющий модуль svpathtools (т. Е.: каково соответствие между именем модуля и именем файла)
  • как это должно выглядеть, чтобы исправить методы svgtools

Моя самая большая проблема — выяснить, как называются подобные вещи, чтобы я мог самостоятельно гуглить. Полные объяснения приветствуются, но указателей для дальнейших исследований мне достаточно, чтобы приступить к работе.

Комментарии:

1. Почему бы не использовать обычное наследование?

2. Не волнуйтесь, для детоксикации от Perl требуется время. 🙂

3. Я не уверен, что исправление ошибок вне тестов уместно. IMHO Лучше наследовать от базового класса или просто объявить эту функцию как автономную и вызвать ее напрямую, без ссылки на svgpathtools. setattr при импорте не является… явным для конечного пользователя, если вам все еще нужно исправить, пожалуйста, оберните его в какую-нибудь install функцию

4. Более серьезно, у Perl и Python очень разные представления о том, как реализуются классы. В Perl класс-это просто пакет. В Python класс сильно отличается от модуля (а пакет-это просто модуль, содержащий другие модули). svgpathtools сам по себе это вовсе не класс; это модуль. svgtools.Path является атрибутом этого модуля, значением которого является класс. Хотя вы можете добавить новый метод в существующий класс, более вероятно, что вы захотите использовать наследование или композицию для определения нового класса, который будет использоваться вместо него.

5. Насколько я помню, Perl даже не поддерживает наследование в языке; существует отдельная библиотека для определения отношения наследования между двумя пакетами. Perl 6 / Raku, возможно, изменил бы это, но у меня никогда не было необходимости (или большого желания) изучать этот язык.

Ответ №1:

В Python метод-это просто атрибут класса, который оказывается функцией, имеющей объект (по соглашению называемый self ) в качестве своего первого аргумента. А класс-это просто объект. Поэтому исправление класса для добавления новых методов или замены существующих (это называется исправлением обезьяны) является тривиальным. Но наследование-гораздо более чистый способ получить тот же результат: новое поведение инкапсулируется в отдельный класс с отдельным именем, что может избавить от дальнейших проблем с обслуживанием…

Вот тривиальный пример с базовым классом, который может вычислить двойное его исходное значение, и расширением, которое может вычислить четверку (* 4):

 class A:
    def __init__(self, a):
        self._val = a
    def double(self):
        return 2 * self._val
 

Пример использования:

 >>> a = A(2)
>>> a.double()
4
 

Давайте выведем его:

 class B(A):
    def quad(self):
        return 4 * self._val
 

которые можно использовать таким образом:

 >>> b = B(2)
>>> b.double()
4
>>> b.quad()
8
 

Вот исправленная версия обезьяны:

 >>> A.quad = lambda self: 4 * self._val
 

что также дает

 >>> a = A(2)
>>> a.quad()
8
 

Но этот способ глобально изменил A класс, который больше не уважает свой исходный (и, надеюсь, документированный API). Если это не будет прокомментировано красным мигающим шрифтом, будущий мастер, безусловно, обвинит вас в этом…

Тем не менее, исправление обезьян может иметь смысл для исправления ошибки в модуле, которую вы не можете (или не хотите) исправлять на уровне исходного кода…

Комментарии:

1. Спасибо — я упускаю одну деталь: где (т. Е. в каком файле) должно произойти исправление mokey, чтобы его можно было использовать повторно (через import )

2. Если вы хотите, чтобы он был импортирован стандартным способом, вам действительно следует рассмотреть наследование. Вы можете исправить класс только после загрузки модуля, в котором он объявлен. И поскольку патч является глобальным, вы должны быть осторожны с побочными эффектами в других модулях, использующих тот же класс…