Каков наилучший (идиоматический) способ проверки типа переменной Python?

Мне нужно знать, является ли переменная в Python строкой или dict. Что-то не так со следующим кодом?

if type(x) == type(str()):
    do_something_with_a_string(x)
elif type(x) == type(dict()):
    do_somethting_with_a_dict(x)
else:
    raise ValueError

Обновление. Я принял ответ Avisser (хотя я передумаю, если кто-то объяснит, почему isinstance предпочтительнее type(x) is).

Но благодаря nakedfanatic для напоминания мне, что он часто чище использовать dict (как аргумент case), чем серию if/elif/else.

Позвольте мне подробно остановиться на моем случае использования. Если переменная является строкой, мне нужно поместить ее в список. Если это dict, мне нужен список уникальных значений. Вот что я придумал:

def value_list(x):
    cases = {str: lambda t: [t],
             dict: lambda t: list(set(t.values()))}
    try:
        return cases[type(x)](x)
    except KeyError:
        return None

Если isinstance является предпочтительным, как бы вы написали эту функцию value_list()?

Ответ 1

Что произойдет, если кто-то передаст строку Unicode в вашу функцию? Или класс, полученный из dict? Или класс, реализующий похожий на интерфейс интерфейс? Следующий код охватывает первые два случая. Если вы используете Python 2.6, вы можете использовать collections.Mapping вместо dict в соответствии с ABC PEP.

def value_list(x):
    if isinstance(x, dict):
        return list(set(x.values()))
    elif isinstance(x, basestring):
        return [x]
    else:
        return None

Ответ 2

type(dict()) говорит "сделай новый дикт, а затем выясни, какой у него тип". Это быстрее сказать просто dict. Но если вы хотите просто проверить тип, более идиоматический способ - isinstance(x, dict).

Обратите внимание, что isinstance также включает подклассы (спасибо Дастин):

class D(dict):
    pass

d = D()
print("type(d) is dict", type(d) is dict)  # -> False
print("isinstance (d, dict)", isinstance(d, dict))  # -> True

Ответ 3

встроенные типы в Python встроены в имена:

>>> s = "hallo"
>>> type(s) is str
True
>>> s = {}
>>> type(s) is dict
True

btw обратите внимание на оператор is. Тем не менее, проверка типа (если вы хотите его называть) обычно выполняется путем переноса специфичного для типа теста в предложении try-except, поскольку это не столько тип переменной, которая важна, но и то, можете ли вы сделать определенный что-то с этим или нет.

Ответ 4

isinstance является предпочтительным по типу, потому что он также оценивает значение True, когда вы сравниваете экземпляр объекта с его суперклассом, что в основном означает, что вам никогда не понадобится специальный случай для вашего старого кода для его использования с подклассами dict или str.

Например:

 >>> class a_dict(dict):
 ...     pass
 ... 
 >>> type(a_dict()) == type(dict())
 False
 >>> isinstance(a_dict(), dict)
 True
 >>> 

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

Ответ 5

Думаю, я пойду за утиным набором слов - "если он будет ходить, как утка, он трясет как утку, ее утку". Таким образом вам не нужно беспокоиться о том, является ли строка юникодом или ascii.

Вот что я буду делать:

In [53]: s='somestring'

In [54]: u=u'someunicodestring'

In [55]: d={}

In [56]: for each in s,u,d:
    if hasattr(each, 'keys'):
        print list(set(each.values()))
    elif hasattr(each, 'lower'):
        print [each]
    else:
        print "error"
   ....:         
   ....:         
['somestring']
[u'someunicodestring']
[]

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

Ответ 6

Я думаю, что на самом деле было бы предпочтительнее сделать

if isinstance(x, str):
    do_something_with_a_string(x)
elif isinstance(x, dict):
    do_somethting_with_a_dict(x)
else:
    raise ValueError

2 Альтернативные формы, в зависимости от вашего кода, один или другой, вероятно, считается даже лучше, чем это. Один из них - не смотреть, прежде чем прыгнуть

try:
  one, two = tupleOrValue
except TypeError:
  one = tupleOrValue
  two = None

Другой подход основан на Гвидо и является формой перегрузки функций, которая делает ваш код более открытым.

http://www.artima.com/weblogs/viewpost.jsp?thread=155514

Ответ 7

Это должно сработать - так что нет, в коде нет ничего плохого. Однако это также можно сделать с помощью dict:

{type(str()): do_something_with_a_string,
 type(dict()): do_something_with_a_dict}.get(type(x), errorhandler)()

Немного более кратким и питоническим, не так ли?


Изменить.. Прислушавшись к совету Avisser, код также работает так и выглядит лучше:

{str: do_something_with_a_string,
 dict: do_something_with_a_dict}.get(type(x), errorhandler)()

Ответ 8

Возможно, вы захотите проверить typecheck. http://pypi.python.org/pypi/typecheck

Модуль проверки типов для Python

Этот пакет предоставляет мощные средства проверки типов времени выполнения для функций, методов и генераторов Python. Не требуя пользовательского препроцессора или изменения языка, пакет typecheck позволяет программистам и инженерам по обеспечению качества делать точные утверждения о вводе и выводе их кода.

Ответ 9

Я использовал другой подход:

from inspect import getmro
if (type([]) in getmro(obj.__class__)):
    # This is a list, or a subclass of...
elif (type{}) in getmro(obj.__class__)):
    # This one is a dict, or ...

Я не могу вспомнить, почему я использовал это вместо isinstance, хотя...

Ответ 10

*sigh*

Нет, аргументы typechecking в python не нужны. Это никогда необходимо.

Если ваш код принимает строку или объект dict, ваш дизайн не работает.

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

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

У вас есть следующие варианты:

1) Сделайте функцию unique_values, которая преобразует dicts в уникальные списки значений:

def unique_values(some_dict):
    return list(set(some_dict.values()))

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

myfunction([some_string])

Если вам нужно передать его dict, вы выполните:

myfunction(unique_values(some_dict))

Это ваш лучший вариант, он чист, понятен и поддерживается. Кто угодно чтение кода сразу же понимает, что происходит, и у вас нет к typecheck.

2) Сделайте две функции: одну, которая принимает списки строк и одну, которая принимает dicts. Вы можете сделать один звонок другим внутри, в наиболее удобном (myfunction_dict может создать список строк и вызвать myfunction_list).

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