Определение списка или кортежа в Python, исключая строки
Быстрый ответ
Чтобы провести проверку, является ли переменная obj
списком или кортежем, используйте данную конструкцию с функцией isinstance(obj, (list, tuple))
:
if isinstance(obj, (list, tuple)):
print("Да, это список или кортеж, но никак не строка.")
TESCT. Этот метод коректно идентифицирует, относится ли объект к типам списков или кортежей, исключая тем самым строки, которые в Python 3 не являются подтипами tuple
или list
.
Не спешите судить об объекте по его типу: "Утиная" типизация и последовательности
Преимущества "утиной" типизации
В Python широко распространён принцип "утиной" типизации. Согласно ему, вместо того, чтобы проверять тип объекта, вы можете попытаться выполнить с ним операции и обработать возможные исключения:
try:
obj[0] # Попытка обращения к первому элементу
print("Скорее всего, это список или кортеж.")
except (TypeError, IndexError):
print("Какое-то несоответствие. Скорее всего, это не то, что нам нужно...")
Однако данный подход может быть неприменим к определенным типам последовательностей, которые могут притворяться списками или кортежами (например, dict_keys
).
Это птица? Это самолет? Нет, это последовательность!
Если вы больше заботитесь о поведении объекта, а не о его конкретном типе, проверьте наличие методов, которые характерны для последовательностей. Ведь суть "утиной" типизации заключается в том, что если объект ведёт себя как утка, значит это и есть утка.
if hasattr(obj, '__getitem__') and not isinstance(obj, str):
print("Судя по всему, перед нами список или кортеж.")
В данном случае мы не ограничиваемся строгой проверкой типов, что позволяет нам идентифицировать и другие объекты, имитирующие последовательности.
Учтите особенности вашего Python: Совместимость версий
Минутка ностальгии: Python 2 и проверка строк
Если вы до сих пор используете Python 2, то для исключения строк вы могли бы проверить наличие типа basestring
, который является родителем для str
и unicode
:
assert not isinstance(obj, basestring) # Здесь мы говорим: "Извините, строк не обнаружено."
В Python 3 же вместо basestring
используется просто str
:
assert not isinstance(obj, str)
Абстрактные базовые классы: элегантный подход
Если у вас используется Python 3.8 или новее, вы можете стильно использовать collections.abc.Sequence для идентификации последовательностей, при этом не забудьте исключить строки:
from collections.abc import Sequence
if isinstance(obj, Sequence) and not isinstance(obj, str):
print("Это последовательность, но ни в коем случае не строка.")
Будьте внимательны: начиная с Python 3.3 произошли некоторые изменения в абстрактных базовых классах модуля Collections.
С учётом необходимости скорости: Производительность
Гонка за производительностью
Выбор гибкости в ущерб скорости может негативно отразиться на производительности. Проверки, основанные на поведении (которые, на самом деле, более универсальны), могут быть медленнее, чем использование isinstance
при работе с определёнными типами объектов в рамках масштабных операций.
Специализированные функции для особых объектов
В некоторых случаях может быть разумным создать специализированные функции для различных типов, применяя isinstance
для оптимизации в зависимости от типа объекта:
if isinstance(obj, tuple):
# Функции для кортежей
elif isinstance(obj, list):
# Функции для списков
Главное — помните, что не стоит увлекаться оптимизацией, рискуя при этом потерять гибкость Python.
Визуализация
Представьте, что у нас есть конвейер завешенный разными товарными позициями:
Конвейер (🚚): [📦, 📦, "📜", ("📄", "📄"), ["📦", "📦"]]
for item in conveyor_belt:
if isinstance(item, (list, tuple)):
print("Обнаружены списки или кортежи на конвейере! 📦➡️✅")
else:
print("Возможно, это не наш товар. 📜❌")
Результат проверки:
📦➡️✅: [📦, 📦]
📜❌: "📜"
📦➡️✅: ("📄", "📄")
📦➡️✅: ["📦", "📦"]
Главным для нас являются упаковки, то есть коробки и пакеты (списки и кортежи). Строки (бумага) нам не нужны!
Возможные проблемы: Подводные камни и специальные случаи
Маскирующиеся объекты
Берегитесь объектов, притворяющихся списками и кортежами: они могут успешно ввести вас в заблуждение, проявляя поведение, соответствующее протоколу __getitem__
. Если это для вас критично, проверьте их на соответствие протоколам последовательностей.
Рекурсивная проверка: Внимание, рекурсия!
Осторожнее с рекурсивными проверками списков и кортежей и фактом, что они могут увести вас в бесконечную рекурсию. Всегда помните о базовом случае для прерывания рекурсии.
Изменения пространства имен: Приспособление к изменениям
Используя абстрактные базовые классы из модуля collections
, принимайте во внимание совместимость с различными версиями Python.
Прагматический подход: функциональность превыше всего
Когда речь идет о том, чтобы определить, больше ли объект похож на список или кортеж, сфокусируйтесь на его функциональности, а не просто на типе. Проверьте наличие необходимых методов и соответствие ожидаемому поведению.