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

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

9 ответов

Решение

Из http://docs.python.org/2/reference/datamodel.html:

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

Декларация-накрест:

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

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

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

class OldStyleClass():
    pass

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

  • супер добавил
  • 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

Классы старого стиля все еще немного быстрее для поиска атрибутов. Это обычно не важно, но может быть полезно в чувствительном к производительности коде Python 2.x:

В [3]: класс А:
   ...:     def __init__(self):
   ...:         self.a = "привет"...: 

В [4]: ​​класс B(объект):
   ...:     def __init__(self):
   ...:         self.a = "привет"...: 

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

В [8]: %timeit aobj.a
10000000 циклов, лучшее из 3: 78,7 нс на цикл

В [10]:% timeit bobj.a
10000000 циклов, лучшее из 3: 86,9 нс на цикл

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

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

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

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

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

Читайте descrintro для более подробной информации.

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

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

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

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

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

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