Тип Python () или __class__, == или - PullRequest
45 голосов
/ 08 марта 2012

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

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

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

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


Обновление: Спасибо всем за отзывы. Я все еще озадачен тем, являются ли объекты класса синглетонами, мой здравый смысл говорит, что они есть, но было действительно трудно получить подтверждение (попробуйте поискать в поисках «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, для данного объекта только один будет его классом, поэтому в этом случае его можно сравнивать для справки. И поскольку сравнение ссылок всегда будет дешевле, чем сравнение значений, я хотел знать, верно ли мое утверждение выше. Я прихожу к выводу, что это так, если только кто-то не представит доказательств противного.

Ответы [ 4 ]

29 голосов
/ 08 марта 2012

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

>>> 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__ - единственное решение, которое будет работать как для новых, так и для старых экземпляров классов, но оно также вызовет исключение для самих объектов классов старого стиля.Вы можете вызвать 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__().

12 голосов
/ 08 марта 2012

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

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

12 голосов
/ 08 марта 2012

Результат 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

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

0 голосов
/ 15 февраля 2014

Обновление: спасибо всем за отзывы. Я все еще озадачен тем, являются ли объекты класса синглетонами, мой здравый смысл говорит, что они есть, но было действительно трудно получить подтверждение (попробуйте поискать в поисках «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, если вы не знаете, что экземпляр факта существует.

...