Определить тип объекта? - PullRequest
1614 голосов
/ 09 февраля 2010

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

Ответы [ 12 ]

1830 голосов
/ 09 февраля 2010

Чтобы получить тип объекта, вы можете использовать встроенную функцию 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
155 голосов
/ 09 февраля 2010

Вы можете сделать это, используя type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>
39 голосов
/ 09 февраля 2010

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

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

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

33 голосов
/ 27 июня 2013

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

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

19 голосов
/ 15 марта 2017

Определить тип объекта 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-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 для явной проверки на наличие подклассов или зарегистрированных абстракций.
  • И просто избегайте проверки типов там, где это имеет смысл.
13 голосов
/ 01 июня 2014

Вы можете использовать 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
4 голосов
/ 14 ноября 2017

будьте осторожны при использовании экземпляра

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

но введите

type(True) == bool
True
>>> type(True) == int
False
4 голосов
/ 17 июня 2016

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

В дополнение к предыдущим ответам, стоит упомянуть о существовании 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.

0 голосов
/ 17 июня 2019

Во многих практических случаях вместо использования 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!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...