Каков наилучший (идиоматический) способ проверки типа переменной 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.

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

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 предпочтительнее, как напиши это

10 ответов


что произойдет, если кто-то передаст строку unicode вашей функции? Или класс, производный от dict? Или класс, реализующий интерфейс, подобный 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

type(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

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

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

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


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

например:

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

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


Я думаю, что я пойду на подход к набору утки - "если он ходит, как утка, он крякает, как утка, его утка". Таким образом, вам не нужно будет беспокоиться о том, является ли строка unicode или 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, я использовал его, но в последнее время познакомился с точной концепцией этого и очень рад этому. Поэтому я хотел бы знать, является ли это излишним делать.


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

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

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

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


Это должно работать-так что нет, нет ничего плохого в вашем коде. Тем не менее, это также можно сделать с помощью 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)()

вы можете проверить typecheck. http://pypi.python.org/pypi/typecheck

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

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


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

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,...


*sigh*

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

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

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

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

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

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

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

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

myfunction([some_string])

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

myfunction(unique_values(some_dict))

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

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

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