функциональная разница между экземпляром и объектом, созданным не через __init__?
Слово self используется для обозначения экземпляра класса. Используя ключевое слово self , мы получаем доступ к атрибутам и методам класса в Python.
__init__ method
__init__ — это метод в классах Python. В объектно-ориентированной терминологии он называется конструктором. Этот метод вызывается, когда объект создается из класса, и позволяет классу инициализировать атрибуты класса.
# Узнайте стоимость прямоугольного поля class Rectangle: def __init__(self, length, breadth, unit_cost=0): self.length = length self.breadth = breadth self.unit_cost = unit_cost def get_area(self): return self.length * self.breadth def calculate_cost(self): area = self.get_area() return area * self.unit_cost # breadth = 120, length = 160, 1 sq unit cost = 2000 руб. rectangle = Rectangle(160, 120, 2000) print(f"Площадь прямоугольника: ")
Отслеживать
ответ дан 4 дек 2020 в 13:17
72.6k 106 106 золотых знаков 38 38 серебряных знаков 55 55 бронзовых знаков
Магический метод __init__ запускается при создании экземпляра класса. Пример:
class A(): def __init__(self): print('Hello, world!') a = A()
Hello, world!
Отслеживать
ответ дан 4 дек 2020 в 13:14
USERNAME GOES HERE USERNAME GOES HERE
10.4k 21 21 золотой знак 25 25 серебряных знаков 53 53 бронзовых знака
__init__ вызывается уже после создания экземпляра класса, а не во время, в примере ниже видно что сначала выполнится print(1, ob), и лишь затем print(2, self), причем объект экземпляра класса создан уже до вызова __init__ , и затем просто передается ему в аргумента в self
class A(): def __new__(cls, *args, **kwargs): ob = super().__new__(cls) print(1, ob) return ob def __init__(self): print(2, self) a = A() # 1 # 2
в примере ниже видно, что __init__ необязательный метод, и атрибуты класса можно создавать где угодно и когда угодно, хотя это конечно и считается неправильным подходом
class A(): def test(self): self.b = 123 a = A() a.test() print(a.b) # 123
в примере ниже видно, что self это не ключевое слово, это просто обычный аргумент у метода и его общепринятое название, но это аргумент который идет первым, ничто не мешает нам назвать его иначе, хотя это конечно и считается неправильным подходом
class A(): def __init__(qwerty): qwerty.b = 123 a = A() print(a.b) # 123
в примере ниже видно, что в исходном классе A , все его методы print(A.test) это обычные функции (function), которые лежат в области видимоcти A , а все методы его объекта a print(a.test) это методы(method), которые лежат в новой области видимости a , причем эти методы, это просто специально «декорированные» исходные функции объекта A print(a.test.__func__) , «декорирование» которых придает функционал необязательности указания первого аргумента self при вызове методов как a.test(123), причем нам ничего не мешает обращаться к методу и через исходную функцию A.test(a, 123), но уже передавая первый аргумент self
class A(): b = 321 def test(self, v): print(v) print(A.test) # a = A() print(a.test) # > print(a.test.__func__) # a.test(123) # 123 A.test(a, 123) # 123
Использование метода .__new__() в классах Python
Примеры использования метода .__new__() при создании классов
При написании классов Python обычно нет необходимости создавать собственную реализацию специального метода .__new__() . В большинстве случаев базовой реализации из встроенного класса объектов достаточно для создания пустого объекта текущего класса.
Тем не менее, есть несколько интересных вариантов использования этого метода.
- Общий принцип использования метода .__new__() ;
- Создание подклассов неизменяемых встроенных типов;
- Метод .__new__() как фабрика случайных объектов;
- Создание «Singleton» класса.
Общий принцип использования метода .__new__() .
Обычно, создание собственной реализации метода .__new__() необходима только тогда, когда нужно управлять созданием нового экземпляра класса на низком уровне. Теперь, если нужна кастомная реализация этого метода, то следует выполнить несколько шагов:
- Создать новый экземпляр, вызвав super().__new__() с соответствующими аргументами.
- Настроить новый экземпляр в соответствии с конкретными потребностями.
- возвратить новый экземпляр, чтобы продолжить процесс создания экземпляра.
С помощью этих трех кратких шагов можно настроить этап создания экземпляра в процессе создания экземпляра Python.
class SomeClass: def __new__(cls, *args, **kwargs): instance = super().__new__(cls) # В этом месте можно настроить свой экземпляр. return instance def __init__(self, val): self.val = val
В этом примере представлена своего рода реализация шаблона .__new__() . Как обычно, .__new__() принимает текущий класс в качестве аргумента, который обычно называется cls .
Обратите внимание, что используются *args и **kwargs , чтобы сделать метод более гибким и удобным в сопровождении, принимая любое количество аргументов. Всегда необходимо определять метод .__new__() с помощью *args и **kwargs , если только нет веской причины следовать другому шаблону.
Вызов super().__new__(cls) необходим, чтобы получить доступ к методу object.__new__() родительского класса object , который является базовой реализацией метода .__new__() для всех классов Python. (Встроенный класс object является базовым классом по умолчанию для всех классов Python)
Важно отметить, что сама функция object.__new__() принимает только один аргумент — класс для создания экземпляра. Если вызывать object.__new__() с большим количеством аргументов, то получим исключение TypeError . Однако object.__new__() по-прежнему принимает и передает дополнительные аргументы в конструктор .__init__() , если класс не имеет собственной реализации .__new__() .
>>> a = SomeClass(7) >>> a.val # 7
Создание подклассов неизменяемых встроенных типов.
Начнем с варианта использования .__new__() , который состоит из подкласса неизменяемого встроенного типа. В качестве примера предположим, что необходимо написать класс Distance как подкласс типа float Python. У класса Distance будет дополнительный атрибут для хранения единицы, которая используется для измерения расстояния.
Первый подход к проблеме с использованием метода .__init__() :
class Distance(float): def __init__(self, value, unit): super().__init__(value) self.unit = unit >>> in_miles = Distance(42.0, "Miles") # Traceback (most recent call last): # . # TypeError: float expected at most 1 argument, got 2
Когда создается подкласс неизменяемого встроенного типа данных, то получаем ошибку. Часть проблемы в том, что значение задается при создании, а менять его при инициализации уже поздно. Кроме того, функция float.__new__() вызывается, как говориться «под капотом«, и она не обрабатывает дополнительные аргументы так же, как object.__new__() . Это то, что вызывает ошибку в этом примере.
Чтобы обойти эту проблему, можно инициализировать объект во время создания с помощью .__new__() вместо переопределения в .__init__() .
class Distance(float): def __new__(cls, value, unit): instance = super().__new__(cls, value) instance.unit = unit return instance >>> in_miles = Distance(42.0, "Miles") >>> in_miles # 42.0 >>> in_miles.unit # 'Miles' >>> in_miles + 42.0 # 84.0
В этом примере .__new__() выполняет три шага:
- метод создает новый экземпляр текущего класса cls , вызывая super().__new__() . На этот раз вызов возвращается к float.__new__() , который создает новый экземпляр и инициализирует его, используя значение в качестве аргумента.
- метод настраивает новый экземпляр, добавляя к нему атрибут .unit .
- возвращает новый экземпляр.
Теперь класс Distance работает, как и ожидалось, позволяя использовать атрибут экземпляра для хранения единиц измерения. В отличие от значения float , хранящегося в экземпляре Distance атрибут .unit является изменяемым.
Метод .__new__() фабрика случайных объектов.
Создание классов, возвращающие экземпляры другого класса может вызвать создание специальной реализации метода .__new__() . При создании классов подобного рода нужно быть осторожны, потому что в этом случае Python полностью пропускает этап инициализации. Таким образом, вы будете нести ответственность за перевод вновь созданного объекта в допустимое состояние, прежде чем использовать его в своем коде.
Следующий пример демонстрирует возврат экземпляров случайно выбранных классов:
# pets.py from random import choice class Pet: def __new__(cls): # выбираем класс случайным образом other = choice([Dog, Cat, Python]) # подставляем вместо собственного класса `cls` # случайно выбранный `other` instance = super().__new__(other) print(f"Я type(instance).__name__>!") return instance def __init__(self): print("Класс `Pet` никогда не запустится!") class Dog: def communicate(self): print("Гав! Гав!") class Cat: def communicate(self): print("Мяу! Мяу!") class Bird: def communicate(self): print("Чик! Чирик!")
В этом примере класс Pet предоставляет метод .__new__() , который создает новый экземпляр, случайным образом выбирая класс из списка существующих классов.
Использование класса Pet в качестве фабрики объектов домашних питомцев:
>>> from pets import Pet >>> pet = Pet() # Я Dog! >>> pet.communicate() # Гав! Гав! >>> isinstance(pet, Pet) # False >>> isinstance(pet, Dog) # True >>> another_pet = Pet() # Я Bird! >>> another_pet.communicate() # Чик! Чирик!
Каждый раз, когда создается экземпляр Pet , то в результате получается случайный объект из другого класса. Такое возможно, т. к. нет ограничений на объект, который может возвращать .__new__() . Использование .__new__() таким образом превращает класс в гибкую и мощную фабрику объектов, не ограниченную экземплярами самого себя.
Наконец, обратите внимание, что метод класса Pet.__init__() никогда не запускается. Это происходит потому, что Pet.__new__() всегда возвращает объекты другого класса, а не самого Pet .
Создание «Singleton» класса.
Иногда нужно реализовать класс, который позволяет создавать только один экземпляр. Этот тип класса широко известен как «Singleton«. В этой ситуации удобен метод .__new__() , потому что он может помочь ограничить количество экземпляров, которые может иметь данный класс.
Примечание. Большинство опытных разработчиков утверждают, что не нужно реализовывать шаблон проектирования «Singleton» в Python, если уже нет рабочего класса и нужно добавить функциональность шаблона поверх него. В остальных случаях можно использовать константу уровня модуля, чтобы получить ту же функциональность «Singleton» без необходимости писать относительно сложный класс.
Пример класса Singleton с использованием метода .__new__() , который позволяет создавать только один экземпляр за раз. Для реализации такого поведения, метод .__new__() проверяет наличие предыдущих экземпляров, кэшированных в атрибуте класса:
class Singleton: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance >>> a = Singleton() >>> b = Singleton() >>> a is b # True
В этом примере класс Singleton имеет атрибут с именем ._instance , который по умолчанию имеет значение None и работает как кеш. Метод .__new__() проверяет, не существует ли предыдущий экземпляр, проверяя, что условие cls._instance равно None . Если это условие истинно, то блок кода if создает новый экземпляр Singleton и сохраняет его в cls._instance . Наконец, метод возвращает вызывающей стороне новый или существующий экземпляр.
Внимание! В приведенном выше примере Singleton не предоставляет реализацию .__init__() . Если когда-нибудь понадобится такой класс с методом .__init__() , то имейте в виду, что этот метод будет запускаться каждый раз, когда вы вызываете конструктор Singleton() . Такое поведение может вызвать непредсказуемые эффекты инициализации и ошибки.
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Пространство имен и область видимости в классах
- Определение классов
- Объект класса и конструктор класса
- Создание экземпляра класса
- Метод экземпляра класса
- Что такое метод класса и зачем нужен
- Что такое статический метод в классах Python и зачем нужен
- Атрибуты класса и переменные экземпляра класса
- Кэширование методов экземпляра декоратором lru_cache
- Закрытые/приватные методы и переменные класса Python
- Наследование классов
- Множественное наследование классов
- Абстрактные классы
- Перегрузка методов в классе Python
- Что такое миксины и как их использовать
- Класс Python как структура данных, подобная языку C
- Создание пользовательских типов данных
- Специальные (магические) методы класса Python
- Базовая настройка классов Python магическими методами
- Настройка доступа к атрибутам класса Python
- Дескриптор класса для чайников
- Протокол дескриптора класса
- Практический пример дескриптора
- Использование метода .__new__() в классах Python
- Специальный атрибут __slots__ класса Python
- Специальный метод __init_subclass__ класса Python
- Определение метаклассов metaclass
- Эмуляция контейнерных типов в классах Python
- Другие специальные методы класса
- Как Python ищет специальные методы в классах
- Шаблон проектирования Фабрика и его реализация
Метод __init__ #
Для корректной работы метода info, необходимо чтобы у экземпляра были переменные hostname и model. Если этих переменных нет, возникнет ошибка:
In [15]: class Switch: . : def info(self): . : print('Hostname: <>\nModel: <>'.format(self.hostname, self.model)) . : In [59]: sw2 = Switch() In [60]: sw2.info() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) ipython-input-60-5a006dd8aae1> in module>() ----> 1 sw2.info() ipython-input-57-30b05739380d> in info(self) 1 class Switch: 2 def info(self): ----> 3 print('Hostname: <>\nModel: <>'.format(self.hostname, self.model)) AttributeError: 'Switch' object has no attribute 'hostname'
Практически всегда, при создании объекта, у него есть какие-то начальные данные. Например, чтобы создать подключение к оборудование с помощью netmiko, надо передать параметры подключения.
В Python эти начальные данные про объект указываются в методе __init__ . Метод __init__ выполняется после того как Python создал новый экземпляр и, при этом, методу __init__ передаются аргументы с которыми был создан экземпляр:
In [32]: class Switch: . : def __init__(self, hostname, model): . : self.hostname = hostname . : self.model = model . : . : def info(self): . : print(f'Hostname: self.hostname>\nModel: self.model>') . :
Обратите внимание на то, что у каждого экземпляра, который создан из этого класса, будут созданы переменные: self.model и self.hostname .
Теперь, при создании экземпляра класса Switch, обязательно надо указать hostname и model:
In [33]: sw1 = Switch('sw1', 'Cisco 3850')
И, соответственно, метод info отрабатывает без ошибок:
In [36]: sw1.info() Hostname: sw1 Model: Cisco 3850
Метод __init__ иногда называют конструктором класса, хотя технически в Python сначала выполняется метод __new__ , а затем __init__ . В большинстве случаев, метод __new__ использовать не нужно.
Важной особенностью метода __init__ является то, что он не должен ничего возвращать. Python сгенерирует исключение, если попытаться это сделать.
Заметки об объектной системе языка Python ч.2
Вторая часть заметок об объектной системе python’a (первая часть тут). В этой статье рассказывается, что такое классы, метаклассы, type, object и как происходит поиск атрибутов в классе.
Классы
Классы (типы) — это объектные фабрики. Их главная задача — создавать объекты, обладающие определенным поведением.
Классы определяют поведение объектов с помощью своих атрибутов (которые хранятся в __dict__ класса): методов, свойств, классовых переменные, дескрипторов, а также с помощью атрибутов, унаследованных от родительских классов.
Инстанцирование обычного объекта происходит в 2 этапа: сначала его создание, потом инициализация. Соответственно, сначала запускается метод класса __new__, который возвращает объект данного класса, потом выполняется метод класса __init__, который инициализирует уже созданный объект.
def __new__(cls, . ) — статический метод (но его можно таковым не объявлять), который создает объект класса cls.
def __init__(self, . ) — метод класса, который инициализирует созданный объект.
Например, объявим класс:
>>> class A ( object ):
. pass
.
>>>
Для класса A не определены ни __new__, ни __init__. В соответствии с алгоритмом поиска атрибутов для класса (типа), который не стоит путать с алгоритмом поиска атрибутов для обычных объектов, когда класс не найдет их в своем__dict__, он будет искать эти методы в __dict__ своих базовых (родительских) классах.
Класс А имеет в качестве родителя встроенный класс object. Таким образом он будет их искать в object.__dict__
Раз есть такие методы, значит, получается, что a = A() аналогичен последовательности вызовов:
a = object.__new__(A)
object.__init__(a)
В общем виде, используя super, который как раз и реализует алгоритм поиска атрибутов по родительским классам [1]:
a = super(A, A).__new__(A)
super(A, A).__init__(a)
>>> class A ( object ):
. def __new__ (cls):
. obj = super (A, cls) . __new__(cls)
. print ‘created object’ , obj
. return obj
. def __init__ ( self ):
. print ‘initing object’ , self
.
>>> A()
created object
initing object
>>>
Singleton v.1
Понимая, как происходит создание объекта, можно написать реализацию паттерна одиночка.
Мы должны гарантировать, что у класса есть только один экземпляр. Т.е. при вызове конструктора класса, всегда возвращаем один и тот же экземпляр класса.
А это значит, что при вызов метода __new__ должен возвращать каждый раз один и тот же объект. Хранить сам объект можно, например, в классовой переменной instance.
В результате получаем:
>>> class C ( object ):
. instance = None
. def __new__ (cls):
. if cls . instance is None :
. cls . instance = super (C, cls) . __new__(cls)
. return cls . instance
.
>>> C() is C()
True
>>> C() . x = 1
>>> c = C()
>>> d = C()
>>> c . x
1
>>> d . x
1
>>> c . x =2
>>> d . x
2
>>> c . x
2
Классы и метаклассы.
Для класса (типа), так же как и для обычного объекта, существует класс (тип), который создает классы и определяет поведение класса. Этот класс называется метаклассом.
Создание класса, как и обычного объекта происходит с помощью вызова конструктора, но т.к. в классе есть несколько дополнительных специальных атрибутов, которые должны быть инициализированы, в конструктор передаются и соответствующие обязательные параметры.
XClass = XMetaClass(name, bases, attrs)
Тогда, сразу после создания
XClass.__name__ равно name,
XClass.__bases__ равен bases,
XClass.__dict__ равен attrs, а
XClass.__class__ равен XMetaClass
По умолчанию для всех определяемых классов метаклассом является type.
>>> class A ( object ):
. pass
.
Эквивалентно, по аналогии с обычными объектами:
>>> type ( ‘A’ , ( object ,), <>)
>>> class B (A):
. def foo ( self ):
. 42
.
При определении класса, можно задать свой метакласс с помощью
классовой переменной __metaclass__:
>>> class A ( object ):
. __metaclass__ = Meta
.
>>>
Что равносильно: A = Meta(‘A’, (object,), <>)
О type и object
Прежде всего type и object — это объекты. И, как у всех порядочных объектов, у них есть специальные атрибуты __class__ и __dict__:
>>> object . __class__
>>> type . __class__
>>> object . __dict__
>>> type . __dict__
Более того object, и type — это объекты типа (классы), и у них тоже есть специальные атрибуты __name__, __bases___:
>>> object . __name__
‘object’
>>> type . __name__
‘type’
>>> object . __bases__
()
>>> type . __bases__
(,)
>>>
Экземпляры типа или класса object — это объекты (любые). Т.е. любой объект — экземпляр класса object:
>>> isinstance ( 1 , object )
True
>>> isinstance ( setattr , object )
True
>>> isinstance ( «foo» , object )
True
>>> isinstance (A, object )
True
Даже функция является объектом:
>>> def bar ():
. pass
.
>>> isinstance (bar, object )
True
Кроме того, класс object сам является своим экземпляром:
>>> isinstance ( object , object )
True
type тоже является его экземпляром:
>>> isinstance ( type , object )
True
Инстанцирование — object() возвращает самый простой и общий объект:
>>> o = object ()
У которого даже __dict__ нет, есть только __class__.
>>> o . __dict__
Traceback (most recent call last):
File «», line 1, in
AttributeError: ‘object’ object has no attribute ‘__dict__’
>>> o . __class__
>>>
Экземпляры класса или типа type — это только другие классы или другие типы:
Число — это не класс
>>> isinstance ( 1 , type )
False
>>> isinstance ( «foo» , type )
False
Встроенная функция setattr тоже не класс.
>>> isinstance ( setattr , type )
False
Класс — это класс.
>>> isinstance (A, type )
True
Тип строки — это класс.
>>> isinstance ( «foo» . __class__, type )
True
Т.к. object и type — тоже классы, то они являются экземплярами класса type:
>>> isinstance ( object , type )
True
>>> isinstance ( type , type )
True
>>>
Т.к. множество классов (типов) являются подмножеством множества объектов, то логично предположить, что type является подклассом object, т.е.
>>> issubclass ( type , object )
True
>>> issubclass ( object , type )
False
type — это просто класс, экземплярами которого являются другие классы. (т.е. метакласс). А сами классы можно считать расширением простых, обычных объектов.
Таким образом, когда мы наследуем класс от object, этот класс автоматически наследует поведение класса object, т.е. при инстанцировании он будет возвращать обычный объект. А когда мы наследуем от класса type, мы также автоматически наследуем поведение класса type, т.е. при инстацированни будет создаваться класс. А класс, который создает класс, называется метаклассом.
Значит, чтобы определить просто класс, нужно наследовать его от object, чтобы определить метакласс — наследуем его от type.
И еще: не нужно путать type(a) и type(name, bases, attrs).
type(a) — вызов с одним аргументом, возвращает тип объекта,
a type(name, bases, attrs) — вызов с тремя аргументами — это вызов конструктора класса.
О поиске атрибутов в классе
Как уже было отмечено, алгоритм поиска атрибутов в обычном объекте, но есть некоторые тонкости, т.к. у типов (классов) есть __bases__ — родительские классы (типы).
Если атрибут есть в __dict__ возвращается он, затем идет поиск по базовым классам из __bases__, а потом идет обращение к __dict__ __class__’а (т.е. фактически метакласса) и его (метакласса) родительских классов (метаклассов).
>>> class Ameta ( type ):
. def foo (cls):
. print ‘Ameta.foo’
.
>>> class A ( object ):
. __metaclass__ = Ameta
.
>>> A . foo()
Ameta.foo
Все что определяется в метаклассе доступно для класса, но не доступно для экзмепляров класса — обычных объектов, т.к. поиск атрибутов в обычном объекте ведется только по __dict__ словарям класса.
>>> a = A()
>>> a . foo
Traceback (most recent call last):
File «» , line 1 , in
AttributeError : ‘A’ object has no attribute ‘foo’
В A.__dict__ ‘foo’ нет:
>>> A . __dict__[ ‘foo’ ]
Traceback (most recent call last):
File «» , line 1 , in
KeyError : ‘foo’
Зато он есть в метаклассе, поэтому:
>>> A . foo()
Ameta.foo
>>> class B (A):
. @classmethod
. def foo (cls):
. print ‘B.foo’
.
>>> B . foo # т.к. foo есть B.__dict__ вернется значение B.__dict__[‘foo’]
>
>>> B . foo()
B.foo
>>> class C (B):
. pass
.
>>> C . foo() # вернет значение из базового класса B.
B.foo
Экземпляр класса C также вызовет метод foo из класса B.
>>> c = C()
>>> c . foo()
B.foo
>>> class D (A):
. pass
.
>>> D . foo()
Ameta.foo
А экземпляр D не найдет:
>>> d = D()
>>> d . foo()
Traceback (most recent call last):
File «» , line 1 , in
AttributeError : ‘D’ object has no attribute ‘foo’
Метаклассы
Метаклассы являются фабриками классов (или типов). Инстанцирование класса тоже проходит в 2 этапа — создание объекта типа (класса) и его инициализация. Это также делается с помощью двух методов метакласса. Сначала вызывается метод __new__ метакласса с параметрами, необходимыми для создания класса — name, bases, attrs, а потом __init__ с теми же параметрами и уже созданным классом.
>>> class Meta ( type ):
. pass
.
>>> Meta( ‘A’ , ( object ,), <>)
В начале метакласс Meta ищет метод __new__ у себя в словаре __dict__, не находит его там и начинает искать в __dict__ своих родительских классах (т.е. метаклассах, в данном случае type), т.е. происходит обычный поиск атрибута в классе. В результате исполнения __new__ с соответствующими параметрами получает новый класс, который потом инициализируется вызовом __init__ метода метакласса.
В совсем развернутом виде получается:
cls = type.__dict__[‘__new__’](Meta, ‘A’, (object,), <>)
type.__dict__[‘__init__’](cls, ‘A’, (object,), <>)
Или с помощью super
cls = super(Meta, Meta).__new__(Meta, ‘A’, (object,), <>)
super(Meta, Meta).__init__(cls, ‘A’, (object,), <>)
Стоит отметить, что в отличие от инстанцирования обычных объектов, используется не object.__new__ и object.__init__, а type.__new__ и type.__init__. У object.__new__ и type.__new__ разные сигнатуры, и object.__new__ возвращает обычный объект (regular object), а type.__new__ — объект типа (typeobject), т.е. класс.
Посмотрим, как это все работает на примере.
>>> class Meta ( type ):
. def __new__ (mcls, name, bases, attrs):
. print ‘creating new class’ , name
. return super (Meta, mcls) . __new__(mcls, name, bases, attrs)
. def __init__ (cls, name, bases, attrs):
. print ‘initing new class’ , name
.
.
>>> class A ( object ):
. __metaclass__ = Meta
.
creating new class A
initing new class A
Во время инстанцирования просто объекта, никаких надписей не выводится.
>>> a = A()
>>>
Кроме того, соответственно, во методах __new__ и __init__ метакласса можно менять все: имя, список суперклассов, атрибуты.
Cсылки
- Unifying types and classes in Python — главный документ, объясняющий что, как и зачем в новых классах.
- Making Types Look More Like Classes — PEP 252, описывающий отличие старых классов от новых.
- Built-in functions — детальное описание работы всех встроенных функций.
- Data model — детальное описание модели данных python’а.
- Python types and objects — объяснение объектной модели python на простых примерах с картинками.
Примечания
[1] Более подробно про super — тут.