Який канонічний спосіб перевірити тип у Python?


1276

Який найкращий спосіб перевірити, чи є даний об’єкт даного типу? Як щодо перевірки, чи успадковується об’єкт від заданого типу?

Скажімо, у мене є об’єкт o. Як перевірити, чи є це str?


7
Ну, канонічний підхід у Python полягає у тому, щоб не перевіряти тип взагалі (якщо ви не налагоджуєте). Зазвичай ви просто намагаєтесь використовувати його як рядок (наприклад, об'єднати з іншими рядками, друкувати на консоль тощо); якщо ви думаєте, що це може вийти з ладу, використовуйте спробуйте / крім або hasattr. Однак, прийнята відповідь - це канонічний спосіб робити те, що ти, як правило, не повинен робити у світі Python. Для отримання додаткової інформації, Google «Python качку набравши» або прочитати їх: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883 / ...
Джон Кумбс

9
Я думаю, що містер Кумбс не помічає таких прикладів, як класи, що не піддаються JSON. Якщо розмістити великий фрагмент даних через функцію (чий код не може вплинути), ви можете перетворити певні фрагменти цих даних у, наприклад, <str> перед тим, як передавати їх. Принаймні, так я опинився на цій сторінці ...
Джон Каррелл

2
Здається, найпоширенішою причиною цього є те, що хочеться розрізняти рядки та ітерабелі рядків. Це складне питання, тому що рядки є ітерабелями рядків - односимвольна рядок є навіть послідовністю самої себе (востаннє я перевіряв - напевно, на неї не слід покладатися). Але хтось коли-небудь мав би користь для чогось струнного? Так . Отже, відповідь "Що мені робити, щоб розрізняти рядки та інші ітерабелі рядків?" правильно: "Це залежить від того, що ви намагаєтеся зробити". :-D
clacke

2
Анотації типу Python тепер є предметом. Погляньте на міпі
Шина

Відповіді:


1522

Щоб перевірити, чи oце екземпляр strчи якийсь підклас str, використовуйте isin substance (це був би "канонічний" спосіб):

if isinstance(o, str):

Щоб перевірити, чи oточно вказано тип str(виключайте підкласи):

if type(o) is str:

Наступне також працює, і може бути корисним у деяких випадках:

if issubclass(type(o), str):

Дивіться вбудовані функції у довідці бібліотеки Python для відповідної інформації.

Ще одна примітка: у цьому випадку, якщо ви використовуєте Python 2, ви, можливо, захочете використовувати:

if isinstance(o, basestring):

тому що це також буде вловлювати рядки Unicode ( unicodeне є підкласом str; і те, strі unicodeпідкласи basestring). Зауважте, що basestringбільше не існує в Python 3, де існує чітке розділення рядків ( str) та бінарних даних ( bytes).

Крім того, isinstanceприймає кортеж класів. Це повернеться, Trueякщо oце примірник будь-якого підкласу будь-якого з (str, unicode):

if isinstance(o, (str, unicode)):

31
str .__ підкласи __ () повертає лише прямі підкласи str і не робить те саме, що issubclass () або isin substance (). (Для цього вам доведеться рекурсивно викликати підкласи .__. __ ().
Thomas Wouters

15
Це хороша відповідь, але я думаю, що насправді слід почати з попередження про те, що зазвичай не варто робити цього в Python. Насправді це, здається, підтверджує припущення про те, що це "канонічна річ, що потрібно робити в Python", а це не так.
Джон Кумбс

4
Це відповіді python2. Наприклад, у python3 немає базових рядків.
dfrankow

4
Яка різниця між екземпляром і "точно"? Якщо type(a) is Objectце не так, це також неправда isinstance(a, Object). Однак, якщо type(a) is SubClassOfObject, то type(a) is Object == False, але isinstance(a, Object) == True. Правильно?
mavavilj

1
@mavavilj - a is bозначає, що a і b - це абсолютно те саме, тобто посилання на одну і ту ж сутність в пам'яті. Отже, aі bповинен був би бути той самий клас, а не підкласи, як і у isinstance(). Дивіться, наприклад, stackoverflow.com/a/133024/1072212
Terry Brown

196

Найбільш Pythonic спосіб перевірити тип об'єкта є ... не перевірити.

Оскільки Python заохочує друкувати Duck , вам слід просто try...exceptвикористовувати методи об’єкта так, як ви хочете їх використовувати. Отже, якщо ваша функція шукає файл, що записується, не перевіряйте, чи це підклас file, просто спробуйте використовувати його .write()метод!

Звичайно, іноді ці приємні абстракції руйнуються і isinstance(obj, cls)це те, що потрібно. Але використовуйте економно.


75
ІМХО, найбільш піфонічний спосіб - це впоратися з будь-яким аргументом, який подано. У своєму коді я часто не можу знати, чи отримую я об'єкт або масив об'єктів, і я використовую внутрішню перевірку типу, щоб перетворити один об'єкт у список одного елемента.
зустріч

14
Скоріше просто намагаються використовувати його метод запису, є випадки, коли ви хочете це зробити, не викликаючи винятку. У цьому випадку ви могли б зробити ... if hasattr(ob, "write") and callable(ob.write): Або зберегти доступ до func = getattr(ob, "write", None) if callable(func): ...
кадру

142
Введення качки - це використання бібліотеки. Перевірка типу стосується написання бібліотеки. Не той самий проблемний домен.
RickyA

16
@RickyA, я не згоден. Типи качок - це взаємодія з об'єктами з використанням інтерфейсів з добре відомою семантикою. Це може стосуватися або коду бібліотеки, або коду, який використовує таку бібліотеку.
Дан Ленскі

6
@ nyuszika7h, в Python3 hasattrлише пригнічує AttributeError - Див.: docs.python.org/3.4/library/functions.html#hasattr
ideaman42

57

isinstance(o, str)повернеться, Trueякщо oє strабо має тип, який успадковує str.

type(o) is strповернеться Trueлише тоді, якщо oце str. Він повернеться, Falseякщо oмає тип, який успадковує str.


6
Звичайно, це не вдасться, якщо об'єкт не є екземпляром 'str', а натомість щось подібне до рядка. Як і unicode, mmap, UserString або будь-який інший визначений користувачем тип. Звичайний підхід в Python - це не робити перевірки.
Thomas Wouters

6
Вам не потрібно вибачатися, добре відповісти на власне запитання. ТАК для відповідей, а не для карми.
Елі Бендерський

2
Це дуже корисно. Тому що різниця між isinstanceі type(var) == type('')незрозуміла.
зустріч

30

Після того, як запитання було задано і відповіли, підказки типу були додані до Python . Підказки типу в Python дозволяють перевіряти типи, але дуже відрізняються від статично набраних мов. Підказки типу в Python пов'язують очікувані типи аргументів з функціями, як дані, доступні для виконання, пов'язані з функціями, і це дозволяє перевіряти типи. Приклад синтаксису підказки типу:

def foo(i: int):
    return i

foo(5)
foo('oops')

У цьому випадку ми хочемо викликати помилку, foo('oops')оскільки є анотований тип аргументу int. Доданий підказки типу не спричиняє помилок при нормальному виконанні сценарію. Однак він додає атрибути до функції, що описує очікувані типи, які інші програми можуть запитувати та використовувати для перевірки на наявність помилок типу.

Однією з цих інших програм, за допомогою яких можна знайти помилку типу, є mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Можливо, вам доведеться встановити mypyваш менеджер пакунків. Я не думаю, що він постачається з CPython, але, схоже, він має певний рівень "офіційності".)

Перевірка типу таким чином відрізняється від перевірки типів у статично набраних мовах складених. Оскільки типи є динамічними в Python, перевірку типів потрібно проводити під час виконання, що спричиняє витрати - навіть на правильні програми - якщо ми наполягаємо на тому, що це відбувається в будь-якому випадку. Явні перевірки типу також можуть бути більш обмежуючими, ніж це потрібно, і спричиняти зайві помилки (наприклад, чи справді аргумент повинен бути саме такого listтипу чи достатньо чогось ітеративного?).

Переваги чіткої перевірки типу полягають у тому, що вона може виявляти помилки раніше та давати чіткіші повідомлення про помилки, ніж набирання качки. Точні вимоги до типу качки можна висловити лише за допомогою зовнішньої документації (сподіваємось, це ґрунтовна та точна), а помилки несумісних типів можуть траплятися далеко не там, де вони походять.

Підказки типу Python мають на меті запропонувати компроміс, у якому типи можна вказати та перевірити, але немає додаткових витрат під час звичайного виконання коду.

У typingзмінних пакету пропозицій типу , які можуть бути використані в натяків типу висловлювати необхідні моделі поведінки , не вимагаючи певних типів. Наприклад, він включає в себе такі змінні, як Iterableі Callableпідказки для уточнення потреби в будь-якому типі з такою поведінкою.

Хоча підказки типу є найбільш піфонічним способом перевірки типів, часто навіть пітонічніше навіть не перевіряти типи і покладатися на друкування качок. Підказки щодо типу є відносно новими, і присяжні все ще залишаються на виборах, коли вони є найбільш пітонічним рішенням. Порівняно неперервне, але дуже загальне порівняння: Підказки типу надають форму документації, яку можна застосувати, дозволяють коду генерувати раніше та простіше зрозуміти помилки, вловлювати помилки, які введення качки не може, і їх можна перевірити статично (у незвичній сенс, але це все ще поза часом виконання). З іншого боку, набирання качок вже давно є піфонічним способом, не нав'язує когнітивних накладів статичного набору тексту, є менш багатослівним і сприймає всі життєздатні типи, а потім і деякі.


2
-1: mypy спеціально називає себе "статичним типом перевірки", тому я не впевнений, звідки у вас "перевірка типу повинна виконуватися під час виконання".
Кевін

@Kevin В ретроспективі це було непотрібним відступом, але щоб отримати більше інформації, підказки типу Python перетворюються на дані виконання та mypyє модулем Python, який використовує importlibдля доступу до цих даних. Чи це "перевірка статичного типу" - це філософське питання, але воно відрізняється від того, що більшість очікує, оскільки в них задіяні звичайний перекладач мови та імпортна техніка.
Праксеоліт

4
Це теж не вірно. Він використовує typed_ast, який сам по собі є лише клоном ast з додатковими можливостями. ast не імпортує модулі; вона аналізує їх на абстрактне синтаксичне дерево.
Кевін

18

Ось приклад, чому набирати качок - це зло, не знаючи, коли це небезпечно. Наприклад: Ось код Python (можливо, опускаючи належне відступ), зауважте, що цю ситуацію можна уникнути, піклуючись про функції речовини та issubclassof, щоб переконатися, що коли вам справді потрібна качка, ви не отримаєте бомбу.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

36
Навіть перевіряючи тип, ви можете створити class EvilDuck(Duck)та перекрити розмову (). Або, швидше за все, class ChineseCancerDuck(Duck)з неприємним побічним ефектом, який проявляється лише через роки. Вам буде краще просто контролювати свою дитину (і ретельно випробовувати її іграшки :)
Бретт Томас,

36
Бомби не говорять. Не додайте безглуздих методів, і цього не станеться.
праворуч

7
@Dmitry, це звичайна критика набору тексту качок: en.wikipedia.org/wiki/Duck_typing#Criticism ... ти в основному кажеш, що будь-який інтерфейс, щодо якого семантика не застосовується мовою, є злом. Я вважаю, що це більше підхід Java. Вся суть типінгу качок Python полягає в тому, що вона працює лише тоді, коли існує загальноприйнята конвенція про те, що означають конкретні інтерфейси. Наприклад, ви можете зв'язати багато Python-коду, змінивши __file__атрибут (зазвичай використовується для ідентифікації файлоподібних об'єктів), щоб означати щось інше.
Дан Ленскі

2
Це все зводиться до старого анекдота "Докторе, боляче, коли я це роблю". ... "Тоді не робіть цього". Незадовільно для того, хто звик "якщо він компілює, то працює", але саме тому тестування одержимості виросло з динамічного мовного світу.
clacke

1
@clacke, в основному, занадто дорого застосовувати типи під час виконання строго, тому що ВСЕ повинно бути об'єктом (для того, щоб зіставити рядок з можливим будь-яким типом), і занадто зручним, щоб не мати каченя, оскільки каченя дозволяє дійсно потужні методи прототипування, які долають речі, які як правило, дуже важко робити жорсткі інтерфейси. Крім того, будь-яка статична мова стикається з точкою, коли їй потрібно створити типи качок за допомогою динамічних бібліотек, оцінювання та струфікації або інтерфейсів, і ці речі по суті не роблять це злом, просто дуже потужним.
Дмитро

12

1
Хоча це посилання може відповісти на питання, краще включити сюди суттєві частини відповіді та надати посилання для довідки. Відповіді лише на посилання можуть стати недійсними, якщо пов’язана сторінка зміниться.
EKons

7

Я думаю, що цікаво про використання динамічної мови на зразок Python - це вам насправді не потрібно перевіряти щось подібне.

Я б просто зателефонував потрібним методам на ваш об'єкт і впіймав AttributeError. Згодом це дозволить вам викликати свої методи з іншими (здавалося б, не пов'язаними) об'єктами для виконання різних завдань, таких як знущання над об'єктом для тестування.

Я багато використовував це, коли вилучав дані з Інтернету, з urllib2.urlopen()якими повертає файл, як об’єкт. Це, в свою чергу, може бути передано майже будь-якому методу, який читається з файлу, оскільки він реалізує той самий read()метод, що і реальний файл.

Але я впевнений, що є час і місце для використання isinstance(), інакше його, мабуть, не було б :)


Хороший приклад того, коли ви повинні використовувати це, якщо ви розбираєте динамічний об'єкт json. Ви не знаєте заздалегідь, чи є поле рядком чи словником.
Сірий

6

Для більш складних перевірок типів мені подобається підхід перевірки вантажів на основі підказки типу python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Ви можете виконувати дуже складні перевірки дуже чистим і читабельним способом.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

6

Ви можете перевірити тип змінної, використовуючи __name__ типу.

Наприклад:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'

Дякую, це секретний код, який я хотів, коли я відображав його як відгук для користувача. Займав мене занадто довго, щоб знайти це ...
Аарон Д. Мараско

5

До Гюго:

Ви, мабуть, маєте на увазі, listа не array, але це вказує на всю проблему з перевіркою типів - ви не хочете знати, чи є спірний об'єкт списком, ви хочете знати, чи це якась послідовність чи це один об'єкт. Тому спробуйте використовувати його як послідовність.

Скажіть, що ви хочете додати об'єкт до вже наявної послідовності, або якщо це послідовність об'єктів, додайте їх усі

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Один фокус з цим полягає в тому, якщо ви працюєте з рядками та / або послідовностями рядків - це хитро, оскільки рядок часто розглядається як єдиний об'єкт, але це також послідовність символів. Гірше, ніж це, оскільки це дійсно послідовність однорядних рядків.

Зазвичай я вибираю розробити свій API так, щоб він приймав лише одне значення або послідовність - це полегшує справи. Не важко поставити [ ]навколо свого єдиного значення, коли ви передасте його в разі потреби.

(Хоча це може спричинити помилки з рядками, оскільки вони схожі на (є) послідовності.)


0

Простий спосіб перевірити тип - порівняти його з тим, чий тип ви знаєте.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True

-1

Я думаю, що найкращий спосіб - це добре ввести свої змінні. Це можна зробити за допомогою бібліотеки "набору тексту".

Приклад:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313) `

Див. Https://docs.python.org/3/library/typing.html


-7

Ви можете перевірити рядок нижче, щоб перевірити, для якого типу символу задане значення:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)

3
Ви впевнені, що не хочете видалити цю відповідь @Venkatesan?
Сірий
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.