Метка: типы данных

Узнать тип type() и проверить тип isinstance()

В Python у нас утиная динамическая типизация, поэтому бывает что нужно узнать тип переменной. Функция type() возвращает тип аргумента или, учитывая, что в Python – все класс, то класс аргумента. Результат можно сравнить с известным типом:

>>> i, s = 10, "hello"
>>> type(i)
<class 'int'>
>>> type(i) is int
True
>>> type(s) is str
True
>>> class A: pass
...
>>> a = A()
>>> type(a) is A
True

Можно создать экземпляр объекта того же класса, что и переменная:

>>> new_a = type(a)()
>>> type(new_a) == type(a)
True

⚠️ Нужно знать! type() не принимает во внимание наследование. Тип наследника отличается от типа родителя:

>>> class B(A): pass
...
>>> type(A()) is type(B())
False

Лучший способ проверить типы – функция isinstance(x, type) (instance англ. – экземпляр). Она возвращает True, если первый аргумент является экземпляром класса во втором аргументе:

>>> isinstance(i, int)
True
>>> isinstance(s, str)
True
>>> isinstance(a, A)
True

Функция поддерживает наследование:

class A: pass
class B(A): pass
b = B()
>>> isinstance(b, A)
True

И самое крутое: вторым аргументом допускается передать кортеж из типов, и isinstance вернет True, если хоть один из типов в кортеже подходит:

>>> isinstance(i, (int, float))
True
>>> isinstance(a, (A, B))
True

Загадка:

class A: ...
a = A()
class A: ...
print(isinstance(a, A))

Правильный ответ был False!

Объяснение. Динамическая натура Python позволяет переопределить класс во время интерпретации. Помните, как недавно я рассказывал про декораторы класса? Там мы подменяли один класс другим. Вот это из той же оперы. Тут мы подменили один класс, другим классом, отвязав имя А от старого класса и привязав его к новому. Старый класс А остался жив только как класс объекта в переменной a. Старого и нового классов разные адреса (id):

class A: ...
print(id(A))  # 140479777819720

a = A()

class A: ...
print(id(A))  # 140479777809672

isinstance(a, A)  # False

Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈 

Перечисления (Enum)

В Python нет специального синтаксиса для перечислений, зато есть модуль enum и класс Enum в нем, от которого можно отнаследоваться для создания собственного перечисления:

from enum import Enum
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Задавать переменные этого типа можно несколькими способами:

c = Color(2)     # по значению
c = Color['RED'] # по строковому имени
c = Color.RED    # по члену класса

Значения из Enum человеко-читаемы при печати:

>>> print(Color.RED)
Color.RED

А также:

>>> Color.RED.name
'RED'
>>> Color.RED.value
1

Для сравнения эквивалентности используют оператор is (хотя == и != тоже работают):

if c is Color.RED:
    print('Red!')
if c is not Color.BLUE:
    print('Not blue!')

Для нескольких значений можно использовать in:

if c in (Color.BLUE, Color.GREEN):
    print('No red at all!')

Если неохота задавать значение самостоятельно, можно делать это автоматически:

from enum import Enum, auto
class Numbers(Enum):
    ONE = auto()
    TWO = auto()
    THREE = auto()
    FOUR = auto()

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

apples = {}
apples[Color.RED] = 'sweet'
apples[Color.GREEN] = 'sour'
>>> apples
{<Color.RED: 1>: 'sweet', <Color.GREEN: 2>: 'sour'}

Кратко создать перечислимый тип можно в функциональном стиле:

from enum import Enum
Animal = Enum('Animal', 'ANT BEE CAT DOG')

Семантика такого определения напоминает namedtuple. Первый аргумент – название перечисления, а второй – строка, где через пробел указаны названия вариантов. Пользоваться таким Enum можно также, как и заданным, через класс (см. выше), единственное, что могут быть проблемки с pickle

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

Animal = Enum('Animal', 'ANT, BEE, CAT, DOG')
Direction = Enum('Direction', ['NORTH', 'SOUTH', 'WEST', 'EAST'])
Color = Enum('Color', [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)])
Mood = Enum('Mood', {'HAPPY': ':-)', 'SAD': ':-('})

P.S. К сожалению, не все современные IDE понимают такое определение Enum, даже последний PyCharm ругается на аргументы и не активирует авто-дополнение по вариантам. Надеюсь, в будущем ситуация изменится в лучшую сторону.

🧙 Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈