[Python, ООП] Динамическое определение класса в Python
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Под динамическим определением объекта можно понимать определение во время исполнения. В отличие от статического определения, которое используется в привычном определении класса с помощью ключевого слова class, динамическое определение использует встроенный класс type.
Абстрактный класс type
Класс type часто используется для получения типа объекта. Например так:
h = "hello"
type(h)
<class 'str'>
Но у него есть другое применение. Он может инициализировать новые типы. Как известно, всё в Python – объект. Из этого следует, что у всех определений имеются типы, включая классы и объекты. Например:
class A:
pass
print(type(A))
<class 'type'>
Может быть не совсем понятно, почему классу присваивается тип класса type, в отличие от его инстанций:
a = A()
print(type(a))
<class '__main__.A'>
Объекту a в качестве типа присваивается класс. Так интерпретатор обрабатывает объект как инстанцию класса. Сам же класс имеет тип класса type потому, что он наследует его от базового класса object:
A.__bases__
(<class 'object'>,)
Тип класса object:
type(object)
<class 'type'>
Класс object наследуют все классы по умолчанию, то есть:
class A(object):
pass
Тоже самое, что:
class A:
pass
Определяемый класс наследует базовый в качестве типа. Однако, это не объясняет, почему базовый класс object имеет тип класса type. Дело в том, что type – это абстрактный класс. Как это уже известно, все классы наследуют базовый класс object, который имеет тип абстрактного класса type. Поэтому, все классы так же имеют этот тип, включая класс type:
type(type)
<class 'type'>
Это «конечная точка типизации» в Python. Цепочка наследования типов замыкается на классе type. Свойство абстрактности классов предполагает наследование и альтерацию атрибутов. А значит, класс type служит базой вообще для всех типов данных в Python. В этом не сложно убедиться:
builtins = [list(), dict(), tuple()]
for obj in builtins:
print(type(obj))
<class 'type'>
<class 'type'>
<class 'type'>
Класс – это абстрактный тип данных, а его инстанции имеют ссылку на класс в качестве типа.
Инициализации новых типов с помощью класса type
При проверке типов класс type инициализируется с единственным аргументом:
type(object) -> type
При этом он возвращает тип объекта. Однако в классе реализован другой способ инициализации с тремя аргументами, который возвращает новый тип:
type(name, bases, dict) -> new type
Параметры инициализации класса type
- name – это строка, которая определяет имя нового класса (типа)
- bases – кортеж базовых классов (классов, которые унаследует новый класс)
- dict – словарь с атрибутами будущего класса. Обычно со строками в ключах и вызываемых типах в значениях
Динамическое определение класса
Инициализируем класс нового типа, предоставив все необходимые аргументы и вызываем его:
MyClass = type(“MyClass”, (object, ), dict())
MyClass()
<__main__.MyClass object at 0x7f8b1d69add8>
С новым классом можно работать как обычно:
m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>
Причём, способ эквивалентен обычному определению класса:
class MyClass:
pass
Динамическое определение атрибутов класса
В пустом классе мало смысла, поэтому возникает вопрос: как добавить атрибуты и методы?
Чтобы ответить на этот вопрос, рассмотрим изначальный код инициализации:
MyClass = type(“MyClass”, (object, ), dict())
Обычно, атрибуты добавляются в класс на стадии инициализации в качестве третьего аргумента – словаря. В словаре можно указать имена атрибутов и значения. Например, это может быть переменная:
MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'
Динамическое определение методов
В словарь можно передать и вызываемые объекты, например методы:
def foo(self):
return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'
У этого способа есть один существенный недостаток – необходимость определять метод статически (думаю, что в контексте задач динамического программирования, это можно рассматривать как недостаток). Кроме этого, определение метода с параметром self вне тела класса выглядит странно. Поэтому вернёмся к динамической инициализации класса без атрибутов:
MyClass = type(“MyClass”, (object, ), dict())
После инициализации пустого класса, можно добавить в него методы динамически, то есть, без явного статического определения:
code = compile('def foo(self): print(“bar”)', "<string>", "exec")
compile – это встроенная функция, которая компилирует исходный код в объект. Код можно выполнить функциями exec() или eval().
Параметры функции compile
- source – исходный код, может быть ссылкой на модуль
- filename – имя файла, в который скомпилируется объект
- mode – если указать "exec", то функция скомпилирует исходный код в модуль
Результатом работы compile является объект класса code:
type(code)
<class 'code'>
Объект code нужно преобразовать в метод. Так как метод – это функция, то начнём с преобразования объекта класса code в объект класса function. Для этого импортируем модуль types:
from types import FunctionType, MethodType
Я импортирую MethodType, так как он понадобится в дальнейшем для преобразования функции в метод класса.
function = FunctionType(code.co_consts[0], globals(), “foo”)
Параметры метода инициализации класса FunctionType
- code – объект класса code. code.co_consts[0] – это обращение к дискриптору co_consts класса code, который представляет из себя кортеж с константами в коде объекта. Представьте себе объект code как модуль с одной единственной функцией, которую мы пытаемся добавить в качестве метода класса. 0 – это её индекс, так как она единственная константа в модуле
- globals() – словарь глобальных переменных
- name – необязательный параметр, определяющий название функции
В результате получилась функция:
function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>
Далее необходимо добавить эту функцию в качестве метода класса MyClass:
MyClass.foo = MethodType(function, MyClass)
Достаточно простое выражение, которое назначает нашу функцию методом класса MyClass.
m = MyClass()
M.foo()
bar
Предупреждение
В 99% случаев можно обойтись статическим определением классов. Однако концепция динамического программирования хорошо раскрывает внутренне устройство Python. Скорее всего вам будет сложно найти применение описанных здесь методов, хотя в моей практике такой случай, все же, был.
А вы работали с динамическими объектами? Может быть в других языках?
===========
Источник:
habr.com
===========
Похожие новости:
- [Python, Алгоритмы, Математика, Обработка изображений] Декодируем JPEG-изображение с помощью Python (перевод)
- [Python, Программирование] Создание нейросети по распознаванию лиц на фотографиях из Вконтакте
- [Python, Программирование, Промышленное программирование] Устройство CPython. Доклад Яндекса
- [Python, Искусственный интеллект, Машинное обучение] Учим ИИ распределять пироги по магазинам с помощью обучения с подкреплением
- Обновление Python 3.8.5 с устранением уязвимостей
- [API, Python, Анализ и проектирование систем, Программирование] Между дизайн-системой и Server Driven UI
- [Big Data, Python, Машинное обучение, Программирование] Почему стоит начать использовать FastAPI прямо сейчас (перевод)
- [Big Data, Математика, Машинное обучение] Sktime: унифицированная библиотека Python для машинного обучения и работы с временными рядами (перевод)
- [Go, Python, Программирование, Разработка веб-сайтов] Священный холивар «Python vs Go»
- [PHP, ООП, Совершенный код] Вы уверены, что пишете объектно-ориентированный код? (перевод)
Теги для поиска: #_python, #_oop (ООП), #_python, #_compile, #_class, #_object, #_dynamic, #_types, #_functiontype, #_methodtype, #_python, #_oop (
ООП
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:02
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Под динамическим определением объекта можно понимать определение во время исполнения. В отличие от статического определения, которое используется в привычном определении класса с помощью ключевого слова class, динамическое определение использует встроенный класс type. Абстрактный класс type Класс type часто используется для получения типа объекта. Например так: h = "hello"
type(h) <class 'str'> Но у него есть другое применение. Он может инициализировать новые типы. Как известно, всё в Python – объект. Из этого следует, что у всех определений имеются типы, включая классы и объекты. Например: class A:
pass print(type(A)) <class 'type'> Может быть не совсем понятно, почему классу присваивается тип класса type, в отличие от его инстанций: a = A()
print(type(a)) <class '__main__.A'> Объекту a в качестве типа присваивается класс. Так интерпретатор обрабатывает объект как инстанцию класса. Сам же класс имеет тип класса type потому, что он наследует его от базового класса object: A.__bases__
(<class 'object'>,) Тип класса object: type(object)
<class 'type'> Класс object наследуют все классы по умолчанию, то есть: class A(object):
pass Тоже самое, что: class A:
pass Определяемый класс наследует базовый в качестве типа. Однако, это не объясняет, почему базовый класс object имеет тип класса type. Дело в том, что type – это абстрактный класс. Как это уже известно, все классы наследуют базовый класс object, который имеет тип абстрактного класса type. Поэтому, все классы так же имеют этот тип, включая класс type: type(type)
<class 'type'> Это «конечная точка типизации» в Python. Цепочка наследования типов замыкается на классе type. Свойство абстрактности классов предполагает наследование и альтерацию атрибутов. А значит, класс type служит базой вообще для всех типов данных в Python. В этом не сложно убедиться: builtins = [list(), dict(), tuple()]
for obj in builtins: print(type(obj)) <class 'type'> <class 'type'> <class 'type'> Класс – это абстрактный тип данных, а его инстанции имеют ссылку на класс в качестве типа. Инициализации новых типов с помощью класса type При проверке типов класс type инициализируется с единственным аргументом: type(object) -> type
При этом он возвращает тип объекта. Однако в классе реализован другой способ инициализации с тремя аргументами, который возвращает новый тип: type(name, bases, dict) -> new type
Параметры инициализации класса type
Динамическое определение класса Инициализируем класс нового типа, предоставив все необходимые аргументы и вызываем его: MyClass = type(“MyClass”, (object, ), dict())
MyClass() <__main__.MyClass object at 0x7f8b1d69add8> С новым классом можно работать как обычно: m = MyClass()
m <__main__.MyClass object at 0x7f8b1d69acc0> Причём, способ эквивалентен обычному определению класса: class MyClass:
pass Динамическое определение атрибутов класса В пустом классе мало смысла, поэтому возникает вопрос: как добавить атрибуты и методы? Чтобы ответить на этот вопрос, рассмотрим изначальный код инициализации: MyClass = type(“MyClass”, (object, ), dict())
Обычно, атрибуты добавляются в класс на стадии инициализации в качестве третьего аргумента – словаря. В словаре можно указать имена атрибутов и значения. Например, это может быть переменная: MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass() m.foo 'bar' Динамическое определение методов В словарь можно передать и вызываемые объекты, например методы: def foo(self):
return “bar” MyClass = type(“MyClass”, (object, ), dict(foo=foo)) m = MyClass() m.foo 'bar' У этого способа есть один существенный недостаток – необходимость определять метод статически (думаю, что в контексте задач динамического программирования, это можно рассматривать как недостаток). Кроме этого, определение метода с параметром self вне тела класса выглядит странно. Поэтому вернёмся к динамической инициализации класса без атрибутов: MyClass = type(“MyClass”, (object, ), dict())
После инициализации пустого класса, можно добавить в него методы динамически, то есть, без явного статического определения: code = compile('def foo(self): print(“bar”)', "<string>", "exec")
compile – это встроенная функция, которая компилирует исходный код в объект. Код можно выполнить функциями exec() или eval(). Параметры функции compile
Результатом работы compile является объект класса code: type(code)
<class 'code'> Объект code нужно преобразовать в метод. Так как метод – это функция, то начнём с преобразования объекта класса code в объект класса function. Для этого импортируем модуль types: from types import FunctionType, MethodType
Я импортирую MethodType, так как он понадобится в дальнейшем для преобразования функции в метод класса. function = FunctionType(code.co_consts[0], globals(), “foo”)
Параметры метода инициализации класса FunctionType
В результате получилась функция: function
<function foo at 0x7fc79cb5ed90> type(function) <class 'function'> Далее необходимо добавить эту функцию в качестве метода класса MyClass: MyClass.foo = MethodType(function, MyClass)
Достаточно простое выражение, которое назначает нашу функцию методом класса MyClass. m = MyClass()
M.foo() bar Предупреждение В 99% случаев можно обойтись статическим определением классов. Однако концепция динамического программирования хорошо раскрывает внутренне устройство Python. Скорее всего вам будет сложно найти применение описанных здесь методов, хотя в моей практике такой случай, все же, был. А вы работали с динамическими объектами? Может быть в других языках? =========== Источник: habr.com =========== Похожие новости:
ООП ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:02
Часовой пояс: UTC + 5