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

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1 037
0
240 884
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

От Новые и классические классы:

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 x is an instance of an old-style class, then x.__class__ designates the class of x, but type(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 as x.__class__ (although this is not guaranteed – a new-style class instance is permitted to override the value returned for x.__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 object or not, classes are new-style in Python 3.

Ни одно из этих различий не звучит как веские причины использовать классы нового стиля, но все говорят, что вы всегда должны использовать новый стиль. Если я использую утиный ввод, как и следовало бы, мне никогда не нужно использовать type(x). Если я не создаю подклассы встроенного типа, то, похоже, нет никаких преимуществ, которые я вижу в классах нового стиля. Недостатком является лишняя типизация (object).

recursive 03.05.2011 16:45

Некоторые функции, такие как super(), не работают с классами старого стиля. Не говоря уже о том, что, как говорится в этой статье, есть фундаментальные исправления, такие как MRO, и специальные методы, что является более чем хорошей причиной для их использования.

John Doe 08.12.2011 21:04

Нужно ли мне все еще беспокоиться об этом различии нового стиля и старого стиля в 2012 году с python 2.7? документация ничего о них не говорит. Могу я просто объявить классы как class MyClass? Или мне еще нужно делать class MyClass(object)?

User 11.12.2012 08:47

@User: вы все равно должны использовать классы нового стиля, в Python 2.7, да. Рекомендуется явно создавать подклассы от object даже в Python 3, где нет классов старого стиля.

Lennart Regebro 12.12.2012 02:25

@User: классы в старом стиле ведут себя в 2.7 точно так же, как и в 2.1 - и, поскольку мало кто даже помнит причуды, а в документации больше не обсуждается большинство из них, они еще хуже. Приведенная выше цитата из документации прямо говорит об этом: есть «исправления», которые не могли быть реализованы в классах старого стиля. Если вы не хотите столкнуться с причудами, с которыми никто не сталкивался со времен Python 2.1 и которые даже не объясняются в документации, не используйте классы старого стиля.

abarnert 07.01.2013 10:30

Вот пример причуды, на которую вы можете наткнуться, если используете классы старого стиля в 2.7: bugs.python.org/issue21785

KT. 17.06.2014 12:18

Для всех, кто задается вопросом, хорошая причина для явного наследования от объекта в Python 3 заключается в том, что это упрощает поддержку нескольких версий Python.

jpmc26 17.09.2014 21:57

Показ синтаксиса поможет.

Chris Redford 11.11.2014 21:00

Метаклассы - это только новый стиль, не так ли?

JL Peyret 19.08.2015 07:50

Еще одна вещь, которая произошла примерно в то же время: многие встроенные функции, например float, int, str, file, раньше были встроенными функциями, которые возвращали объекты соответствующего типа. Теперь они являются типовыми объектами - типами, которые существовали раньше, без имен; но эти типы теперь имеют поведение «конструктор», поэтому могут заменять функции. Т.е. вызов типа float преобразует что-то в float и т. д. Теперь вы можете сказать if type(x) is float, тогда как в более старых версиях вам было нужно, например. if type(x) is type(1.0). Фактически, `тип '- одна из тех вещей, которые раньше были функцией, а теперь являются типом.

greggo 19.05.2016 01:45

«... в Python 3. Тем не менее, рекомендуется создавать подклассы от объекта». уже не правильный совет: docs.python.org/2/reference/…

danio 26.01.2017 12:14

Множество причин использовать только классы нового стиля. Это упрощает все во всем - сохраняет концептуально простой язык, уменьшает путаницу, добавляет функциональность, избегает причуд, связанных с классами старого стиля, и обеспечивает совместимость с Python 3.

jkdev 11.05.2017 20:58

@JLPeyret: Да, но это не серьезная проблема, поскольку, используя метакласс вообще, вы автоматически переходите в новый стиль (даже если вы не наследуете object). Фактически, обходной путь Py2 для создания всех классов в модуле по умолчанию в новом стиле без наследования от object состоит в том, чтобы определить __metaclass__ = type в верхней части файла, после чего все определения классов неявно будут в новом стиле.

ShadowRanger 10.01.2019 09:01

Классы нового стиля наследуются от 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.

aaronasterling 22.12.2010 02:53

Это неправильный пример класса Python в старом стиле? class AnotherOldStyleClass: pass

Ankur Agarwal 21.08.2013 00:23

@abc Я считаю, что class A: pass и class A(): pass строго эквивалентны. Первый означает «A не наследует родительский класс», а второй означает "Наследование без родительского класса". Это очень похоже на not is и is not.

eyquem 21.12.2013 03:21

Кстати, для 3.X автоматически предполагается наследование «объекта» (это означает, что у нас нет возможности не наследовать «объект» в 3.X). Однако по причине обратной совместимости неплохо сохранить там "(объект)".

Yo Hsiao 14.04.2014 06:32

Если мы собираемся получить техническую информацию о унаследованных классах, в этом ответе следует отметить, что вы создаете еще один класс старого стиля, наследуя от класса старого стиля. (Как написано, этот ответ заставляет пользователя сомневаться, можете ли вы унаследовать от класса старого стиля. Можно.)

jpmc26 17.09.2014 21:56

Классы старого стиля также могут (необязательно) наследовать от объекта или от другого класса. class OldStyleOptional(object): pass

cowlinator 05.05.2018 03:36

Классы старого стиля все еще немного быстрее для поиска атрибутов. Обычно это не важно, но может быть полезно в чувствительном к производительности коде 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/…, в частности в разделе о слоты

xuloChavez 20.03.2012 20:58

не похоже на 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

Benedikt Waldvogel 25.03.2012 03:38

Для меня все еще быстрее для aobj в CPython 2.7.2 на x86-64 Linux.

xioxox 05.04.2012 16:49

Вероятно, полагаться на чистый код Python для приложений, чувствительных к производительности, - плохая идея. Никто не говорит: «Мне нужен быстрый код, поэтому я буду использовать классы Python старого стиля». Numpy не считается чистым Python.

Phillip Cloud 01.07.2012 02:42

также в IPython 2.7.6 это неверно. '' '' 477 нс против 456 нс на петлю '' ''

kmonsoor 20.07.2014 15:48

В IPython 2.7.6 я все еще считаю, что это правда, 32,4 против 39 нс (i7-2760QM, Linux, x86-64)

xioxox 21.07.2014 16:04

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

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

  1. Классы старого стиля

    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>
    >>>
    
    
  2. Классы нового стиля

    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? Не могли бы вы поделиться ссылкой?

Muatik 22.01.2014 19:03

_names_cache - это словарь, который кэширует (сохраняет для последующего извлечения) каждое имя, которое вы передаете Person.__new__. Метод setdefault (определенный в любом словаре) принимает два аргумента: ключ и значение. Если ключ находится в dict, он вернет его значение. Если его нет в dict, он сначала установит значение, переданное в качестве второго аргумента, а затем вернет его.

ychaouche 23.01.2014 01:53

Использование неверно. Идея состоит в том, чтобы не создавать новый объект, если он уже существует, но в вашем случае всегда вызывается __new__(), и он всегда создает новый объект, а затем бросает его. В этом случае if предпочтительнее .setdefault().

Amit Upadhyay 08.07.2015 13:56

Но я не понял, почему разница в выводе, то есть в старом классе стиля два экземпляра были разными, поэтому возвращено False, но в новом классе стиля оба экземпляра одинаковы. Как ? Какое изменение в новом классе стиля сделало два экземпляра одинаковыми, чего не было в классе старого стиля?

Pabitra Pati 27.10.2016 05:31

@PabitraPati: Это своего рода дешевая демонстрация. __new__ на самом деле не подходит для классов старого стиля, он не используется при создании экземпляров (это просто случайное имя, которое выглядит особенным, как определение __spam__). Таким образом, построение класса в старом стиле вызывает только __init__, в то время как конструкция в новом стиле вызывает __new__ (объединение в одноэлементный экземпляр по имени) для создания и __init__ для его инициализации.

ShadowRanger 24.03.2017 22:20

Важные изменения поведения между классами старого и нового стиля

  • супер добавлен
  • MRO изменено (объяснено ниже)
  • дескрипторы добавлен
  • новые объекты класса стиля не могут быть подняты, если они не являются производными от Exception (пример ниже)
  • __slots__ добавлен

MRO (Порядок разрешения методов) изменен

Об этом упоминалось в других ответах, но вот конкретный пример разницы между классическим 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

Хорошее четкое резюме, спасибо. Когда вы говорите «сложно объяснить по-английски», я думаю, вы описываете пост-упорядоченный поиск в глубину в отличие от старого класса, который использует предварительный поиск в глубину. (предварительный заказ означает, что мы ищем себя до нашего первого ребенка, а постзаказ означает, что мы ищем себя после нашего последнего ребенка).

Steve Carter 05.10.2016 12:20

Другие вопросы по теме