如何在以下场景中使用python元类?

如何在以下场景中使用python元类?,python,metaclass,Python,Metaclass,我想创建一个具有级联特性的配置类。我这是什么意思?假设我们有这样一个配置类 class BaseConfig(metaclass=ConfigMeta, ...): def getattr(): return 'default values provided by the metaclass' class Config(BaseConfig): class Embedding(BaseConfig, size=200): class WordEmbedd

我想创建一个具有级联特性的配置类。我这是什么意思?假设我们有这样一个配置类

class BaseConfig(metaclass=ConfigMeta, ...):
   def getattr():
       return 'default values provided by the metaclass'

class Config(BaseConfig):
   class Embedding(BaseConfig, size=200):
       class WordEmbedding(Embedding):
           size = 300
当我在代码中使用它时,我将访问如下配置:

def function(Config, blah, blah):
    word_embedding_size = Config.Embedding.Word.size
    char_embedding_size = Config.Embedding.Char.size
最后一行访问
嵌入
类“Char”中不存在的属性。它应该调用
getattr()
,在本例中,它应该返回
200
。我对元类不太熟悉,不能做出很好的判断,但我想我需要定义元类的
\uuu new\uuu()

这种方法有意义吗?还是有更好的方法

编辑:


可以使用元类设置属性:

class ConfigMeta(type):
    def __new__(mt, clsn, bases, attrs):
        try:
            _ = attrs['size']
        except KeyError:
            attrs['size'] = 300
        return super().__new__(mt, clsn, bases, attrs)

现在,如果类没有
size
属性,它将被设置为300(根据需要更改此属性)。

这有点混乱。我不确定这是否是用默认参数声明配置的最佳表示法——它似乎很冗长。但是是的,考虑到Python中元类和魔术方法的灵活性,像这样的东西可以满足所有需要的灵活性

出于这个原因,我想说,使用嵌套类作为名称空间,就像您正在做的那样,可能是对它们唯一有用的事情。(嵌套类)。很多人试图利用嵌套类,但却对Python OO产生了误解,这是很常见的

因此-对于您的问题,您需要在最后一个类中,存在一个
\uuu getattr\uuu
方法,该方法可以获取atributes的默认值。这些属性依次被声明为嵌套类的关键字,嵌套类也可以具有相同的元类。否则,嵌套类的层次结构只适用于使用Python中的点表示法获取嵌套属性

此外,对于嵌套集中的每个类,如果未定义下一级嵌套类,则可以传入要用作默认值的关键字参数。在给定的示例中,尝试使用不存在的
Char
访问
Config.Embedding.Char.size
应返回默认的“size”。并不是说“嵌入”中的
\uuuu getattr\uuuu
可以返回一个伪“Char”对象,而是该对象必须产生
size
属性。因此,我们的
\uuuuu getattr\uuuuu
还没有生成一个对象,该对象本身有一个propper
\uuuu getattr\uuuuu

但是,我建议更改您的需求——不要将默认值作为关键字参数传递,而是使用保留名称,如
\u default
,您可以在其中放置默认属性。通过这种方式,您可以提供深度嵌套的默认子压力,而不仅仅是标量值,并且实现可能更简单

事实上,要简单得多。通过像您建议的那样对类使用关键字,您实际上需要让一个元类在数据结构中设置这些默认参数(可以在
\uuuuuu new\uuuuuu
\uuuu init\uuuu
中设置)。但是,只要始终使用嵌套类,并使用保留名称,metac类上的自定义
\uu getattr\uuu
就可以工作。这将检索配置类本身上不存在的类属性,如果请求的属性不存在,则只需尝试检索我提到的
\u default

因此,您可以使用以下内容:

class ConfigMeta(type):
    def __getattr__(cls, attr):
        return cls._default

class Base(metaclass=ConfigMeta):
    pass

class Config(Base):
    class Embed(Base):
        class _default(Base):
            size = 200
        class Word(Base):
            size = 300

assert Config.Embed.Char.size == 200
assert Config.Embed.Word.size == 300
顺便说一句——就在去年,我在一个项目中工作,使用这样的配置,使用默认值,但使用字典语法——这就是为什么我提到我不确定嵌套类是否是一个好的设计。但是由于所有的功能都可以由一个有3个LoC的元类提供,我想这比任何东西都好


另外,这也是为什么我认为能够嵌套整个默认子树对于您想要的东西很有用的原因——我已经去过了。

我认为元类没问题,您应该在元类中覆盖getattr,然后以某种方式获取值。(覆盖新内容并仅保存案例中的kwargs)术语“大小”不是固定的。对于不同的配置,它可能不同。大小用于嵌入,我可能有“级别”用于日志配置。例如,我需要通过
类日志(BaseConfig,level=logging.DEBUG)
将属性的名称传递给元类
class ConfigMeta(type):
    def __getattr__(cls, attr):
        return cls._default

class Base(metaclass=ConfigMeta):
    pass

class Config(Base):
    class Embed(Base):
        class _default(Base):
            size = 200
        class Word(Base):
            size = 300

assert Config.Embed.Char.size == 200
assert Config.Embed.Word.size == 300