Рубрики
Без рубрики

Определение динамического класса в Python

Автор оригинала: Peter Gleeson.

Вот аккуратный питонский трюк, который вы можете просто найти полезный один день. Давайте посмотрим на то, как вы можете динамически определять классы, и создавать их экземпляры по мере необходимости.

Этот трюк использует возможности ориентированного на объект Python, поэтому мы сначала рассмотрим этими.

Классы и объекты

Python – это объектно-ориентированный язык, то есть позволяет вам написать код в Объектно-ориентированная парадигма Отказ

Ключевая концепция в этой парадигме программирования – это классы. В Python они используются для создания объектов, которые могут иметь атрибуты.

Объекты являются конкретными экземплярами класса. Класс по сути является план, что является объектом и как оно должно вести себя.

Классы определяются двумя типами атрибута:

  • Атрибуты данных – переменные доступны для данного экземпляра этого класса
  • Методы – Функции доступны для экземпляра этого класса

Классический пример ООП обычно включает в себя различные типы животных или еды. Здесь я прошел более практично с простой темой визуализации данных.

Сначала определите класс Barchart Отказ

class BarChart:
	def __init__(self, title, data):
    	self.title = title
    	self.data = data
   	def plot(self):
    	print("\n"+self.title)
        for k in self.data.keys():
        	print("-"*self.data[k]+" "+k)

__init__ Метод позволяет устанавливать атрибуты при создании. То есть, когда вы создаете новый экземпляр Barchart , вы можете пройти аргументы, которые предоставляют название и данные графика.

Этот класс также имеет Участок () метод. Это печатает очень базовую гистограмму на консоль, когда она называется. Это может вызвать более интересные вещи в реальном применении.

Далее создается экземпляр экземпляра Barchart :

data = {"a":4, "b":7, "c":8}bar = BarChart("A Simple Chart", data)

Теперь вы можете использовать бар Объект в остальной части вашего кода:

bar.data['d'] = bar.plot()
A Simple Chart
---- a
------- b
-------- c
----- d

Это здорово, потому что это позволяет динамически определять класс и создавать экземпляры. Вы можете раскрутить экземпляры других гистограмм в одной строке кода.

new_data = {"x":1, "y":2, "z":3}
bar2 = BarChart("Another Chart", new_data)
bar2.plot()
Another Chart
- x
-- y
--- z

Скажем, вы хотели определить несколько классов диаграммы. Наследование Позволяет определить классы, которые «наследуют» свойства от базовых классов.

Например, вы можете определить базу Диаграмма класс. Затем вы можете определить полученные классы, которые наследуют от основы.

class Chart:
	def __init__(self, title, data):
    	self.title = title
        self.data = data
    def plot(self):
    	pass
class BarChart(Chart):
	def plot(self):
    	print("\n"+self.title)
        for k in self.data.keys():
        	print("-"*self.data[k]+" "+k)
class Scatter(Chart):
	def plot(self):
    	points = zip(data['x'],data['y'])
        y = max(self.data['y'])+1
        x = max(self.data['x'])+1
        print("\n"+self.title)
        for i in range(y,-1,-1):
        	line = str(i)+"|"
            for j in range(x):
            	if (j,i) in points:
                	line += "X"
                else:
                	line += " "
            print(line)

Здесь Диаграмма Класс – это базовый класс. Barchart и Разброс Классы наследуют __init __ () Метод от Диаграмма. Но у них есть свои Участок () Методы, которые переопределяют один, определенный в Диаграмма Отказ

Теперь вы также можете создавать объекты Chart Chart.

data = {'x':[1,2,4,5], 'y':[1,2,3,4]}
scatter = Scatter('Scatter Chart', data)
scatter.plot()
Scatter Chart
4|     X
3|	  X 
2|  X
1| X
0|

Этот подход позволяет вам писать больше абстрактного кода, давая вашему приложению большую гибкость. Наличие чертежей для создания бесчисленных изменений одного и того же общего объекта, сэкономит вам излишне повторяющиеся строки кода. Это также может облегчить понять ваш код вашего приложения.

Вы также можете импортировать классы в будущие проекты, если вы хотите повторно использовать их позже.

Фабричные методы

Иногда вы не будете знать конкретный класс, который вы хотите реализовать до выполнения времени. Например, возможно, созданные вами объекты будут зависеть от ввода пользователя или результатов другого процесса с вариантом переменного результата.

Фабричные методы предложить решение. Это методы, которые принимают динамический список аргументов и возврат объекта. Поставляемые аргументами определяют класс объекта, который возвращается.

Простой пример показан ниже. Эта фабрика может вернуть либо гистограмму, либо объект графика разброса, в зависимости от Стиль Аргумент он получает. Умеренный фабричный метод может даже угадать лучший класс для использования, глядя на структуру данные аргумент

def chart_factory(title, data, style):
	if style == "bar":
    	return BarChart(title, data)
    if style == "scatter":
    	return Scatter(title, data)
    else:
    	raise Exception("Unrecognized chart style.")
        
    
chart = chart_factory("New Chart", data, "bar")
chart.plot()

Фабричные методы великолепны, когда вы заранее знаете, какие классы вы хотите вернуть, и условия, под которыми они возвращаются.

Но что, если вы даже не знаете это заранее?

Динамические определения

Python позволяет вам определять классы динамически и создавать с ними объекты по мере необходимости.

Почему вы можете сделать это? Краткий ответ еще больше абстракции.

По общему признанию, нужно писать код на этом уровне абстракции, как правило, является редким возникновением. Как всегда при программировании, вы должны рассмотреть, если есть более простое решение.

Тем не менее, могут быть времена, когда это действительно оказывается полезным, чтобы динамически определить классы. Мы охватим возможный случай использования ниже.

Вы можете быть знакомы с Python’s Тип () функция. С одним аргументом он просто возвращает «тип» объекта аргумента.

type(1) # 
type('hello') # 
type(True) # 

Но с тремя аргументами Тип () Возвращает совершенно новый Тип объекта Отказ Это эквивалентно определению нового класса Отказ

NewClass = type('NewClass', (object,), {})
  • Первый аргумент – это строка, которая дает новое имя класса
  • Далее – кортеж, который содержит любые базовые классы Новый класс должен наследовать от
  • Окончательный аргумент – это словарь атрибутов, специфичных для этого класса

Когда вам нужно использовать что-то такое же аннотация, как это? Рассмотрим следующий пример.

Таблица колба это библиотека Python, которая генерирует синтаксис для таблиц HTML. Он может быть установлен через диспетчер пакетов PIP.

Вы можете использовать таблицу Flask, чтобы определить классы для каждой таблицы, которую вы хотите создать. Вы определяете класс, который наследует от базы Таблица класс. Его атрибуты являются объектами столбца, которые являются экземплярами Col класс.

from flask_table import Table, Col
class MonthlyDownloads(Table):
	month = Col('Month')
    downloads = Col('Downloads')
    
data = [{'month':'Jun', 'downloads':700},
		{'month':'Jul', 'downloads':900},
        {'month':'Aug', 'downloads':1600},
        {'month':'Sep', 'downloads':1900},
        {'month':'Oct', 'downloads':2200}]
        
table = MonthlyDownloads(data)print(table.__html__())

Затем вы создаете экземпляр класса, передавая данные, которые вы хотите отобразить. __html __ () Метод генерирует требуемый HTML.

Теперь, скажем, вы разрабатываете инструмент, который использует таблицу Flask, чтобы генерировать таблицы HTML на основе пользовательского файла конфигурации. Вы заранее не знаете, сколько столбцов пользователь хочет определить – это может быть один, это может быть сто! Как ваш код может определить правильный класс для работы?

Определение динамического класса полезно здесь. Для каждого класса вы хотите определить, вы можете динамически построить атрибуты Словарь.

Скажите, что ваш пользователь Config – это файл CSV, со следующей структурой:

Table1, column1, column2, column3
Table2, column1
Table3, column1, column2

Вы можете прочитать строку файла CSV, используя первый элемент каждой строки как имя каждого класса таблицы. Остальные элементы в этой строке будут использоваться для определения Col объекты для этого класса таблицы. Они добавляются к атрибуты Словарь, который создан итеративно.

for row in csv_file:
	attributes = {}
    for column in row[1:]:
    	attributes[column] = Col(column)
        globals()[row[0]] = type(row[0], (Table,), attributes)

Вышеуказанный код определяет классы для каждой из таблиц в файле конфигурации CSV. Каждый класс добавлен в глобал Словарь.

Конечно, это относительно тривиальный пример. Фласком способен генерировать гораздо более сложные таблицы. Real Life Case использует бы лучше использовать это! Но, надеюсь, вы видели, как определение динамического класса может оказаться полезным в некоторых контекстах.

Так что теперь вы знаете …

Если вы новичок в Python, то стоит встать на скорость с классами и объектами в начале. Попробуйте реализовать их в вашем следующем учебном проекте. Или Просмотрите проекты с открытым исходным кодом на GitHub Чтобы увидеть, как другие разработчики используют их.

Для тех, кто с небольшим количеством опыта, он может быть очень награжден, чтобы узнать, как все работают «за кулисами». Просмотр Официальные документы Может быть освещение!

Вы когда-нибудь нашли использование для определения динамического класса в Python? Если это так, было бы здорово поделиться этим в ответах ниже.