Разница между collections.abc.Sequence и typing.Sequence

Я читал статью о collection.abc и классе ввода в стандартной библиотеке Python и обнаружил, что оба класса имеют одинаковые функции.

Я попробовал оба варианта, используя приведенный ниже код, и получил те же результаты.

from collections.abc import Sequence

def average(sequence: Sequence):
    return sum(sequence) / len(sequence)

print(average([1, 2, 3, 4, 5]))  # result is 3.0

from typing import Sequence

def average(sequence: Sequence):
    return sum(sequence) / len(sequence)

print(average([1, 2, 3, 4, 5])) # result is 3.0


При каком условии collection.abc станет лучшим вариантом для набора текста. Есть ли преимущества использования одного над другим?

В более старых версиях Python typing.Sequence — это общая версия collections.abc.Sequence, используемая для набора текста. В последних версиях оба являются общими, а typing.Sequence по существу устарела.

joel 30.01.2023 19:33
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

На самом деле, в вашем коде вам не нужен ни один из них:

Ввод с аннотациями, который вы делаете с импортированным классом Sequences, является дополнительной функцией, предназначенной для (1) быстрой документации; (2) проверка кода перед его запуском с помощью статических анализаторов кода, таких как Mypy.

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

Пока объект, который вы передаете в свою функцию, учитывает часть интерфейса Sequence, который ему понадобится, он будет работать (ему нужны __len__ и __getitem__ как есть)

Просто запустите свой код без аннотаций и посмотрите, как он работает:

def average(myvariable):
    return sum(myvariable) / len(myvariable)

Тем не менее, вот что происходит: list — это превосходная «последовательность» в Python, которая реализует все, что нужно последовательности.

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

С другой стороны, collections.abc.Sequence предшествует необязательным рекомендациям по вводу в PEP 484: он работает как «виртуальный суперкласс», который может указывать все, что работает как последовательность во время выполнения (с помощью isinstance) (*). И его можно использовать в качестве надежного базового класса для реализации собственных полнофункциональных классов cusotm Sequence: просто наследуйте от collections.abc.Sequence и реализуйте функциональные методы __getitem__ и __len__, как указано в документации здесь: https://docs.python.org /3/library/collections.abc.html (то есть для последовательностей только для чтения — для изменяемых последовательностей отметьте collections.abc.MutableSequence, конечно).

(*) чтобы ваша пользовательская реализация последовательности была распознана как собственно последовательность, она должна быть «зарегистрирована» во время выполнения с вызовом collections.abc.Sequence.register. Однако, насколько мне известно, большинство инструментов для статической проверки типов не распознают это и будут ошибаться в своем статическом анализе)

Спасибо @jsbueno, я знаю, что он будет работать без аннотаций. Я зачислен на буткемп и изучаю аннотацию типов, отсюда и причина первоначального вопроса.

Oluwasube 30.01.2023 15:29

да - теперь я завершил ответ фактическими различиями между обоими Sequence.

jsbueno 30.01.2023 15:35
Ответ принят как подходящий

Хорошо, что вы используете аннотации типов! Как говорится в документации, если вы используете Python 3.9+, вам, скорее всего, никогда не следует использовать typing.Sequence из-за его устаревания. С момента введения универсальных типов псевдонимов в 3.9 классы collections.abc все поддерживают подписку и должны правильно распознаваться средствами проверки статических типов всех разновидностей.

Таким образом, преимущество использования collections.abc.T вместо typing.T в основном заключается в том, что последний устарел и не должен использоваться.

Как упомянул jsbueno в своем ответе, аннотации никогда не будут иметь последствий во время выполнения, если, конечно, они не будут явно выбраны фрагментом кода. Они просто неотъемлемая часть хорошего стиля кодирования. Но ваша функция все равно будет работать, то есть ваш скрипт будет выполняться без ошибок, даже если вы аннотируете свою функцию чем-то абсурдным, например def average(sequence: 4%3): ....


Правильные аннотации по-прежнему чрезвычайно ценны. Таким образом, я бы порекомендовал вам как можно скорее привыкнуть к некоторым из лучших практик. (Более или менее строгая проверка статического типа, такая как mypy, очень полезна для этого.) Во-первых, когда вы используете универсальные типы, такие как Sequence, вы всегда должны предоставлять соответствующие аргументы типа. Это могут быть переменные типа, если ваша функция также является универсальной, или они могут быть конкретными типами, но вы всегда должны их включать.

В вашем случае, если вы ожидаете, что содержимое вашего sequence будет чем-то, что можно добавить с тем же типом и разделить на целое число, вы можете, например, захотеть. обозначьте это как Sequence[float]. (В системе типов Python float считается супертипом int, хотя номинального наследования нет.)

Еще одна рекомендация — постараться использовать как можно более широкие типы параметров. (Это перекликается с парадигмой динамической типизации Python.) Идея состоит в том, что вы просто указываете, что ожидаемый объект должен уметь «крякать», но не говорите, что он должен быть уткой.

В вашем примере, поскольку вы полагаетесь на совместимость аргумента с sum , а также с len , вам следует подумать, какие типы ожидают эти функции. Функция len проста, так как она просто вызывает метод __len__ объекта, который вы ей передаете. Функция sum более тонкая, но в вашем случае важная часть заключается в том, что она ожидает итерируемого элементов, которые можно добавить (например, float).

Если вы посмотрите на Азбуку коллекций, вы заметите, что Sequence на самом деле предлагает гораздо больше, чем вам нужно, поскольку это обратимая коллекция. Collection — это самый широкий встроенный тип, который соответствует вашим требованиям, потому что он имеет __iter__ (из Iterable) и __len__ (из Sized). Итак, вы могли бы сделать это вместо этого:

from collections.abc import Collection


def average(numbers: Collection[float]) -> float:
    return sum(numbers) / len(numbers)

(Кстати, имя параметра не должно отражать его тип.)

Наконец, если вы хотите сделать все возможное и быть максимально широким, вы можете определить свой собственный протокол , который даже шире, чем Collection (путем избавления от наследования Container):

from collections.abc import Iterable, Sized
from typing import Protocol, TypeVar


T = TypeVar("T", covariant=True)


class SizedIterable(Sized, Iterable[T], Protocol[T]):
    ...


def average(numbers: SizedIterable[float]) -> float:
    return sum(numbers) / len(numbers)

Это имеет то преимущество, что поддерживает очень широкое структурное подтипирование, но, скорее всего, это излишне.

(Чтобы ознакомиться с основами типизации Python, обязательно прочтите PEP 483 и PEP 484.)

«Они просто неотъемлемая часть хорошего стиля кодирования» — не могли бы вы поделиться некоторыми доказательствами этого утверждения? Лично я по-прежнему остаюсь с авторами PEP484, у которых «[...] нет желания когда-либо делать подсказки типов обязательными, даже по соглашению», и я работаю над некоторыми нетипизированными кодовыми базами, не чувствуя себя «как я могу жить без аннотаций».

SUTerliakov 30.01.2023 20:21

@SUTerliakov Трудно представить доказательства этого. Хотя и выражено в терминах констатации факта, это, конечно, только мое мнение. Что-то вроде «стиля» — такая неуловимая метрика, что я сомневаюсь, что мы сможем достичь чего-то вроде «окончательного консенсуса». Я лично не могу представить себе ситуацию, когда было бы неплохо (или лучше, чем включать) исключить аннотации типов, если не считать того, что буквально сколачивают одноразовый скрипт для какого-то специального действия. Но это, вероятно, обсуждение для другого места.

Daniil Fajnberg 30.01.2023 20:54

Другие вопросы по теме