Яка різниця між старим стилем та класами нового стилю в Python?


Відповіді:


559

З класів Нового стилю та класики :

До Python 2.1, класичні стилі були єдиним ароматом, доступним для користувача.

Поняття класу (старого стилю) не пов'язане з поняттям типу: якщо xце екземпляр класу старого стилю, то x.__class__ позначає клас x, але type(x)є завжди <type 'instance'>.

Це відображає той факт, що всі екземпляри старого стилю, незалежно від їх класу, реалізуються з одним вбудованим типом, званим екземпляром.

Класи нового стилю були введені в Python 2.2 для уніфікації понять класу та типу . Клас нового стилю - це просто визначений користувачем тип, ні більше, ні менше.

Якщо x - екземпляр класу нового стилю, він, type(x)як правило, такий самий, як x.__class__(хоча це не гарантується - екземпляр класу нового стилю дозволений замінити значення, повернене для x.__class__).

Основна мотивація до впровадження класів нового стилю полягає у наданні єдиної об'єктної моделі з повною мета-моделлю .

Він також має ряд негайних переваг, як, наприклад, можливість підкласи більшості вбудованих типів або введення "дескрипторів", які дозволяють обчислювати властивості.

З міркувань сумісності класи за замовчуванням все ще старого стилю .

Класи нового стилю створюються шляхом визначення іншого класу нового стилю (тобто типу) як батьківського класу або об'єкта "тип верхнього рівня", якщо не потрібен інший батьків.

Поведінка класів нового стилю відрізняється від класів у старому стилі у ряді важливих деталей на додаток до того, який тип повертається.

Деякі з цих змін є основоположними для нової об'єктної моделі, як, наприклад, використання спеціальних методів. Інші - це «виправлення», які раніше не можна було реалізувати щодо проблем сумісності, як, наприклад, порядок вирішення методу у випадку багаторазового успадкування.

У Python 3 є лише класи нового стилю .

Незалежно від того, підкласи ви objectчи ні, класи є новим стилем у Python 3.


41
Жодна з цих відмінностей не звучить як переконливі причини використовувати класи нового стилю, але всі кажуть, що завжди слід використовувати новий стиль. Якщо я використовую текст качки, як слід, мені ніколи не потрібно користуватися type(x). Якщо я не підкласую вбудований тип, то, здається, не існує жодної переваги, яку я бачу у класах нового стилю. Є недолік, який полягає в додатковому наборі тексту (object).
рекурсивна

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

21
@User: Заняття в старому стилі поводяться так само в 2.7, як і в 2.1 - і тому, що мало хто навіть пам’ятає химерність, і в документації більше не йдеться про більшість з них, вони ще гірші. Цитата документації вище прямо говорить про це: є "виправлення", які неможливо було реалізувати на класах старого стилю. Якщо ви не хочете зіткнутися з хитрощами, якими ніхто ще не займався з Python 2.1, і якщо документація більше не пояснює, не використовуйте класи старого стилю.
abarnert

10
Ось приклад химерності, на яку можна натрапити, якщо ви використовуєте класи старого стилю в 2.7: bugs.python.org/issue21785
KT.

5
Для всіх, хто цікавиться, хороша причина, щоб явно успадкувати об’єкт в Python 3, полягає в тому, що це спрощує підтримку декількох версій Python.
jpmc26

308

Декларація:

Класи нового стилю успадковують від об'єкта або іншого класу нового стилю.

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Заняття в старому стилі ні.

class OldStyleClass():
    pass

Примітка Python 3:

Python 3 не підтримує класи старого стилю, тому будь-яка форма, зазначена вище, призводить до нового класу.


24
якщо клас нового стилю успадковує від іншого класу нового стилю, то за розширенням він успадковує від object.
aaronasterling

2
Це невірний приклад класу python у старому стилі? class AnotherOldStyleClass: pass
Ankur Agarwal

11
@abc Я вважаю , що class A: passі class A(): passстрого еквівалентні. Перший означає "A не успадковує жоден батьківський клас", а другий означає "A успадковує не батьківський клас" . Це дуже схоже на not isіis not
eyquem

5
Як бічна примітка, для 3.X наслідування "об'єкта" автоматично передбачається (тобто, у нас немає способу не успадкувати "об'єкт" в 3.X). З причини зворотної сумісності, не погано тримати там "(об'єкт)".
Йо Хсіяо

1
Якщо ми збираємося отримати технічну інформацію про успадковані класи, у цій відповіді слід зазначити, що ви створюєте інший клас старого стилю, успадковуючи від старого класу стилів. (Як написано, ця відповідь залишає у користувача запитання, чи можна успадковувати клас старого стилю. Можна.)
jpmc26

224

Важливі зміни поведінки між старим та новим класами стилів

  • супер додано
  • 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

8
Приємний чіткий підсумок, дякую. Коли ви говорите "важко пояснити англійською мовою", я думаю, що ви описуєте пошук першого поглиблення після замовлення на відміну від класу старого стилю, який використовує пошук попереднього глибини попереднього замовлення. (попереднє замовлення означає, що ми шукаємо себе перед своєю першою дитиною, а порядок означає, що ми шукаємо себе після останньої дитини).
Стів Картер

40

Класи старого стилю все ще незначно швидші для пошуку атрибутів. Зазвичай це не важливо, але воно може бути корисним у коді Python 2.x, залежно від продуктивності:

У [3]: клас А:
   ...: def __init __ (само)
   ...: self.a = 'привіт там'
   ...:

У [4]: ​​клас B (об’єкт):
   ...: def __init __ (само)
   ...: self.a = 'привіт там'
   ...:

В [6]: aobj = A ()
В [7]: bobj = B ()

В [8]:% timeit aobj.a
10000000 петель, найкраще 3: 78,7 нс на петлю

В [10]:% timeit bobj.a
10000000 петель, найкраще 3: 86,9 нс на петлю

5
Цікаво, що ви помітили на практиці, я щойно прочитав, що це тому, що класи нового стилю, знайшовши атрибут у диктаті екземпляра, повинні зробити додатковий пошук, щоб розібратися, чи це опис, тобто він має метод отримання, який потрібно викликати, щоб отримати значення, яке потрібно повернути. Класи старого стилю просто повертають знайдений об'єкт без додавання обчислень (але тоді не підтримують дескриптори). Ви можете прочитати більше в цьому чудовому дописі від Guido python-history.blogspot.co.uk/2010/06/… , зокрема розділ про слоти
xuloChavez

1
не здається правдою для CPython 2.7.2:%timeit aobj.a 10000000 loops, best of 3: 66.1 ns per loop %timeit bobj.a 10000000 loops, best of 3: 53.9 ns per loop
Benedikt Waldvogel,

1
Ще швидше для aobj в CPython 2.7.2 на x86-64 Linux для мене.
xioxox

41
Напевно, погана ідея покладатися на чистий код Python для додатків, що залежать від продуктивності. Ніхто не каже: "Мені потрібен швидкий код, тому я буду використовувати старі стилі Python". Numpy не вважається чистим Python.
Філліп Хмара

також у IPython 2.7.6, це неправда. '' '' 477 нс проти 456 нс за цикл '' ''
kmonsoor

37

Гвідо написав «Внутрішню історію» про класи нового стилю , дійсно чудову статтю про клас нового стилю та старого стилю в Python.

У Python 3 є лише новий клас стилю. Навіть якщо ви пишете "клас старого стилю", це неявно походить від object.

Класи нового стилю мають деякі розширені можливості, яких не вистачає в класах старого стилю, наприклад super, новий C3 mro , деякі магічні методи тощо.


24

Ось дуже практична, правдива / хибна різниця. Єдина відмінність між двома версіями наступного коду полягає в тому, що у другій версії Person успадковує від об'єкта . Окрім цього, дві версії однакові, але з різними результатами:

  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>
    >>>

2
що робить "_names_cache"? Не могли б ви поділитися довідкою?
Муатік

4
_names_cacheце словник, який кеширує (зберігає для подальшого пошуку) кожне ім'я, до якого ви переходите Person.__new__. Метод setdefault (визначений у будь-якому словнику) бере два аргументи: ключ та значення. Якщо ключ знаходиться в диктаті, він поверне своє значення. Якщо його немає в диктаті, він встановить його спочатку на значення, передане як другий аргумент, а потім поверне його.
ychaouche

4
Використання неправильне. Ідея полягає в тому, щоб не конструювати новий об’єкт, якщо він вже існує, але у вашому випадку __new__()завжди називається, і він завжди конструює новий об’єкт, а потім кидає його. У цьому випадку a ifє кращим над .setdefault().
Аміт Упадхей

Але я не зрозумів, чому різниця у виході, тобто у старому класі стилю, два екземпляри були різними, таким чином повернулися помилковими, але у новому класі стилю обидва екземпляри однакові. Як? У чому полягає зміна класу нового стилю, яка зробила два екземпляри однаковими, чого не було в класі старого стилю?
Пабітра Паті

1
@PabitraPati: Тут це якась дешева демонстрація. __new__насправді це не річ для класів старого стилю, вона не звикає, наприклад, при будівництві (це просто випадкове ім'я, яке виглядає особливим, як визначення __spam__). Тож побудова класу старого стилю лише викликає__init__ , тоді як конструкція нового стилю викликає __new__(з’єднуючись з однотонним екземпляром за назвою), щоб сконструювати та __init__ініціалізувати його.
ShadowRanger

10

Класи нового стилю успадковують objectі повинні бути записані як такі в Python 2.2 далі (тобто class Classname(object):замість class Classname:). Основна зміна полягає в уніфікації типів і класів, і приємний побічний ефект цього полягає в тому, що він дозволяє успадкувати від вбудованих типів.

Прочитайте опис для більш детальної інформації.


8

Нові класи стилів можуть використовувати super(Foo, self)де Fooклас та selfє екземпляр.

super(type[, object-or-type])

Повернути об’єкт проксі, який делегує виклики методу батьківському або рідному класу типу. Це корисно для доступу до успадкованих методів, які були замінені в класі. Порядок пошуку такий самий, як у getattr (), за винятком того, що сам тип пропускається.

А в Python 3.x ви можете просто використовувати super()всередині класу без будь-яких параметрів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.