Comments 16
Экземпляр класса - это же инстанс, ну когда память под класс выделили, так? А без экземпляра класс просто тип бестелесный, да?
Полагаю что нет. Как минимум, есть инстанс типа класса
Да, экземпляр класса - это инстанс этого класса. Что вы подразумеваете под бестелестностью? То, что у него нет ни полей, ни методов? Это не так. Все классы наследуются как минимум от object и обладают его атрибутами. Самое простое это магический метод __init__
, а из полей можно привести в пример переменную __doc__
. А экземпляры класса никак не влияют на его атрибуты.
Ну то что класс без своего инстанса - это просто определение класса, с полями, методами и прочим, но к ним нельзя обратиться (считать, вызвать), т.к. класс не положен на память, поля не имеют данных, а методы - кода. Но прошу не пинать, я не питонщик.
Нет. В питоне у класса есть инстанс, по сути обычный объект с незначительными нюансами. Как бы статик методы на это намекают.
А про какой вы язык говорите? Если объяснять как дело обстоит в питоне, то можно привести следующий код:
>>> class MyClass:
class_attribute = "Hello"
@classmethod
def class_method(cls):
print("This is a class method")
def instance_method(self):
print("This is an instance method")
>>> MyClass.class_attribute
'Hello'
>>> MyClass.class_method()
This is a class method
>>> MyClass.instance_method(object)
This is an instance method
У нас нет экземпляра, но мы можем получить доступ как к полям, так и методам класса. И даже к методам экземпляра (на это указывает параметр self), однако надо передать явно ссылку, потому что когда метод экземпляра вызывает от экземпляра, то в него автоматически (неявно) передается ссылка на этот объект. Здесь, для работы функции, ссылка не важна и передаем ее просто для того, чтобы у нас не поднялось исключение.
Изучение ООП питона после C++ - тот ещё кошмар... А статья полезная: кратко, четко, лаконично. Спасибо!
И насколько это эффективно?
Насколько метаклассы эффективны? Сами по себе или в контексте их использования где-то в коде? Конечно, если можно их не использовать, то лучше поискать аналог без них. Потому что они, как минимум, усложняют читабельность.
Может возникнуть заблуждение, что если класс A является экземпляром класса B (то есть isinstance(A, B) равно True), то можно создать экземпляры класса A с помощью класса B. Однако это не всегда верно.
Это вообще не верно
Класс А является экземпляром класса B. Это значит что B - метакласс. Класс int - экземпляр класса type. Очевидно что 3 и 7 нельзя создать с помощью класса type.
Возможно вы имели в виду "если объект А ... можно создать объект А и аналогичные ему"
Это заблуждение очень просто развеять на бытовом примере
d = Dog()
isinstance(d, Animal) == True
Очевидно что d нельзя создать с помощью только класса Animal, это его наследник.
Когда рассматриваем экземпляры (кто кого инстанцирует)
isinstance не отвечает на вопрос "кто кого инстанцирует"(!), он отвечает на вопрос "есть ли между первым аргументом и вторым is-a relationship", "может ли А быть интерпретировано как экземпляр класса B". И это снимает существенную часть вопросов - int это класс (тип), экземпляр класса type, а тот - наследник object'а. Очевидно, int можно, например, подставить в функцию которая принимает object.
Да, вы правы, здесь вообще лучше убрать упоминание о классах. Имелось в виду, что если функция isinstance возвращает True, то это еще не говорит о том, что первый объект экземпляр второго.
Очевидно что d нельзя создать с помощью только класса Animal, это его наследник.
Вы, верно, ошиблись. d не наследник Animal. Dog наследник Animal как я понял. И тут вы показали пример, о котором говорю и я. isinstance возвращает True, но второй аргумент не порождает первый.
isinstance не отвечает на вопрос "кто кого инстанцирует"(!)
В статье не было так написано. Я наоборот писал следующее:
Хотя если дословно переводить название функции (является экземпляром/есть экземпляр) ... При этом будет ошибкой как в наследовании сказать, что type порождает объекты целого типа ...
И я специально обозначил, что эта схема довольно условна. Говорится только о том, что и в каком порядке порождает объекты.
Не понял это:
он отвечает на вопрос ... "может ли А быть интерпретировано как экземпляр класса B" .
И что вы действительно думаете, что int может интерпретироваться как экземпляр object? int наследник object и не более.
Метакласс в двух словах: хук для создания класса. Другими словами возможность создать класс программно, а не декларативно.
Спасибо за поднятие темы еще раз в этом месяце. Могу отметить, что к пониманию, что такое метаклассы, так и не подобрались. И только в комментариях @Andrey_Solomatin наконец-то дал максимально близкое объяснение. Поблагодарил его отдельно.
Повторяя свой комментарий из соседней статьи. Метакласс это объект, который уточняет поведение какого либо другого класса, и метакласс, как и любой объект python, можно создать runtime (вопреки утверждению в статье):
MyMetaclass = type('MyMetaClass', (type,), {})
Управлять поведением класса и экземплярами класса можно несколькими способами: через декоратор, через наследование, через метакласс. Так в чем же разница?
Метакласс - официальный способ вклиниться в код ДО объявления класса. Это в статье отмечено, но не подчеркнуто, а ведь это единственное существенное отличие.
Декоратор класса: может донастраивать класс ПОСЛЕ объявления, поскольку получает на вход уже объявленный класс.
Родительский класс - уточняет поведение класса или экземпляров в runtime, предоставляя доступ к атрибутам и методам как напрямую так и через вызов super.
Именно хук beforeCreateClass и является тем важным инструментом метакласса, ради чего нужны метаклассы. И вот именно этот хук используется в ABC, хотя лучше бы использовать Protocol. Не реализовал метод протокола - лови Exception ДО создания класса на старте а не в runtime. Но это тоже шито белыми нитками из-за возможности объявления классов в runtime. Именно поэтому я категорически против Protocols/ABC, поскольку они способны добавить более сложные ошибки в код вместо упрощения.
Причина использования Metaclass в Django аналогична. На объявлении класса модели происходит внесение модели в реестр моделей (django.apps.apps.all_models
). Последнее можно было бы решить более стандартно, например, через сигналы, но что сделано - то сделано.
В остальном по статье - утверждение "в Python все является объектом" намного глубже, чем может показаться на первый взгляд, и желаю автору добраться до настоящего понимания этого высказывания. Для этого, вероятно, стоит посмотреть повнимательнее внутрь cpython/Objects/typeobject.c
Спасибо за пример, что метаклассы действительно можно создавать динамически. Поправлю в статье. Можно ли использовать в статье ваши слова о том, что можно управлять поведением класса разными способами?
Погружение в метаклассы в Python