3

我发现元类的存在和使用可以通过为类创建过程提供一个优雅的句柄来使您免于编写大量代码。我在我的应用程序中使用它,其中实例化了几个交互服务器。详细说明:

每个设备都实例化一个特定于其操作的服务器类,它是(...的子类)的子类,最终是这一BaseServer类。现在,有些设备服务器需要一个ThreadedTCPserver,有些需要一个SimpleTCPServer(模块:)socketserver。它们不能都派生自同一个类,因为使用ThreadingMixin覆盖了SimpleTCPServer.

为了处理这个动态类配置,我创建了一个MetaServerType,它选择 BaseServer 的基类 as(SimpleTCPServer,)或 as (ThreadedTCPServer,)--> 生成我想要的动态配置的服务器类的结果!(呜呼)

现在,我的问题来了:我想使用一个存储参数的配置文件,这些参数默认由 MetaServerType 使用。例如:config.default_loglevel 或 config.default_handler 等。并且可以根据元类规范覆盖单个服务器(从命令行或其他方式)。

只有一个配置对象的实例通过程序流是一种好的设计实践吗?实现这一点的一种方法是在元类的类主体中初始化配置对象——但我的程序流程从其他地方开始,这意味着元类被多次调用,从而产生了各种配置实例。似乎在导入时调用了元类(?)

因此,非常欢迎您提供详细的答案:

  1. 如何为元类提供配置信息?
  2. 单个配置实例通过程序流进行编辑、更新甚至最终编写的好方法是什么?
  3. 元类的输入参数能否以某种方式扩展超出Metaclass.__new__(meta, name, bases, attrs)
    1. 额外的问题:这是否让我们更接近(服务器的)有限状态机,以便状态(而不是实例)可以“暂停”或“恢复”?
4

1 回答 1

3

1 - 如何为元类提供配置信息?

有几种方法可以做到这一点 - 因为您的元类存在于它们自己的模块中(是的,模块一次执行一次import无论它在同一个应用程序中导入多少次),这是配置它们的好方法将有一个可调用对象(同一模块上的类或函数),它将设置用于配置的“全局变量”。

尽管由于“global”这个名称的来源C而声名狼藉,但Python中的全局变量实际上是“模块”变量:这意味着该模块中的所有函数(包括方法)都可以访问这些变量。其他模块中的函数或代码必须为此模块名称添加前缀。

所以一个像这样的函数:

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

可以在创建服务器实例之前从您的应用程序入口点调用。(当然,您可以传递要读取的配置文件路径而不是p1, p2, ...)

2 - 让单个配置实例通过程序流,进行编辑,更新甚至最终编写的好方法是什么?

元类模块上的全局(模块)变量名可以被所有人读取,并且可以与复杂的配置对象相关联。也许像上面这样的“配置”功能的存在可以使这个问题过时。

但是如果你真的需要一个“单例”对象,即只有一个实例的对象,你可以用简单的方法来做:在元类字典上有一个类,然后传递那个类,而不是它的一个实例。如果您有字典而不是课程,则更好,更清洁。

如果您需要创建一个“真正的”单例对象,您应该创建一个类并覆盖其中的__new__方法,以便它始终返回第一个创建的实例 - 示例:

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(meta, name, bases, attrs) 之外?

没有利用语言语法。我的意思是,总是可以将元类调用为普通的 Python 调用,但这会阻止您使用语言语法来描述您的类:您需要将类主体定义为字典以attrs传递称呼。

例如,要创建一个派生异常类,可以这样做:

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

代替:

class MyException(Exception):
    pass

将附加信息传递给元类的常用方法是在类主体上使用具有固定名称的属性。然后元类检查这些属性attrs并使用它们。它可以选择将 then 保留在结果类中,或者attrs此时从 dict 中删除它们。

如果您需要传递元类的信息仅在运行时已知,则这些属性可以指向其他(模块级)变量,或包含在类创建时评估的 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 属性来进一步控制类的创建。

于 2011-04-04T00:01:07.547 回答