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

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

15 ответов

Решение

Чтобы получить тип объекта, вы можете использовать встроенный 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

Вы можете сделать это с помощью type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>

Это может быть более Pythonic, чтобы использовать try... except блок. Таким образом, если у вас есть класс, который крякает, как список, или крякает, как диктат, он будет вести себя правильно, независимо от его типа.

Чтобы уточнить, предпочтительный метод "определения различий" между типами переменных - это то, что называется утиной типизацией: пока методы (и возвращаемые типы), на которые отвечает переменная, - это то, что ожидает ваша подпрограмма, обрабатывайте ее так, как вы ожидаете быть. Например, если у вас есть класс, который перегружает операторы скобок getattr а также setattr, но использует какую-то забавную внутреннюю схему, было бы уместно вести себя как словарь, если это то, что он пытается эмулировать.

Другая проблема с type(A) is type(B) проверка в том, что если A это подкласс B это оценивает false когда, программно, вы бы надеялись, что это будет true, Если объект является подклассом списка, он должен работать как список: проверка типа, представленного в другом ответе, предотвратит это. (isinstance будет работать, однако).

На экземплярах объекта у вас также есть:

__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 и в классах New-Style (возможно, из Python 2.6) класс и тип были объединены, и это может иногда привести к неожиданным результатам. Главным образом по этой причине мой любимый способ тестирования типов / классов - встроенная функция isinstance.

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

Определите тип объекта с помощью type

>>> obj = object()
>>> type(obj)
<class 'object'>

Хотя это работает, избегайте двойных символов подчеркивания, таких как __class__ - они не являются семантически публичными, и, хотя, возможно, и не в этом случае, встроенные функции обычно ведут себя лучше.

>>> obj.__class__ # avoid this!
<class 'object'>

проверка типа

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

Ну, это другой вопрос, не используйте тип - используйте 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 - согласно принципу подстановки Лискова, вы хотите иметь возможность использовать экземпляры подкласса, не нарушая ваш код - и 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-типирование и не проверять код явно. 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 явно проверять фактические подклассы или зарегистрированные абстракции.
  • И просто избегайте проверки типов там, где это имеет смысл.

Ты можешь использовать type() или же isinstance(),

>>> type([]) is list
True

Имейте в виду, что вы можете ударить 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

Будьте осторожны, используя isinstance

isinstance(True, bool)
True
>>> isinstance(True, int)
True

но типа

type(True) == bool
True
>>> type(True) == int
False

using type()

      x='hello this is a string'
print(type(x))

output

      <class 'str'>

to extract only the str use this

      x='this is a string'
print(type(x).__name__)#you can use__name__to find class

output

      str

if you use type(variable).__name__ it can be read by us

Хотя вопросы довольно старые, я наткнулся на это, когда сам нашел правильный путь, и я думаю, что он все еще нуждается в разъяснении, по крайней мере для 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

In many practical cases instead of using type or isinstance you can also use @functools.singledispatch, which is used to define generic functions (function composed of multiple functions implementing the same operation for different types).

In other words, you would want to use it when you have a code like the following:

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

Here is a small example of how it works:

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'>

Additionaly we can use abstract classes to cover several types at once:

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!

В дополнение к предыдущим ответам, стоит упомянуть существование collections.abc который содержит несколько абстрактных базовых классов (ABC), которые дополняют типизацию утки.

Например, вместо явной проверки, является ли что-то списком с:

isinstance(my_obj, list)

Вы можете, если вам интересно узнать, позволяет ли ваш предмет получать предметы, использовать collections.abc.Sequence:

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

если вы строго заинтересованы в объектах, которые позволяют получать, устанавливать и удалять элементы (например, изменяемые последовательности), вы бы выбрали collections.abc.MutableSequence,

Многие другие азбуки определены там, Mapping для объектов, которые могут быть использованы в качестве карт, Iterable, Callable и так далее. Полный список всего этого можно увидеть в документации по collections.abc ,

      value = 12
print(type(value)) # will return <class 'int'> (means integer)

или вы можете сделать что-то вроде этого

      value = 12
print(type(value) == int) # will return true

В общем, вы можете извлечь строку из объекта с именем класса,

str_class = object.__class__.__name__

и используя его для сравнения,

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..

type() это лучшее решение, чем isinstance()особенно для booleans:

True а также False это просто ключевые слова, которые означают 1 а также 0 в питоне. Таким образом,

isinstance(True, int)

а также

isinstance(False, int)

оба возвращаются True, Оба логических значения являются экземпляром целого числа. type()Впрочем, умнее:

type(True) == int

возвращается False,

Для полноты картины isinstance не будет работать для проверки типа подтипа, который не является экземпляром. Хотя это имеет смысл, ни один из ответов (включая принятый) не охватывает этого. Для этого используйте issubclass.

>>> class a(list):
...   pass
... 
>>> isinstance(a, list)
False
>>> issubclass(a, list)
True
Другие вопросы по тегам