Подтвердить что ты не робот

Тип Python() или __class__, == или

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

obj.__class__ == Foo
obj.__class__ is Foo
type(obj) == Foo
type(obj) is Foo

Есть ли причины выбирать один за другим? (различия в производительности, ловушки и т.д.)

Другими словами: а) существует ли какая-либо практическая разница между использованием __class__ и type(x)? b) являются объектами класса, всегда безопасными для сравнения, используя is?


Обновление: Спасибо всем за отзыв. Я все еще озадачен тем, являются ли объекты класса одиночными, мой здравый смысл говорит, что это так, но было действительно сложно получить подтверждение (попробуйте googling для "python", "class" и "unique" или "singleton" ).

Я также хотел бы пояснить, что для моих конкретных нужд лучшее "дешевое" решение, которое работает, является лучшим, поскольку я пытаюсь оптимизировать большинство из нескольких специализированных классов (почти достигая точки где разумная задача - сбросить Python и развить этот конкретный модуль на C). Но причина этого вопроса заключалась в том, чтобы лучше понять язык, поскольку некоторые из его функций немного неясны для меня, чтобы легко найти эту информацию. Вот почему я позволяю дискуссиям немного расшириться, а не поселиться за __class__ is, поэтому я могу услышать мнение более опытных людей. Пока это было очень плодотворно!

Я провел небольшой тест, чтобы сравнить производительность 4 альтернатив. Результаты профилировщика были:

               Python  PyPy (4x)
type()    is   2.138   2.594
__class__ is   2.185   2.437
type()    ==   2.213   2.625
__class__ ==   2.271   2.453

Неудивительно, что is работает лучше, чем == для всех случаев. type() лучше работает на Python (на 2% быстрее) и __class__ лучше работает в PyPy (на 6% быстрее). Интересно отметить, что __class__ == лучше работает в PyPy, чем type() is.


Обновление 2: многие люди, похоже, не понимают, что я имею в виду, когда "класс является одноэлементным", поэтому я приведу пример:

>>> class Foo(object): pass
...
>>> X = Foo
>>> class Foo(object): pass
...
>>> X == Foo
False
>>> isinstance(X(), Foo)
False
>>> isinstance(Foo(), X)
False

>>> x = type('Foo', (object,), dict())
>>> y = type('Foo', (object,), dict())
>>> x == y
False
>>> isinstance(x(), y)
False

>>> y = copy.copy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True
>>> y = copy.deepcopy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True

Не имеет значения, есть ли N объектов типа type, заданных объектом, только один будет его классом, поэтому в этом случае безопасно сравнивать для справки. И поскольку сравнение ссылок всегда будет дешевле сравнения значений, я хотел бы знать, действительно ли мое утверждение выше. Я пришел к выводу, что это так, если только кто-то не докажет доказательства.

4b9b3361

Ответ 1

Для классов старого стиля существует разница:

>>> class X: pass
... 
>>> type(X)
<type 'classobj'>
>>> X.__class__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class X has no attribute '__class__'
>>> x = X()
>>> x.__class__
<class __main__.X at 0x171b5d50>
>>> type(x)
<type 'instance'>

Точкой классов нового стиля было унифицировать класс и тип. Технически говоря, __class__ - единственное решение, которое будет работать как для экземпляров класса new, так и для старого стиля, но также будет генерировать исключение для объектов класса старого стиля. Вы можете вызвать type() для любого объекта, но не для каждого объекта __class__. Кроме того, вы можете гадать с __class__ таким образом, что вы не можете гадать с type().

>>> class Z(object):
...     def __getattribute__(self, name):
...             return "ham"
... 
>>> z = Z()
>>> z.__class__
'ham'
>>> type(z)
<class '__main__.Z'>

Лично у меня обычно есть среда только с классами нового стиля, и в качестве стиля предпочитают использовать type(), поскольку я обычно предпочитаю встроенные функции, когда они существуют для использования магических атрибутов. Например, я также предпочел бы bool(x) на x.__nonzero__().

Ответ 2

Результат type() эквивалентен obj.__class__ в новых классах стилей, а объекты класса небезопасны для сравнения, используя is, вместо этого используйте ==.

Для новых классов стиля предпочтительный путь здесь будет type(obj) == Foo.

Как отметил Майкл Хоффман в своем ответе, между новыми и старыми классами стилей есть разница, поэтому для обратного совместимого кода вам может понадобиться использовать obj.__class__ == Foo.

Для тех, кто утверждает, что isinstance(obj, Foo) является предпочтительным, рассмотрим следующий сценарий:

class Foo(object):
    pass

class Bar(Foo):
    pass

>>> obj = Bar()
>>> isinstance(obj, Foo)
True
>>> type(obj) == Foo
False

OP хочет поведение type(obj) == Foo, где оно будет ложным, хотя Foo является базовым классом Bar.

Ответ 3

is следует использовать только для проверки личности, а не для проверки типа (существует исключение из правила, в котором вы можете и должны использовать is для проверки против одиночных чисел).

Примечание. Обычно я бы не использовал type и == для проверок типов. Предпочтительным способом проверки типа является isinstance(obj, Foo). Если у вас когда-либо есть причина проверить, что что-то не является экземпляром подкласса, это пахнет мне как рыбный дизайн. Если class Foo(Bar):, то Bar - Foo, и вам следует избегать ситуаций, когда какая-то часть вашего кода должна работать с экземпляром Foo, но разбивается на Bar экземпляр.

Ответ 4

Обновление: Спасибо всем за отзыв. Я все еще озадачен тем, являются ли объекты класса одиночными, мой здравый смысл говорит, что это так, но было действительно сложно получить подтверждение (попробуйте googling для "python", "class" и "unique" или "singleton" ).

Я могу подтвердить, что __instance__ является одноэлементным. Вот доказательство.

>>> t1=File.test()
made class
>>> t2=File.test()
made class
>>> print t1.__class__()
made class
<File.test object at 0x1101bdd10>
>>> print t2.__class__()
made class
<File.test object at 0x1101bdd10>

Как вы можете видеть, как t1 и t2 распечатывают одно и то же значение в памяти, даже если t1 и t2 находятся в разных значениях в памяти. Вот доказательство этого.

>>> print t1
<File.test object at 0x1101bdc90>
>>> print t2
<File.test object at 0x1101bdcd0>

Метод __instance__ существует только в том случае, если вы использовали class "name"(object):. Если вы используете классический класс стиля class "name":, то метод __instance__ не существует.

То, что это означает, должно быть наиболее общим, которое вы, вероятно, хотите использовать type, если вы не знаете, что экземпляр факта существует.