В чем разница между старым и новым стилем классов в Python? - PullRequest
911 голосов
/ 10 сентября 2008

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

Ответы [ 9 ]

519 голосов
/ 10 сентября 2008

С http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes:

До 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.

288 голосов
/ 30 июля 2009

Декларация мудр:

Классы нового стиля наследуются от объекта или другого класса нового стиля.

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Классы старого стиля этого не делают.

class OldStyleClass():
    pass
211 голосов

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

  • супер добавлено
  • 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
36 голосов
/ 12 июля 2010

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

Гвидо написал Внутренняя история о классах нового стиля , действительно отличная статья о классах нового и старого стиля в Python.

В Python 3 есть только класс нового стиля, даже если вы пишете «класс старого стиля», он неявно наследуется от object.

У классов нового стиля есть некоторые расширенные функции, которых нет в классах старого стиля, такие как super и новый C3 mro , некоторые магические методы и т. Д.

21 голосов
/ 09 октября 2013

Вот очень практичное различие между Истиной и Ложью. Единственная разница между двумя версиями следующего кода заключается в том, что во второй версии 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>
>>>
7 голосов
/ 28 октября 2008

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

Подробнее descrintro .

5 голосов
/ 30 апреля 2013

Новые классы стилей могут использовать super(Foo, self), где Foo - это класс, а self - это экземпляр.

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

Возвращает прокси-объект, который делегирует вызовы метода родительскому или родственному классу типа. Это полезно для доступа к унаследованным методам, которые были переопределены в классе. Порядок поиска такой же, как и в getattr (), за исключением того, что сам тип пропускается.

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

4 голосов
/ 11 сентября 2008

Вернее, вы всегда должны использовать классы нового стиля, , если только не имеет кода, который должен работать с версиями Python старше 2.2.

...