Метаклассы настраивают классы.Но можем ли мы настроить метаклассы? - PullRequest
3 голосов
/ 03 апреля 2011

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

Каждое устройство создает серверный класс, специфичный для его работы, который является подклассом (подклассом ...) в конечном счете этого BaseServer класса.Теперь некоторым серверам устройств требуется ThreadedTCPserver, а некоторым - SimpleTCPServer (модуль: socketserver).Все они не могут быть производными от одного и того же класса, поскольку использование ThreadingMixin переопределяет поведение SimpleTCPServer.

. Для обработки этой конфигурации динамического класса я создал MetaServerType, который выбирает базовые классы для BaseServer как(SimpleTCPServer,) или как (ThreadedTCPServer,) -> создание желаемого результата для динамически настроенных классов серверов!(Woo Hoo)

Теперь, вот мой вопрос: я хотел бы использовать файл конфигурации, где хранятся параметры, и эти параметры по умолчанию используются MetaServerType.Например: config.default_loglevel или config.default_handler и т. Д. И отдельные серверы могут быть переопределены (из командной строки или иным образом) в соответствии со спецификациями метакласса.

Хорошей практикой проектирования является наличие только один экземпляр объекта конфигурации, передаваемого через программный поток?Один из способов добиться этого - инициализировать объект config в теле класса метакласса, но мой программный поток начинается в другом месте, и это означает, что метакласс вызывается несколько раз, создавая различные экземпляры config.Похоже, что метакласс вызывается во время импорта (?)

Так что подробный ответ очень приветствуется:

  1. Как можно предоставить метаклассы с информацией о конфигурации?
  2. Каков хороший способ передать единственный экземпляр конфигурации через поток программы, отредактировать, обновить и, возможно, в конце концов написать?
  3. Могут ли входные аргументы метакласса бытькак-то простирается за пределы Metaclass.__new__(meta, name, bases, attrs)?
  4. Дополнительный вопрос: приближает ли это нас на один шаг ближе к конечному автомату (серверов), чтобы состояние (не экземпляры) можно было «приостановить» или «возобновить»?

1 Ответ

3 голосов
/ 04 апреля 2011

1 - Как можно предоставить метаклассы с информацией о конфигурации?

Есть несколько способов сделать это - так как ваши метаклассы живут в своем собственном модуле (и да, модуль выполняется один раз в import раз, независимо от того, сколько раз он был импортирован в одно и то же приложение), хороший способ настроить их - иметь вызываемый объект (либо класс или функция в том же модуле), который будет устанавливать «глобальные переменные», которые будут использоваться для конфигурации.

Несмотря на свою плохую репутацию из-за C, откуда происходит название «global», глобальные переменные в Python на самом деле являются «модульными» переменными: это означает, что все функции (включая методы) в этом модуле могут обращаться к этим переменным. Функции или код в других модулях должны иметь префикс имени модуля для этого.

Так что функция как:

def configure_servers(p1, p2,...):
    global opt1, opt2, ...
    opt1 = p1
    opt2 = p2
    (...)

Может быть вызвано из точки входа вашего приложения до создания экземпляров сервера. (Конечно, вы можете передать путь к файлу конфигурации вместо p1, p2, ...)

2 - Какой хороший способ передать экземпляр конфигурации SINGLE через поток программы, чтобы быть отредактировано, обновлено и возможно в конце концов написано?

Имя глобальной переменной (модуля) в модуле метакласса может быть прочитано всеми ими, и оно может быть связано со сложным объектом конфигурации. Возможно, наличие функции «config», подобной приведенной выше, может сделать этот вопрос устаревшим.

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

Если вам нужно создать «настоящий» одноэлементный объект, вам следует создать класс и переопределить в нем метод __new__, чтобы он всегда возвращал первый созданный экземпляр - Пример: * +1028 *

class Singleton(object):
   _instance = None
   def __new__(cls, *args, **kw):
       if cls._instance is not None:
           return cls._instance
       self = object.__new__(cls, *args, **kw)
       cls._instance = self
       return self

3 - Можно ли каким-то образом расширить входные аргументы метакласса за пределы Metaclass.new (мета, имя, базы, атрибуты)?

Не использует синтаксис языка. Я имею в виду, что всегда можно вызвать метакласс как обычный вызов Python, но это помешает вам использовать синтаксис языка для описания вашего класса: вам нужно определить тело класса как словарь для передачи как attrs за звонок.

Например, чтобы создать производный класс исключений, можно сделать:

MyException = type("MyException", (Exception, ), {})

Вместо:

class MyException(Exception):
    pass

Обычный способ передачи дополнительной информации в метакласс - использование атрибутов с фиксированными именами в теле класса. Затем метакласс проверяет эти атрибуты внутри attrs и использует их. Затем он может выбрать сохранение в результирующем классе или удалить их из attrs на данном этапе.

Если информация, необходимая для передачи метакласса, известна только во время выполнения, эти атрибуты могут указывать на другие (на уровне модуля) переменные или содержать выражения Python, которые оцениваются во время создания класса.

mod_server_type = "TCP"

class YAServer(ParentServer):
   __metaclass__ = ServerMetaBase
   _sever_type = mod_server_type
   with open("config_file") as config:
      _server_params = pickle.load(config)
   del config

   def __init__(self,...):
      ...

В приведенном выше примере ваш метакласс может использовать атрибуты _server_type и _server_params для дальнейшего управления созданием класса.

...