Определить тип объекта?

Есть ли простой способ определить, является ли переменная списком, словарем или чем-то еще? Я возвращаю объект обратно, который может быть либо типом, и мне нужно уметь отличать.

Ответы

Ответ 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!