- Динамическое определение класса в Python
- Метакласс type
- Инициализация новых типов с помощью класса type
- Параметры инициализации класса type
- Динамическое определение класса
- Динамическое определение атрибутов класса
- Динамическое определение методов
- Параметры функции compile
- Параметры метода инициализации класса FunctionType
- Предупреждение
- Ссылки
- How to create a Class dynamically with type() in Python?
- Examples
- 1. Create a class dynamically with given classname and attributes
- 2. Create a class dynamically with a constructor
- 3. Create a class dynamically with a constructor
- Summary
Динамическое определение класса в Python
Под динамическим определением объекта можно понимать определение во время исполнения. В отличие от статического определения, которое используется в привычном определении класса с помощью ключевого слова class , динамическое определение использует встроенный класс type .
Метакласс type
Класс type часто используется для получения типа объекта. Например так:
Но у него есть другое применение. Он может инициализировать новые типы. Как известно, всё в Python – объект. Из этого следует, что у всех определений имеются типы, включая классы и объекты. Например:
Может быть не совсем понятно, почему классу присваивается тип класса type , в отличие от его экземпляров:
Объекту a в качестве типа присваивается класс. Так интерпретатор обрабатывает объект как экземпляр класса. Сам же класс имеет тип класса type потому, что он наследует его от базового класса object :
Класс object наследуют все классы по умолчанию, то есть:
Определяемый класс наследует базовый в качестве типа. Однако, это не объясняет, почему базовый класс object имеет тип класса type . Дело в том, что type – это метакласс. Как это уже известно, все классы наследуют базовый класс object , который имеет тип метакласса type . Поэтому, все классы так же имеют этот тип, включая сам метакласс type :
Это «конечная точка типизации» в Python. Цепочка наследования типов замыкается на классе type . Метакласс type служит базой для всех классов в Python. В этом несложно убедиться:
builtins = [list, dict, tuple] for obj in builtins: type(obj)
Класс – это абстрактный тип данных, а его экземпляры имеют ссылку на класс в качестве типа.
Инициализация новых типов с помощью класса type
При проверке типов класс type инициализируется с единственным аргументом:
При этом он возвращает тип объекта. Однако в классе реализован другой способ инициализации с тремя аргументами, который возвращает новый тип:
type(name, bases, dict) -> new type
Параметры инициализации класса type
- name
Строка, которая определяет имя нового класса (типа). - bases
Кортеж базовых классов (классов, которые унаследует новый класс). - dict
Словарь с атрибутами будущего класса. Обычно со строками в ключах и вызываемых типах в значениях.
Динамическое определение класса
Инициализируем класс нового типа, предоставив все необходимые аргументы и вызываем его:
MyClass = type("MyClass", (object, ), dict()) MyClass
С новым классом можно работать как обычно:
Причём, способ эквивалентен обычному определению класса:
Динамическое определение атрибутов класса
В пустом классе мало смысла, поэтому возникает вопрос: как добавить атрибуты и методы?
Чтобы ответить на этот вопрос, рассмотрим изначальный код инициализации:
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”)', "", "exec")
compile – это встроенная функция, которая компилирует исходный код в объект. Код можно выполнить функциями exec() или eval() .
Параметры функции compile
- source
Исходный код, может быть ссылкой на модуль. - filename
Имя файла, в который скомпилируется объект. - mode
Если указать «exec» , то функция скомпилирует исходный код в модуль.
Объект 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
Необязательный параметр, определяющий название функции.
Далее необходимо добавить эту функцию в качестве метода класса MyClass :
MyClass.foo = MethodType(function, MyClass)
Достаточно простое выражение, которое назначает нашу функцию методом класса MyClass .
Предупреждение
В 99% случаев можно обойтись статическим определением классов. Однако концепция метапрограммирования хорошо раскрывает внутреннее устройство Python. Скорее всего вам будет сложно найти применение описанных здесь методов, хотя в моей практике такой случай, все же, был.
А вы работали с динамическими объектами? Может быть в других языках?
Ссылки
How to create a Class dynamically with type() in Python?
In Python, to create a class dynamically, you can use type() built-in function. This can be used to create user defined classes on the fly, without changing the code when a new class type is required.
Consider the following simple example, where we create a class named MyClass of type object, with attributes: x.
MyClass = type('MyClass', (object,), )
In this tutorial, we will learn how to create a class dynamically, with examples.
Examples
1. Create a class dynamically with given classname and attributes
In this example, we define a function that creates and returns a class. The function has two parameters. First argument is the classname, and the second argument is a dictionary representing the attributes of the class.
Python Program
def createClass(classname, attributes): return type(classname, (object,), attributes) Car = createClass('Car', ) mycar = Car() print(f"Car name: \nCar age: years")
Car name: Audi Car age: 5 years
2. Create a class dynamically with a constructor
In this example, we define a function that creates and returns a class with a constructor.
Python Program
def createClass(classname, attributes): return type(classname, (object,), < '__init__': lambda self, arg1, arg2: setattr(self, 'args', (arg1, arg2)), 'args': attributes >) Car = createClass('Car', ) mycar = Car('Audi R8', 3) print(f"Car name: \nCar age: years")
Car name: Audi R8 Car age: 3 years
3. Create a class dynamically with a constructor
In this example, we consider the function defined in the previous example, and create class types: Car and Student dynamically.
Python Program
def createClass(classname, attributes): return type(classname, (object,), < '__init__': lambda self, arg1, arg2: setattr(self, 'args', (arg1, arg2)), 'args': attributes >) Car = createClass('Car', ) Student = createClass('Student', ) mycar = Car('Audi R8', 3) print(f"Car name: \nCar age: years") student1 = Student('Ram', 12) print(f"Student name: \nStudent age: years")
Car name: Audi R8 Car age: 3 years Student name: Ram Student age: 12 years
Summary
In this tutorial of Python Examples, we learned how to use type() function to create a class type dynamically.