python дженерики что такое

Дженерики / шаблоны в python?

как python обрабатывает сценарии универсального / шаблонного типа? Скажем, я хочу создать внешний файл «BinaryTree.py» и пусть он обрабатывает двоичные деревья, но для любого типа данных.

поэтому я мог бы передать ему тип пользовательского объекта и иметь двоичное дерево этого объекта. Как это делается в python?

8 ответов

Python использует утиной типизацией, поэтому для обработки нескольких типов не требуется специальный синтаксис.

Если вы из фона C++, вы запомните это, пока операции, используемые в функции/классе шаблона, определены для некоторого типа T (на уровне синтаксиса), вы можете использовать этот тип T в шаблоне.

Итак, в основном, он работает одинаково:

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

на самом деле теперь вы можете использовать дженерики в Python 3.5+. См.PEP-484 и ввод в библиотеке документации.

в соответствии с моей практикой это не очень бесшовно и ясно, особенно для тех, кто знаком с Java Generics, но все еще можно использовать.

поскольку python динамически типизирован, это очень просто. Фактически, вам нужно будет выполнить дополнительную работу для класса BinaryTree, чтобы не работать с любым типом данных.

например, если вы хотите, чтобы значения ключей, которые используются для размещения объекта в дереве, были доступны в объекте из метода key() вы просто называете key() на объекты. Например:

обратите внимание, что вам никогда не нужно определять, какой класс object_to_insert. До тех пор, пока у него есть key() метод, он будет работать.

исключение, если вы хотите, чтобы он работал с основными типами данных, такими как строки или целые числа. Вам придется обернуть их в класс, чтобы заставить их работать с вашим общим BinaryTree. Если это звучит слишком тяжело, и вы хотите дополнительную эффективность на самом деле просто хранения строк, извините, это не то, что Python хорош.

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

чтобы продемонстрировать, что я имею в виду, этот класс дерева примет что-нибудь для своих двух ветвей:

и его можно использовать следующим образом:

после того, как я придумал некоторые хорошие мысли о создании общих типов в python, я начал искать других, у которых была та же идея, но я не мог найти их. Итак, вот оно. Я пробовал это, и это работает хорошо. Это позволяет нам параметризовать наши типы в python.

Теперь вы можете вывести типы из этого универсального типа.

это решение является упрощенным, и у него есть свои ограничения. Каждый раз, когда вы создаете универсальный тип, он создает новый тип. Таким образом, несколько классов наследуют List( str ) как родитель будет наследовать от двух классов. Чтобы преодолеть это, вам нужно создать dict для хранения различных форм внутреннего класса и возврата предыдущего созданного внутреннего класса, а не создания нового. Это предотвратит создание дубликатов типов с одинаковыми параметрами. Если интересно, более элегантное решение можно сделать с декораторами и / или метаклассами.

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

посмотрите, как это делают встроенные контейнеры. dict и list и так далее содержат гетерогенные элементы любых типов, которые вам нравятся. Если вы определяете, скажем, insert(val) функция для вашего дерева, она в какой-то момент сделает что-то вроде node.value = val и Python позаботится об остальном.

если вы используете Python 2 или хотите переписать java-код. Их не является реальным решением для этого. Вот что я получаю, работая ночью:https://github.com/FlorianSteenbuck/python-generics я все еще не получаю компилятор, поэтому вы в настоящее время используете его так:

TODOs

Источник

Дженерики / шаблоны в питоне?

Как Python обрабатывает сценарии универсального / шаблонного типа? Скажем, я хочу создать внешний файл «BinaryTree.py» и заставить его обрабатывать двоичные деревья, но для любого типа данных.

Таким образом, я мог передать ему тип пользовательского объекта и получить двоичное дерево этого объекта. Как это делается в Python?

10 ответов

Python использует типизацию утки, поэтому ему не требуется специальный синтаксис для обработки нескольких типов.

Если вы из C ++ фона, вы помните, что, пока операции, используемые в функции / классе шаблона, определены для некоторого типа T (на уровне синтаксиса), вы можете использовать этот тип < > в шаблоне.

Итак, в основном, это работает так же:

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

После того, как я придумал несколько хороших мыслей о создании универсальных типов в Python, я начал искать тех, у кого была такая же идея, но я не смог найти ни одной. Итак, вот оно. Я попробовал это, и это работает хорошо. Это позволяет нам параметризировать наши типы в python.

Теперь вы можете получать типы из этого универсального типа.

Это решение упрощенное и имеет свои ограничения. Каждый раз, когда вы создаете универсальный тип, он создает новый тип. Таким образом, несколько классов, наследующих List( str ) в качестве родителя, будут наследоваться от двух отдельных классов. Чтобы преодолеть это, вам нужно создать dict для хранения различных форм внутреннего класса и вернуть ранее созданный внутренний класс, а не создавать новый. Это предотвратит создание дублированных типов с одинаковыми параметрами. Если интересно, более элегантное решение может быть сделано с помощью декораторов и / или метаклассов.

Поскольку python динамически типизирован, это очень просто. На самом деле, вам придется проделать дополнительную работу, чтобы ваш класс BinaryTree не работал с каким-либо типом данных.

Исключение составляют случаи, когда вы хотите, чтобы он работал с базовыми типами данных, такими как строки или целые числа. Вам нужно будет обернуть их в классе, чтобы заставить их работать с вашим универсальным BinaryTree. Если это звучит слишком тяжело, и вам нужна дополнительная эффективность при простом хранении строк, извините, это не то, в чем Python хорош.

На самом деле теперь вы можете использовать дженерики в Python 3.5+. См. PEP-484 и типирование документации библиотеки.

Согласно моей практике, это не очень легко и понятно, особенно для тех, кто знаком с Java Generics, но все еще пригоден для использования.

Если вы используете Python 2 или хотите переписать Java-код. Их не реальное решение для этого. Вот что я получаю за ночь: https://github.com/FlorianSteenbuck/python-generics Я до сих пор не получил компилятор, так что вы сейчас используете его так:

Задачи

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

Чтобы продемонстрировать, что я имею в виду, этот класс дерева примет что-нибудь для своих двух ветвей:

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

С этим новым метаклассом мы можем переписать пример в ответе, на который я ссылаюсь как:

Этот подход имеет несколько приятных преимуществ

К счастью, были предприняты некоторые усилия для общего программирования на python. Существует библиотека: универсальная

Он не прогрессировал в течение многих лет, но вы можете иметь общее представление о том, как использовать и создавать свою собственную библиотеку.

Источник

Как работать с типизацией в Python

Первые упоминания о подсказках типов в языке программирования Python появились в базе Python Enhancement Proposals (PEP-483). Такие подсказки нужны для улучшения статического анализа кода и автодополнения редакторами, что помогает снизить риски появления багов в коде.

В этой статье мы рассмотрим основы типизации кода Python и ее роль в динамически-типизированном языке, эта информация будет наиболее полезна для начинающих Python-разработчиков.

Типизация в Python

Для обозначения базовых типов переменных используются сами типы:

Пример использования базовых типов в python-функции:

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

На первом месте стоит массив типов входных параметров, на втором — тип возвращаемого значения.

Про остальные абстрактные типы контейнеров можно прочитать в документации Python.

Также Python позволяет определять свои Generic-типы.

В данном примере TypeVar означает переменную любого типа, которую можно подставить при указании. Например:

На месте KeyType или ValueType могут быть конкретные типы.

Зачем это нужно

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

Конечно, можно написать и проще:

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

Использование Pydantic помогает корректно валидировать данные, при этом тип автоматически поменяется на требуемый.

Как можно заметить, более строгая типизация кода помогает сделать его проще и безопаснее. Однако, использование некоторых возможностей Pydantic может нежелательно повлиять на код. Так, мутация данных при валидации способна привести к тому, что тип значения модели будет непонятен. Например:

В данном примере созданный User после валидации будет иметь отличный от того, который был указан в модели. Это ведет к возможным крупным багам, которые лучше всегда избегать.

Также сейчас набирает большую популярность фреймворк FastAPI, который, благодаря Pydantic, позволяет быстро писать веб-приложения с автоматической валидацией данных.

В данном примере эндпоинт /item автоматически валидирует входящий json и передает его в функцию как требуемую модель.

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

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

Нововведения Python 3.9.0

Заключение

В этой статье мы рассмотрели некоторые типы в языке Python. В заключение отметим, что типизированный код в Python становится намного более читаемым и очевидным, что помогает проводить ревью в команде и не допускать глупых ошибок. Хорошее описание типов также позволяет разработчикам быстрее влиться в проект, понять, что происходит, и погрузиться в задачи. Также при использовании определенных библиотек удается в несколько раз сократить количество строк кода, которые ранее требовались только для валидации типов и значений.

Источник

GenericsВ¶

Defining generic classesВ¶

Programs can also define new generic classes. Here is a very simple generic class that represents a stack:

Using Stack is similar to built-in container types:

Type inference works for user-defined generic types as well:

Construction of instances of generic types is also type checked:

Generic class internalsВ¶

Generic aliases can be instantiated or subclassed, similar to real classes, but the above examples illustrate that type variables are erased at runtime. Generic Stack instances are just ordinary Python objects, and they have no extra runtime overhead or magic due to being generic, other than a metaclass that overloads the indexing operator.

Note that the generic aliases in typing don’t support constructing instances:

In Python 3.6 indexing generic types or type aliases results in actual type objects. This means that generic types in type annotations can have a significant runtime cost. This was changed in Python 3.7, and indexing generic types became a cheap operation.

Defining sub-classes of generic classesВ¶

User-defined generic classes and generic classes defined in typing can be used as base classes for another classes, both generic and non-generic. For example:

Generic can be omitted from bases if there are other base classes that include type variables, such as Mapping[KT, VT] in the above example. If you include Generic[. ] in bases, then it should list all type variables present in other bases (or more, if needed). The order of type variables is defined by the following rules:

If there are no Generic[. ] in bases, then all type variables are collected in the lexicographic order (i.e. by first appearance).

Generic functionsВ¶

Generic type variables can also be used to define generic functions:

As with generic classes, the type variable can be replaced with any type. That means first can be used with any sequence type, and the return type is derived from the sequence item type. For example:

Note also that a single definition of a type variable (such as T above) can be used in multiple generic functions or classes. In this example we use the same type variable in two generic functions:

A variable cannot have a type variable in its type unless the type variable is bound in a containing generic class or function.

Generic methods and generic selfВ¶

You can also define generic methods — just use a type variable in the method signature that is different from class type variables. In particular, self may also be generic, allowing a method to return the most precise type known at the point of access.

This feature is experimental. Checking code with type annotations for self arguments is still not fully implemented. Mypy may disallow valid code or allow unsafe code.

In this way, for example, you can typecheck chaining of setter methods:

Note also that mypy cannot always verify that the implementation of a copy or a deserialization method returns the actual type of self. Therefore you may need to silence mypy inside these methods (but not at the call site), possibly by making use of the Any type.

Variance of generic typesВ¶

Let us illustrate this by few simple examples:

This function needs a callable that can calculate a salary for managers, and if we give it a callable that can calculate a salary for an arbitrary employee, it’s still safe.

List is an invariant generic type. Naively, one would think that it is covariant, but let us consider this code:

Type variables with value restrictionВ¶

By default, a type variable can be replaced with any type. However, sometimes it’s useful to have a type variable that can only have some specific types as its value. A typical example is a type variable that can only have values str and bytes :

This is actually such a common type variable that AnyStr is defined in typing and we don’t need to define it ourselves.

We can use AnyStr to define a function that can concatenate two strings or bytes objects, but it can’t be called with other argument types:

Note that this is different from a union type, since combinations of str and bytes are not accepted:

In this case, this is exactly what we want, since it’s not possible to concatenate a string and a bytes object! The type checker will reject this function:

Another interesting special case is calling concat() with a subtype of str :

Type variables with upper boundsВ¶

In a call to such a function, the type T must be replaced by a type that is a subtype of its upper bound. Continuing the example above,

Type parameters of generic classes may also have upper bounds, which restrict the valid values for the type parameter in the same way.

A type variable may not have both a value restriction (see Type variables with value restriction ) and an upper bound.

Declaring decoratorsВ¶

One common application of type variable upper bounds is in declaring a decorator that preserves the signature of the function it decorates, regardless of that signature.

Note that class decorators are handled differently than function decorators in mypy: decorating a class does not erase its type, even if the decorator has incomplete type annotations.

Here’s a complete example of a function decorator:

From the final block we see that the signatures of the decorated functions foo() and bar() are the same as those of the original functions (before the decorator is applied).

The bound on F is used so that calling the decorator on a non-function (e.g. my_decorator(1) ) will be rejected.

Decorator factoriesВ¶

Functions that take arguments and return a decorator (also called second-order decorators), are similarly supported via generics:

Sometimes the same decorator supports both bare calls and calls with arguments. This can be achieved by combining with @overload :

Generic protocolsВ¶

The main difference between generic protocols and ordinary generic classes is that mypy checks that the declared variances of generic type variables in a protocol match how they are used in the protocol definition. The protocol in this example is rejected, since the type variable T is used covariantly as a return type, but the type variable is invariant:

This example correctly uses a covariant type variable:

See Variance of generic types for more about variance.

Generic protocols can also be recursive. Example:

Generic type aliasesВ¶

Type aliases can be imported from modules just like other names. An alias can also target another alias, although building complex chains of aliases is not recommended – this impedes code readability, thus defeating the purpose of using aliases. Example:

A type alias does not define a new type. For generic type aliases this means that variance of type variables used for alias definition does not apply to aliases. A parameterized generic alias is treated simply as an original type with the corresponding type variables substituted.

Источник

Тип универсального псевдонима GenericAlias в Python.

Например, функция ожидает список, содержащий элементы с float :

В этом примере функция ожидает dict с ключами типа str и значениями типа int :

Содержание:

Встроенные функции isinstance() и issubclass() не принимают типы GenericAlias ​​в качестве второго аргумента:

Среда выполнения Python не применяет [аннотации типов][]. Это распространяется на универсальные типы и их параметры типа. При создании объекта из GenericAlias ​​элементы контейнера не проверяются на соответствие их типу.

Например, следующий код не рекомендуется к использованию, но он будет работать без ошибок:

Кроме того, параметризованные универсальные шаблоны стирают параметры типа во время создания объекта:

Вызов функций repr() или str() для универсального типа показывает параметризованный тип:

Метод дженериков __getitem__() вызовет исключение, чтобы запретить такие ошибки, как dict[str][str] :

Стандартные коллекции универсальных типов:

Специальные атрибуты универсального псевдонима.

Все параметризованные универсальные шаблоны реализуют специальные атрибуты, доступные только для чтения.

genericalias.__origin__ :

Этот атрибут указывает на непараметрический универсальный класс:

genericalias.__args__ :

Этот атрибут представляет собой кортеж универсальных типов (возможно, длиной 1), переданный исходному методу __class_getitem__() универсального контейнера:

genericalias.__parameters__ :

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *