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


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


1356


источник


Ответы:


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

1575



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

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

138



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

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

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


37



На экземплярах объекта вы также имеете:

__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 встроенный функция.


29



Определить тип объекта 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



You can use type() or isinstance().

>>> type([]) is list
True

Be warned that you can clobber list or any other type by assigning a variable in the current scope of the same name.

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

Above we see that dict gets reassigned to a string, therefore the test:

type({}) is dict

...fails.

To get around this and use type() more cautiously:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True

10



While the questions is pretty old, I stumbled across this while finding out a proper way myself, and I think it still needs clarifying, at least for Python 2.x (did not check on Python 3, but since the issue arises with classic classes which are gone on such version, it probably doesn't matter).

Here I'm trying to answer the title's question: how can I determine the type of an arbitrary object? Other suggestions about using or not using isinstance are fine in many comments and answers, but I'm not addressing those concerns.

The main issue with the type() approach is that it doesn't work properly with old-style instances:

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

Executing this snippet would yield:

Are o and t instances of the same class? True

Which, I argue, is not what most people would expect.

The __class__ approach is the most close to correctness, but it won't work in one crucial case: when the passed-in object is an old-style class (not an instance!), since those objects lack such attribute.

This is the smallest snippet of code I could think of that satisfies such legitimate question in a consistent fashion:

#!/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

4



As an aside to the previous answers, it's worth mentioning the existence of collections.abc which contains several abstract base classes (ABCs) that complement duck-typing.

For example, instead of explicitly checking if something is a list with:

isinstance(my_obj, list)

you could, if you're only interested in seeing if the object you have allows getting items, use collections.abc.Sequence:

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

if you're strictly interested in objects that allow getting, setting and deleting items (i.e mutable sequences), you'd opt for collections.abc.MutableSequence.

Many other ABCs are defined there, Mapping for objects that can be used as maps, Iterable, Callable, et cetera. A full list of all these can be seen in the documentation for collections.abc.


3



be careful using isinstance

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

but type

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

1