Есть ли простой способ определить, является ли переменная списком, словарем или чем-то еще? Я возвращаю объект обратно, который может быть либо типом, и мне нужно уметь отличать.
Определить тип объекта?
Ответ 1
Чтобы получить тип объекта, вы можете использовать встроенную функцию type()
. Передача объекта в качестве единственного параметра возвращает объект типа этого объекта:
>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True
>>> type({})
<type 'dict'>
>>> type([])
<type 'list'>
Это, конечно, также работает для пользовательских типов:
>>> class Test1 (object):
pass
>>> class Test2 (Test1):
pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True
Обратите внимание, что type()
будет возвращать только непосредственный тип объекта, но не сможет рассказать вам о наследовании типа.
>>> type(b) is Test1
False
Чтобы покрыть это, вы должны использовать функцию isinstance
. Это, конечно, также работает для встроенных типов:
>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True
isinstance()
обычно является предпочтительным способом обеспечения типа объекта, поскольку он также принимает производные типы. Поэтому, если вам не нужен объект типа (по какой-либо причине), использование isinstance()
предпочтительнее type()
.
Второй параметр isinstance()
также принимает кортеж типов, поэтому его можно сразу проверить на несколько типов. isinstance
затем вернет true, если объект имеет любой из этих типов:
>>> isinstance([], (tuple, list, set))
True
Ответ 2
Вы можете сделать это с помощью type()
:
>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>
Ответ 3
Возможно, Pythonic может использовать блок try
... except
. Таким образом, если у вас есть класс, который взломает, как список, или quacks, как dict, он будет вести себя правильно, независимо от того, каков его тип на самом деле.
Чтобы уточнить, предпочтительный метод "говорить разницу" между типами переменных - это что-то, называемое утиная печать: до тех пор, пока методы ( и возвращаемые типы), на которые отвечает переменная, - это то, что ожидает ваша подпрограмма, рассматривайте ее так, как вы ожидаете. Например, если у вас есть класс, который перегружает операторы скобки getattr
и setattr
, но использует какую-то смешную внутреннюю схему, было бы разумно вести себя как словарь, если это то, что он пытается подражать.
Другая проблема с проверкой type(A) is type(B)
заключается в том, что если A
является подклассом B
, он оценивает false
, когда программно вы надеетесь, что это будет true
. Если объект является подклассом списка, он должен работать как список: проверка типа, представленного в другом ответе, будет препятствовать этому. (isinstance
будет работать, однако).
Ответ 4
В экземплярах объекта вы также имеете:
__class__
атрибут. Вот пример, взятый из консоли Python 3.3
>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
... pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>
Остерегайтесь того, что в классах python 3.x и в классах нового стиля (возможно, из Python 2.6) класс и тип были объединены, и это может привести к неожиданным результатам. В основном по этой причине мой любимый способ тестирования типов/классов заключается в isinstance встроенной функции.
Ответ 5
Определить тип объекта Python
Определите тип объекта с помощью type
>>> obj = object()
>>> type(obj)
<class 'object'>
Хотя он работает, избегайте двойных символов подчеркивания, таких как __class__
- они не являются семантически общедоступными и, хотя, возможно, и не в этом случае, встроенные функции обычно имеют лучшее поведение.
>>> obj.__class__ # avoid this!
<class 'object'>
проверка типов
Есть ли простой способ определить, является ли переменная списком, словарем или чем-то еще? Я возвращаю объект обратно, который может быть либо типом, и мне нужно уметь отличать.
Хорошо, что другой вопрос, не используйте type-use isinstance
:
def foo(obj):
"""given a string with items separated by spaces,
or a list or tuple,
do something sensible
"""
if isinstance(obj, str):
obj = str.split()
return _foo_handles_only_lists_or_tuples(obj)
Это относится к случаю, когда ваш пользователь может делать что-то умное или разумное путем подкласса str
- в соответствии с принципом Liskov Substitution, вы хотите иметь возможность использовать экземпляры подкласса, не нарушая ваш код, и isinstance
поддерживает это.
Использовать абстракции
Еще лучше, вы можете найти конкретный базовый базовый класс от collections
или numbers
:
from collections import Iterable
from numbers import Number
def bar(obj):
"""does something sensible with an iterable of numbers,
or just one number
"""
if isinstance(obj, Number): # make it a 1-tuple
obj = (obj,)
if not isinstance(obj, Iterable):
raise TypeError('obj must be either a number or iterable of numbers')
return _bar_sensible_with_iterable(obj)
Или просто не указывать явно
Или, может быть, лучше всего использовать утиную печать, и явным образом не проверяю ваш код. Duck-typing поддерживает Liskov Substitution с большей элегантностью и меньшей детализацией.
def baz(obj):
"""given an obj, a dict (or anything with an .items method)
do something sensible with each key-value pair
"""
for key, value in obj.items():
_baz_something_sensible(key, value)
Заключение
- Используйте
type
, чтобы фактически получить класс экземпляра. - Используйте
isinstance
для явной проверки фактических подклассов или зарегистрированных абстракций. - И просто избегайте проверки типов там, где это имеет смысл.
Ответ 6
Вы можете использовать type()
или isinstance()
.
>>> type([]) is list
True
Предупредите, что вы можете clobber list
или любой другой тип, назначив переменную в текущей области с тем же именем.
>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'
Выше мы видим, что dict
переназначается в строку, поэтому тест:
type({}) is dict
... выходит из строя.
Чтобы обойти это и использовать type()
более осторожно:
>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True
Ответ 7
В то время как вопросы довольно старые, я наткнулся на это, когда узнал правильный путь сам, и я думаю, что он все еще нуждается в уточнении, , по крайней мере, для Python 2.x (не проверял Python 3, но поскольку проблема возникает с классическими классами, которые ушли на такую версию, это, вероятно, не имеет значения).
Здесь я пытаюсь ответить на вопрос заголовка: как определить тип произвольного объекта? Другие предложения об использовании или отсутствии использования isinstance хороши во многих комментариях и ответах, но я не рассматриваю эти проблемы.
Основная проблема с подходом type()
заключается в том, что он не работает должным образом со старыми экземплярами:
class One:
pass
class Two:
pass
o = One()
t = Two()
o_type = type(o)
t_type = type(t)
print "Are o and t instances of the same class?", o_type is t_type
Выполнение этого фрагмента даст:
Are o and t instances of the same class? True
Что, я утверждаю, не то, что ожидали бы большинство людей.
Подход __class__
является самым близким к правильности, но он не будет работать в одном критическом случае: когда переданный объект является классом старого стиля (а не экземпляром!), поскольку эти объекты не имеют таких атрибут.
Это самый маленький фрагмент кода, который я мог бы думать о том, что он удовлетворяет таким законным вопросам согласованным образом:
#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
obj_type = getattr(obj, "__class__", _NO_CLASS)
if obj_type is not _NO_CLASS:
return obj_type
# AFAIK the only situation where this happens is an old-style class
obj_type = type(obj)
if obj_type is not ClassType:
raise ValueError("Could not determine object '{}' type.".format(obj_type))
return obj_type
Ответ 8
будьте осторожны, используя isinstance
isinstance(True, bool)
True
>>> isinstance(True, int)
True
но введите
type(True) == bool
True
>>> type(True) == int
False
Ответ 9
Как и в предыдущих ответах, стоит упомянуть о существовании collections.abc
, который содержит несколько абстрактных базовых классов (ABC), которые дополнять утиную печать.
Например, вместо явной проверки, есть ли что-то в списке:
isinstance(my_obj, list)
если вам интересно, только если объект, который вы имеете, позволяет получать элементы, используйте collections.abc.Sequence
:
from collections.abc import Sequence
isinstance(my_obj, Sequence)
если вас строго интересуют объекты, которые позволяют получать, устанавливать и удалять элементы (например, изменяемые последовательности), вы выбрали бы collections.abc.MutableSequence
.
Здесь определены многие другие ABC, Mapping
для объектов, которые могут использоваться как карты, Iterable
, Callable
и т.д. Полный список из них можно найти в документации для collections.abc
.
Ответ 10
type()
- лучшее решение, чем isinstance()
, особенно для booleans
:
True
и False
- это просто ключевые слова, которые означают 1
и 0
в Python. Таким образом,
isinstance(True, int)
а также
isinstance(False, int)
оба возвращают True
. Оба логических значения являются экземпляром целого числа. type()
, однако, является более умным:
type(True) == int
возвращает False
Ответ 11
Как правило, вы можете извлечь строку из объекта с именем класса,
str_class = object.__class__.__name__
и использовать его для сравнения,
if str_class == 'dict':
# blablabla..
elif str_class == 'customclass':
# blebleble..
Ответ 12
Во многих практических случаях вместо использования type
или isinstance
вы также можете использовать @functools.singledispatch
, который используется для определения универсальных функций (функция, состоящая из нескольких функций, реализующих одну и ту же операцию для разных типов).
Другими словами, вы хотели бы использовать его, когда у вас есть код, подобный следующему:
def do_something(arg):
if isinstance(arg, int):
... # some code specific to processing integers
if isinstance(arg, str):
... # some code specific to processing strings
if isinstance(arg, list):
... # some code specific to processing lists
... # etc
Вот небольшой пример того, как это работает:
from functools import singledispatch
@singledispatch
def say_type(arg):
raise NotImplementedError(f"I don't work with {type(arg)}")
@say_type.register
def _(arg: int):
print(f"{arg} is an integer")
@say_type.register
def _(arg: bool):
print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>
Кроме того, мы можем использовать абстрактные классы для одновременного использования нескольких типов:
from collections.abc import Sequence
@say_type.register
def _(arg: Sequence):
print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!