Яка різниця між старим стилем та класами нового стилю в Python? Коли я повинен використовувати те чи інше?
Яка різниця між старим стилем та класами нового стилю в Python? Коли я повинен використовувати те чи інше?
Відповіді:
З класів Нового стилю та класики :
До 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.
super()
не працюють на класах старого стилю. Не кажучи вже про те, як сказано в цій статті, існують фундаментальні виправлення, як MRO, і спеціальні методи, що є більш ніж вагомим приводом для його використання.
Декларація:
Класи нового стилю успадковують від об'єкта або іншого класу нового стилю.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
Заняття в старому стилі ні.
class OldStyleClass():
pass
Примітка Python 3:
Python 3 не підтримує класи старого стилю, тому будь-яка форма, зазначена вище, призводить до нового класу.
object
.
class AnotherOldStyleClass: pass
class A: pass
і class A(): pass
строго еквівалентні. Перший означає "A не успадковує жоден батьківський клас", а другий означає "A успадковує не батьківський клас" . Це дуже схоже на not is
іis not
Важливі зміни поведінки між старим та новим класами стилів
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
Класи старого стилю все ще незначно швидші для пошуку атрибутів. Зазвичай це не важливо, але воно може бути корисним у коді 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 нс на петлю
%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
Гвідо написав «Внутрішню історію» про класи нового стилю , дійсно чудову статтю про клас нового стилю та старого стилю в Python.
У Python 3 є лише новий клас стилю. Навіть якщо ви пишете "клас старого стилю", це неявно походить від object
.
Класи нового стилю мають деякі розширені можливості, яких не вистачає в класах старого стилю, наприклад super
, новий C3 mro , деякі магічні методи тощо.
Ось дуже практична, правдива / хибна різниця. Єдина відмінність між двома версіями наступного коду полягає в тому, що у другій версії Person успадковує від об'єкта . Окрім цього, дві версії однакові, але з різними результатами:
Заняття в старому стилі
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
це словник, який кеширує (зберігає для подальшого пошуку) кожне ім'я, до якого ви переходите Person.__new__
. Метод setdefault (визначений у будь-якому словнику) бере два аргументи: ключ та значення. Якщо ключ знаходиться в диктаті, він поверне своє значення. Якщо його немає в диктаті, він встановить його спочатку на значення, передане як другий аргумент, а потім поверне його.
__new__()
завжди називається, і він завжди конструює новий об’єкт, а потім кидає його. У цьому випадку a if
є кращим над .setdefault()
.
__new__
насправді це не річ для класів старого стилю, вона не звикає, наприклад, при будівництві (це просто випадкове ім'я, яке виглядає особливим, як визначення __spam__
). Тож побудова класу старого стилю лише викликає__init__
, тоді як конструкція нового стилю викликає __new__
(з’єднуючись з однотонним екземпляром за назвою), щоб сконструювати та __init__
ініціалізувати його.
Класи нового стилю успадковують object
і повинні бути записані як такі в Python 2.2 далі (тобто class Classname(object):
замість class Classname:
). Основна зміна полягає в уніфікації типів і класів, і приємний побічний ефект цього полягає в тому, що він дозволяє успадкувати від вбудованих типів.
Прочитайте опис для більш детальної інформації.
Нові класи стилів можуть використовувати super(Foo, self)
де Foo
клас та self
є екземпляр.
super(type[, object-or-type])
Повернути об’єкт проксі, який делегує виклики методу батьківському або рідному класу типу. Це корисно для доступу до успадкованих методів, які були замінені в класі. Порядок пошуку такий самий, як у getattr (), за винятком того, що сам тип пропускається.
А в Python 3.x ви можете просто використовувати super()
всередині класу без будь-яких параметрів.
type(x)
. Якщо я не підкласую вбудований тип, то, здається, не існує жодної переваги, яку я бачу у класах нового стилю. Є недолік, який полягає в додатковому наборі тексту(object)
.