В чем разница между классами старого и нового стиля в Python? Когда я должен использовать тот или иной?






От Новые и классические классы:
Up to Python 2.1, old-style classes were the only flavour available to the user.
The concept of (old-style) class is unrelated to the concept of type: if
xis an instance of an old-style class, thenx.__class__designates the class ofx, buttype(x)is always<type 'instance'>.This reflects the fact that all old-style instances, independently of their class, are implemented with a single built-in type, called instance.
New-style classes were introduced in Python 2.2 to unify the concepts of class and type. A new-style class is simply a user-defined type, no more, no less.
If x is an instance of a new-style class, then
type(x)is typically the same asx.__class__(although this is not guaranteed – a new-style class instance is permitted to override the value returned forx.__class__).The major motivation for introducing new-style classes is to provide a unified object model with a full meta-model.
It also has a number of immediate benefits, like the ability to subclass most built-in types, or the introduction of "descriptors", which enable computed properties.
For compatibility reasons, classes are still old-style by default.
New-style classes are created by specifying another new-style class (i.e. a type) as a parent class, or the "top-level type" object if no other parent is needed.
The behaviour of new-style classes differs from that of old-style classes in a number of important details in addition to what type returns.
Some of these changes are fundamental to the new object model, like the way special methods are invoked. Others are "fixes" that could not be implemented before for compatibility concerns, like the method resolution order in case of multiple inheritance.
Python 3 only has new-style classes.
No matter if you subclass from
objector not, classes are new-style in Python 3.
Некоторые функции, такие как super(), не работают с классами старого стиля. Не говоря уже о том, что, как говорится в этой статье, есть фундаментальные исправления, такие как MRO, и специальные методы, что является более чем хорошей причиной для их использования.
Нужно ли мне все еще беспокоиться об этом различии нового стиля и старого стиля в 2012 году с python 2.7? документация ничего о них не говорит. Могу я просто объявить классы как class MyClass? Или мне еще нужно делать class MyClass(object)?
@User: вы все равно должны использовать классы нового стиля, в Python 2.7, да. Рекомендуется явно создавать подклассы от object даже в Python 3, где нет классов старого стиля.
@User: классы в старом стиле ведут себя в 2.7 точно так же, как и в 2.1 - и, поскольку мало кто даже помнит причуды, а в документации больше не обсуждается большинство из них, они еще хуже. Приведенная выше цитата из документации прямо говорит об этом: есть «исправления», которые не могли быть реализованы в классах старого стиля. Если вы не хотите столкнуться с причудами, с которыми никто не сталкивался со времен Python 2.1 и которые даже не объясняются в документации, не используйте классы старого стиля.
Вот пример причуды, на которую вы можете наткнуться, если используете классы старого стиля в 2.7: bugs.python.org/issue21785
Для всех, кто задается вопросом, хорошая причина для явного наследования от объекта в Python 3 заключается в том, что это упрощает поддержку нескольких версий Python.
Показ синтаксиса поможет.
Метаклассы - это только новый стиль, не так ли?
Еще одна вещь, которая произошла примерно в то же время: многие встроенные функции, например float, int, str, file, раньше были встроенными функциями, которые возвращали объекты соответствующего типа. Теперь они являются типовыми объектами - типами, которые существовали раньше, без имен; но эти типы теперь имеют поведение «конструктор», поэтому могут заменять функции. Т.е. вызов типа float преобразует что-то в float и т. д. Теперь вы можете сказать if type(x) is float, тогда как в более старых версиях вам было нужно, например. if type(x) is type(1.0). Фактически, `тип '- одна из тех вещей, которые раньше были функцией, а теперь являются типом.
«... в Python 3. Тем не менее, рекомендуется создавать подклассы от объекта». уже не правильный совет: docs.python.org/2/reference/…
Множество причин использовать только классы нового стиля. Это упрощает все во всем - сохраняет концептуально простой язык, уменьшает путаницу, добавляет функциональность, избегает причуд, связанных с классами старого стиля, и обеспечивает совместимость с Python 3.
@JLPeyret: Да, но это не серьезная проблема, поскольку, используя метакласс вообще, вы автоматически переходите в новый стиль (даже если вы не наследуете object). Фактически, обходной путь Py2 для создания всех классов в модуле по умолчанию в новом стиле без наследования от object состоит в том, чтобы определить __metaclass__ = type в верхней части файла, после чего все определения классов неявно будут в новом стиле.
Классы нового стиля наследуются от object и должны быть написаны как таковые в Python 2.2 и новее (т.е. class Classname(object): вместо class Classname:). Основное изменение заключается в унификации типов и классов, и приятным побочным эффектом этого является то, что оно позволяет наследовать от встроенных типов.
Подробнее читайте в descrintro.
По декларации:
Классы нового стиля наследуются от объект или от другого класса нового стиля.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
Классы старого стиля - нет.
class OldStyleClass():
pass
Примечание Python 3:
Python 3 не поддерживает классы старого стиля, поэтому любая из указанных выше форм приводит к созданию класса нового стиля.
если класс нового стиля наследуется от другого класса нового стиля, то по расширению он наследуется от object.
Это неправильный пример класса Python в старом стиле? class AnotherOldStyleClass: pass
@abc Я считаю, что class A: pass и class A(): pass строго эквивалентны. Первый означает «A не наследует родительский класс», а второй означает "Наследование без родительского класса". Это очень похоже на not is и is not.
Кстати, для 3.X автоматически предполагается наследование «объекта» (это означает, что у нас нет возможности не наследовать «объект» в 3.X). Однако по причине обратной совместимости неплохо сохранить там "(объект)".
Если мы собираемся получить техническую информацию о унаследованных классах, в этом ответе следует отметить, что вы создаете еще один класс старого стиля, наследуя от класса старого стиля. (Как написано, этот ответ заставляет пользователя сомневаться, можете ли вы унаследовать от класса старого стиля. Можно.)
Классы старого стиля также могут (необязательно) наследовать от объекта или от другого класса. class OldStyleOptional(object): pass
Классы старого стиля все еще немного быстрее для поиска атрибутов. Обычно это не важно, но может быть полезно в чувствительном к производительности коде Python 2.x:
In [3]: class A: ...: def __init__(self): ...: self.a = 'hi there' ...: In [4]: class B(object): ...: def __init__(self): ...: self.a = 'hi there' ...: In [6]: aobj = A() In [7]: bobj = B() In [8]: %timeit aobj.a 10000000 loops, best of 3: 78.7 ns per loop In [10]: %timeit bobj.a 10000000 loops, best of 3: 86.9 ns per loop
Интересно, что вы заметили на практике, я только что прочитал, что это связано с тем, что классы нового стиля, как только они нашли атрибут в экземпляре dict, должны выполнить дополнительный поиск, чтобы определить, является ли это описанием, т. Е. получать, который необходимо вызвать, чтобы получить возвращаемое значение. Классы старого стиля просто возвращают найденный объект без дополнительных вычислений (но тогда не поддерживают дескрипторы). Вы можете прочитать больше в этом отличном посте Гвидо python-history.blogspot.co.uk/2010/06/…, в частности в разделе о слоты
не похоже на CPython 2.7.2: %timeit aobj.a10000000 loops, best of 3: 66.1 ns per loop%timeit bobj.a10000000 loops, best of 3: 53.9 ns per loop
Для меня все еще быстрее для aobj в CPython 2.7.2 на x86-64 Linux.
Вероятно, полагаться на чистый код Python для приложений, чувствительных к производительности, - плохая идея. Никто не говорит: «Мне нужен быстрый код, поэтому я буду использовать классы Python старого стиля». Numpy не считается чистым Python.
также в IPython 2.7.6 это неверно. '' '' 477 нс против 456 нс на петлю '' ''
В IPython 2.7.6 я все еще считаю, что это правда, 32,4 против 39 нс (i7-2760QM, Linux, x86-64)
Гвидо написал Внутренняя история о классах нового стиля, действительно отличную статью о классах нового и старого стиля в Python.
В Python 3 есть только класс нового стиля. Даже если вы напишете «класс старого стиля», он неявно унаследован от object.
Классы нового стиля имеют некоторые расширенные функции, отсутствующие в классах старого стиля, такие как super, новый C3 mro, некоторые магические методы и т. д.
Классы нового стиля могут использовать super(Foo, self), где Foo - это класс, а self - экземпляр.
super(type[, object-or-type])Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.
А в Python 3.x вы можете просто использовать super() внутри класса без каких-либо параметров.
Вот очень практичная разница, правда / ложь. Единственное различие между двумя версиями следующего кода состоит в том, что во второй версии Человек наследуется от объект. В остальном две версии идентичны, но с разными результатами:
Классы старого стиля
class Person():
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed1 is ahmed2
print ahmed1
print ahmed2
>>> False
<__main__.Person instance at 0xb74acf8c>
<__main__.Person instance at 0xb74ac6cc>
>>>
Классы нового стиля
class Person(object):
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed2 is ahmed1
print ahmed1
print ahmed2
>>> True
<__main__.Person object at 0xb74ac66c>
<__main__.Person object at 0xb74ac66c>
>>>
что делает _names_cache? Не могли бы вы поделиться ссылкой?
_names_cache - это словарь, который кэширует (сохраняет для последующего извлечения) каждое имя, которое вы передаете Person.__new__. Метод setdefault (определенный в любом словаре) принимает два аргумента: ключ и значение. Если ключ находится в dict, он вернет его значение. Если его нет в dict, он сначала установит значение, переданное в качестве второго аргумента, а затем вернет его.
Использование неверно. Идея состоит в том, чтобы не создавать новый объект, если он уже существует, но в вашем случае всегда вызывается __new__(), и он всегда создает новый объект, а затем бросает его. В этом случае if предпочтительнее .setdefault().
Но я не понял, почему разница в выводе, то есть в старом классе стиля два экземпляра были разными, поэтому возвращено False, но в новом классе стиля оба экземпляра одинаковы. Как ? Какое изменение в новом классе стиля сделало два экземпляра одинаковыми, чего не было в классе старого стиля?
@PabitraPati: Это своего рода дешевая демонстрация. __new__ на самом деле не подходит для классов старого стиля, он не используется при создании экземпляров (это просто случайное имя, которое выглядит особенным, как определение __spam__). Таким образом, построение класса в старом стиле вызывает только __init__, в то время как конструкция в новом стиле вызывает __new__ (объединение в одноэлементный экземпляр по имени) для создания и __init__ для его инициализации.
Важные изменения поведения между классами старого и нового стиля
Exception (пример ниже)__slots__ добавленОб этом упоминалось в других ответах, но вот конкретный пример разницы между классическим MRO и C3 MRO (используется в новых классах стиля).
Вопрос в том, в каком порядке ищутся атрибуты (включая методы и переменные-члены) при множественном наследовании.
Классические классы выполняет поиск в глубину слева направо. Остановитесь на первом матче. У них нет атрибута __mro__.
class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 0
assert C21().i == 2
try:
C12.__mro__
except AttributeError:
pass
else:
assert False
Классы нового стиля MRO сложнее синтезировать в одном английском предложении. Подробно объясняется здесь. Одно из его свойств состоит в том, что базовый класс ищется только после того, как все его производные классы были найдены. У них есть атрибут __mro__, который показывает порядок поиска.
class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 2
assert C21().i == 2
assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)
Exception.В Python 2.5 можно было поднять множество классов, а в Python 2.6 это было удалено. В Python 2.7.3:
# OK, old:
class Old: pass
try:
raise Old()
except Old:
pass
else:
assert False
# TypeError, new not derived from `Exception`.
class New(object): pass
try:
raise New()
except TypeError:
pass
else:
assert False
# OK, derived from `Exception`.
class New(Exception): pass
try:
raise New()
except New:
pass
else:
assert False
# `'str'` is a new style object, so you can't raise it:
try:
raise 'str'
except TypeError:
pass
else:
assert False
Хорошее четкое резюме, спасибо. Когда вы говорите «сложно объяснить по-английски», я думаю, вы описываете пост-упорядоченный поиск в глубину в отличие от старого класса, который использует предварительный поиск в глубину. (предварительный заказ означает, что мы ищем себя до нашего первого ребенка, а постзаказ означает, что мы ищем себя после нашего последнего ребенка).
Ни одно из этих различий не звучит как веские причины использовать классы нового стиля, но все говорят, что вы всегда должны использовать новый стиль. Если я использую утиный ввод, как и следовало бы, мне никогда не нужно использовать
type(x). Если я не создаю подклассы встроенного типа, то, похоже, нет никаких преимуществ, которые я вижу в классах нового стиля. Недостатком является лишняя типизация(object).