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

StackOverflow https://stackoverflow.com/questions/378927

  •  22-08-2019
  •  | 
  •  

Вопрос

На этот вопрос уже есть ответ здесь:

Мне нужно знать, является ли переменная в 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

Обновить:Я принял ответ ависсера (хотя я изменю свое мнение, если кто-нибудь объяснит, почему 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() функция?

Это было полезно?

Решение

Что произойдет, если кто-то передаст строку unicode вашей функции?Или класс, производный от dict?Или класс, реализующий интерфейс, подобный dict?Следующий код охватывает первые два случая.Если вы используете Python 2.6, вы можете захотеть использовать collections.Mapping вместо того , чтобы dict в соответствии с АЗБУКА БОДРОСТИ ДУХА.

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

Другие советы

type(dict()) говорит "создайте новый 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

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

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

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

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

Например:

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

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

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

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

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

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

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

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

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

myfunction([some_string])

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

myfunction(unique_values(some_dict))

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top