Зачем нужен super python

Объясните как работает метод super,а так-же конструкция super().__init__() в python? [закрыт]

Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы он был сосредоточен только на одной проблеме.

Объясните как работает метод super , а так-же конструкция super().__init__() в python? super часто используется в классах. Желательно объяснить как можно проще и на простых примерах.

Отличный ответ но гуглом я умею пользоваться.Читаю и разбираюсь но на данный момент понять не могу, сюда написал затем,что-бы может у кого нибудь получилось объяснить.

1 ответ 1

super() — это функция, которая обращается к классу, от которого наследуется текущий.

class A: def some_method(self): print('Spam, eggs!!1') class B(A): def some_method(self): print('Hello, World!') x = B() x.some_method() 

Как Вы можете заметить, мы перегрузили метод родительского класса. Но что, если нам потребуется всего лишь немного дополнить его? Как нам дополнить родительский метод, не копируя его полностью и не изобретая велосипедов? Тут нам и нужен super() :

class A: def some_method(self): print('Spam, eggs!!1') class B(A): def some_method(self): super().some_method() print('Hello, World!') x = B() x.some_method() 

С помощью super().some_method() мы вызвали родительский метод, а после дополнили свой. Именно для этого чаще всего используется эта функция.

__init__() — это метод инициализации класса, следовательно super().__init__() вызывает метод инициализации из родительского класса. Например, чтобы дополнить его.

Источник

Необычный Python в обычных библиотеках

Специалист в Data Science из Amazon буквально прочитал код самых распространённых библиотек Python. В этом материале он делится секретами работы с Python, о которых узнал из этих библиотек. За подробностями приглашаем под кат к старту нашего флагманского курса по Data Science:

Вызов super() в базовых классах

Функция super() в Python позволяет наследовать базовые классы (они же суперклассы или родительские классы) без необходимости явно ссылаться на базовый класс. Обычно метод super() используется в методе __init__ . Множественное наследование практически невозможно без super() , хотя оно может быть удобно при одиночном наследовании.

Одно из интересных применений super() — его вызов в классе базовом классе. Этот приём я заметил в requests.adapters, в BaseAdapter :

class BaseAdapter: """The Base Transport Adapter""" def __init__(self): super().__init__() def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): 

Базовый класс ни от чего не наследуется, так зачем же вызывать в нём super() ?

Немного покопавшись, я узнал вот что: в базовом классе ключевое слово super() позволяет реализовать совместное множественное наследование. Без него вызовы __init__ родительских классов — после класса без super — пропускаются. Ниже — пример с базовым классом BaseEstimator и миксином ServingMixin , где DecisionTree унаследует оба класса.

Итак, у нас BaseEstimator , который в своём __init__ не вызывает super() . Для вывода атрибутов у него есть базовый метод __repr__ :

class BaseEstimator: def __init__(self, name, **kwargs): self.name = name def __repr__(self): return f', '.join(f': ' for k, v in vars(self).items()) 

Затем мы наследуем BaseEstimator через подкласс DecisionTree . Всё работает нормально — при печати экземпляра DecisionTree отображаются атрибуты BaseEstimator и DecisionTree :

class DecisionTree(BaseEstimator): def __init__(self, depth, **kwargs): super().__init__(**kwargs) self.depth = depth dt = DecisionTree(name='DT', depth=1) print(dt) > name: DT, depth: 1 

Теперь пронаследуемся от ServingMixin и создадим экземпляр DecisionTree :

class ServingMixin: def __init__(self, mode, **kwargs): super().__init__(**kwargs) self.mode = mode class DecisionTree(BaseEstimator, ServingMixin): def __init__(self, depth, **kwargs): super().__init__(**kwargs) self.depth = depth dt = DecisionTree(name='Request Time DT', depth=1, mode='online') print(dt) > name: Request Time DT, depth: 1 dt.mode > AttributeError: 'DecisionTree' object has no attribute 'mode' 

Заметно, что ServingMixin не наследуется должным образом: атрибут ServingMixin ( mode ) не отображается, когда выводится экземпляр дерева решений, и, если мы пытаемся получить доступ к атрибуту mode , то окажется, что он не существует.

Это связано с тем, что без super() в BaseEstimator класс DecisionTree не вызывает следующий родительский класс в порядке разрешения методов.

Исправить это можно вызовом super() в BaseEstimator , и DecisionTree заработает, как ожидалось:

class BaseEstimator: def __init__(self, name, **kwargs): self.name = name super().__init__(**kwargs) def __repr__(self): return f', '.join(f': ' for k, v in vars(self).items()) class ServingMixin: def __init__(self, mode, **kwargs): super().__init__(**kwargs) self.mode = mode class DecisionTree(BaseEstimator, ServingMixin): def __init__(self, depth, **kwargs): super().__init__(**kwargs) self.depth = depth dt = DecisionTree(name='Request Time DT', depth=1, mode='online') print(dt) > name: Request Time DT, mode: online, depth: 1 dt.mode > 'online' 

Именно поэтому мы можем захотеть вызвать super() в базовом классе.

Когда использовать миксины?

Mixin — это класс, предоставляющий реализации методов для повторного использования дочерними классами. Он представляет ограниченную форму множественного наследования и родительский класс, который просто даёт функциональные возможности подклассам, не содержит состояния и не предназначен для создания экземпляров. Scikit-learn широко использует миксины. Это ClassifierMixin , TransformerMixin , OutlierMixin и т. д.

Когда использовать миксины? Они подходят, когда хочется:

  • предоставить множество дополнительных функций для класса;
  • использовать определённую функцию во множестве разных классов.
from werkzeug import BaseRequest class Request(BaseRequest): pass 

Если нужно добавить поддержку заголовка accept , перепишем код вот так:

from werkzeug import BaseRequest, AcceptMixin class Request(AcceptMixin, BaseRequest): pass 

Нужна поддержка пользовательского агента, аутентификации и т. д.? Нет проблем, просто добавьте миксины:

from werkzeug import BaseRequest, AcceptMixin, UserAgentMixin, AuthenticationMixin class Request(AcceptMixin, UserAgentMixin, AuthenticationMixin, BaseRequest): pass 

Благодаря модуляризации этих функций в виде миксинов (а не добавления в класс) базовый класс не раздувается функциями, которые могут использовать только несколько подклассов. Кроме того, эти миксины теперь могут повторно использоваться другими дочерними классами, которые могут не наследоваться от BaseRequest .

Использование относительного импорта

Относительный импорт гарантирует, что мы ищем текущий пакет (и импортируем из него) перед поиском в остальной части PYTHONPATH . Он работает, если перед импортом поставить . — вот пример из base.py от Sklearn:

from .utils.validation import check_X_y from .utils.validation import check_array 

Что произойдёт, если base.py не использует относительный импорт? Если у нас есть пакет с именем utils в каталоге нашего скрипта, во время импорта Python будет искать наш пакет utils , а не utils от sklearn, тем самым нарушая работу sklearn. Точка в выражении импорта гарантирует, что base.py в sklearn сначала ищет utils от Sklearn.

Есть ли другая причина не использовать относительный импорт? Пожалуйста, оставьте комментарий ниже.

Когда добавлять код в __init__.py

__init__.py помечает каталоги как каталоги пакетов Python. Обычная практика — оставлять __init__.py пустыми. Тем не менее во многих библиотеках, которые я читал, были непустые, а иногда длинные файлы __init__.py . Это заставило меня задуматься, что и почему можно добавить в __init__.py .

Во-первых, мы можем добавить в __init__.py импорт, когда хотим реорганизовать код, который вырос в несколько модулей, без критических изменений для существующих пользователей. Допустим, у нас есть один модуль ( models.py ), который содержит реализацию для DecisionTree и Bandit . Со временем этот единственный модуль превращается в пакет моделей с модулями для tree и bandit . Чтобы обеспечить согласованность API для существующих пользователей, в __init__.py в пакете моделей мы можем добавить следующее:

from .tree import DecisionTree, RandomForest from .bandit import Bandit, TSBandit 

Это гарантирует, что существующие пользователи смогут продолжить импорт через from models import DecisionTree , а не from models.tree import DecisionTree . Для них API не меняется, а существующий код не ломается.

Это подводит к ещё одной причине добавить код в __init__.py , а именно — предоставить упрощённый API, чтобы пользователям не приходилось вникать в детали реализации:

app __init__.py model_implementation.py data_implementation.py 

Вместо того чтобы заставлять пользователей решать, что импортировать из model_implementation и data_implementation , мы можем всё упростить, добавив в __init__.py такой код:

from .model_implementation import SimpleModel from .data_implementation import SimpleDataLoader 

В нём говорится, что SimpleModel и SimpleDataLoader — единственные части приложения, с которыми должны работать пользователи, и это упрощает использование пакета приложения (например, from app import SimpleModel, SimpleDataLoader ). И, если пользователи знают, что делают, и хотят импортировать напрямую из model_implementation , то это тоже возможно.

Так делается в Pandas, где __init__.py импортируются типы данных, считыватели (reader) и API изменения формы, а ещё так делается в Accelerate от Hugging Face.

Помимо упомянутого выше применения, мы также можем захотеть:

  • инициализировать логгер в __init__.py основного пакета, чтобы использовать его в нескольких модулях;
  • выполнить проверки совместимости.

Когда использовать экземпляр, класс и статические методы

Краткий обзор методов, которые можно реализовать для класса. Такие методы пригодятся, когда:

  • Методам экземпляра нужен экземпляр класса, и доступ к нему они могут получить через self .
  • Методам класса не нужен экземпляр. Таким образом, они не могут получить доступ к экземпляру ( self ), но им доступен к класс cls
  • Статические методы не имеют доступа к self или cls . Они работают как обычные функции, но относятся к пространству имён классов.

Методы класса используются, когда хочется вызвать его, но не создавать его экземпляр. Обычно это происходит, когда не нужна информация об экземпляре, но нужна информация о классе (т. е. о другом его классе или статических методах). Методы класса можно использовать как конструктор. Преимущество методов класса заключается в том, что класс не нужно хардкодить, также они позволяют подклассам работать с методами.

Статические методы используются, когда нам не нужны аргументы класса или экземпляра, но некий метод связан с классом и удобно, чтобы этот метод находился в пространстве имён класса. Это могут быть специфичные для класса служебные методы. Оформляя метод как статический, мы улучшаем читаемость и понимание кода, сообщаем, что метод не зависит от класса или экземпляра.

Скрытая функция conftest.py

Обычно conftest.py используется для предоставления фикстур для всего каталога. После определения фикстуры в conftest.py , они могут использоваться любым тестом в пакете без необходимости импортировать их. Этот файл также используется для загрузки внешних плагинов и определения хуков, как setup и teardown.

Но, просматривая sklearn, я наткнулся на пустой conftest.py с этим интересным комментарием:

# Даже если этот файл пустой, он полезен тем, что при запуске из корневой папки # ./sklearn добавляется в sys.path с помощью pytest. Смотрите # https://docs.pytest.org/en/latest/explanation/pythonpath.html. # Например, это позволяет создавать расширения на месте и запускать pytest # doc/modules/clustering.rst, а также использовать sklearn из локальной папки, а не только из site-packages. 

Оказывается, sklearn использовал менее известную функцию conftest.py : его существование в корневом пути гарантирует, что pytest распознает модули без необходимости указывать PYTHONPATH . В фоновом режиме pytest изменяет sys.path , включая все найденные в корневом пути подмодули.

Поясняющая документация библиотек

Помимо чтения кода, мы можем учиться, читая документы, объясняющие библиотеку. Давайте сосредоточимся на принципах проектирования библиотек.

  • согласованность: все объекты имеют общий согласованный интерфейс из ограниченного набора методов;
  • композицию, когда, где это возможно, объекты реализуются с помощью существующих строительных блоков.

Другой пример — fastai, где используется многоуровневый подход. Фреймворк предоставляет высокоуровневый API, готовые к использованию функции обучения моделей для различных приложений. Высокоуровневый API построен на основе иерархии низкоуровневых API, последние предоставляют компонуемые стандартные блоки. Этот многоуровневый подход позволяет через настройку API-интерфейсов среднего уровня быстро создать прототип перед его кастомизацией.

Изображение

Вот некоторые необычные способы работы с Python, о которых я узнал, читая эти билиотеки:

А мы поможем прокачать ваши навыки или с самого начала освоить профессию, востребованную в любое время:

Источник

Читайте также:  Animated background for css
Оцените статью