简体   繁体   English

在以下情况下如何使用python metaclass?

[英]How to use python metaclass for the following scenario?

I want to create a configuration class with cascading feature. 我想创建一个具有级联功能的配置类。 What do I mean by this? 我是什么意思 let say we have a configuration class like this 假设我们有一个这样的配置类

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

when I use this in code I will access the configuration as follows, 当我在代码中使用此代码时,我将按以下方式访问配置,

def function(Config, blah, blah):
    word_embedding_size = Config.Embedding.Word.size
    char_embedding_size = Config.Embedding.Char.size

The last line access a property which does not exist in Embedding class 'Char'. 最后一行访问Embedding类“ Char”中不存在的属性。 That should invoke getattr() which should return 200 in this case. 那应该调用getattr() ,在这种情况下应该返回200 I am not familiar with metaclasses enough to make a good judgement, but I gues I need to define the __new__() of the metaclass. 我对元类的熟悉程度不足以做出良好的判断,但是我__new__()我需要定义元类的__new__()

does this approach makes sense or is there a better way to do it? 这种方法有意义吗,还是有更好的方法呢?

EDIT: 编辑:

class Config(BaseConfig):
   class Embedding(BaseConfig, size=200):
       class WordEmbedding(Embedding):
           size = 300
   class Log(BaseConfig, level=logging.DEBUG):
       class PREPROCESS(Log):
           level = logging.INFO

#When I use 
log = logging.getLogger(level=Config.Log.Model.level) #level should be INFO

You can use a metaclass to set the attribute: 您可以使用元类来设置属性:

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

Now if the class does not have the size attribute, it would be set to 300 (change this to meet your need). 现在,如果该类没有size属性,则将其设置为300(更改此值以满足您的需要)。

This is a bit confuse. 这有点混乱。 I am not sure if this would be the best notation to declare configurations with default parameters - it seems verbose. 我不确定这是否是使用默认参数声明配置的最佳表示法-似乎很冗长。 But yes, given the flexibility of metaclasses and magic methods in Python, it is possible for something like this to old all flexibility you need. 但是,是的,考虑到Python中元类和魔术方法的灵活性,类似这样的东西可能会超出您所需的所有灵活性。

Just for the sake of it, I'd like to say that using nested classes as namespaces, like you are doing, is probably the only useful thing for them. 仅出于此目的,我想说的是,像您所做的那样,将嵌套类用作名称空间可能对它们唯一有用的事情。 (nested classes). (嵌套类)。 It is common to see a lot of people that misunderstands Python OO at all trying to make use of nested classes. 经常看到很多人对Python OO都产生了误解,而他们都试图利用嵌套类。

So - for your problem, you need that in the final class, a __getattr__ method exists that can fetch default values for atributes. 所以-对于您的问题,您需要在最后一个类中存在一个__getattr__方法,该方法可以获取属性的默认值。 These attributes in turn are declared as keywords to nested classes - which also can have the same metaclass. 这些属性又被声明为嵌套类的关键字-嵌套类也可以具有相同的元类。 Otherwise, the hierarchy of nested classes just work for you to fetch nested attributes, using the dot notation in Python. 否则,嵌套类的层次结构只适合您使用Python中的点表示法来获取嵌套属性。

Moreover, for each class in a nested set, one can pass in keyword parameters that are to be used as default, if the next level of nested classes is not defined. 而且,对于嵌套集中的每个类,如果未定义下一级嵌套类,则可以传入将用作默认值的关键字参数。 In the given example, trying to access Config.Embedding.Char.size with a non exisitng Char should return the default "size". 在给定的示例中,尝试使用不存在的Char访问Config.Embedding.Char.size应该返回默认的“ size”。 Not that a __getattr__ in "Embedding" can return you a fake "Char" object - but that object is the one that have to yield a size attribute. 并不是说“嵌入”中的__getattr__可以返回伪造的“ Char”对象-而是该对象是必须产生size属性的对象。 So, our __getattr__ have yet to yield an object that has itself a propper __getattr__ ; 因此,我们的__getattr__尚未产生一个本身具有合适的__getattr__的对象;

However, I will suggest a change to your requirements - instead of passing in the default values as keyword parameters, to have a reserved name - like _default inside which you can put your default attributes. 但是,我建议您更改要求 -而不是将默认值作为关键字参数传递,而是要保留一个名称-像_default一样,您可以在其中放置默认属性。 That way, you can provide deeply nested default subtress, instead of just scalar values as well, and the implementation can possibly be simpler. 这样,您可以提供深度嵌套的默认子项,而不仅仅是标量值,并且实现可能会更简单。

Actually - a lot simpler. 实际上-简单得多。 By using keywords to the class as you propose, you'd actually need to have a metaclass set those default parameters in a data structure(it would be possible in either __new__ or __init__ though). 通过按照您的建议使用类的关键字,实际上需要使元类在数据结构中设置那些默认参数(尽管可以在__new____init__ )。 But by just using the nested classes all the way, with a reserved name, a custom __getattr__ on the metac class will work. 但是,通过完全使用嵌套类并使用保留名称,可以在metac类上使用自定义__getattr__ That will retrieve unexisting class attributes on the configuration classes themselves, and all one have to do, if a requested attribute does not exist, is try to retrieve the _default class I mentioned. 这将检索配置类本身上不存在的类属性,如果请求的属性不存在,则所有要做的就是尝试检索我提到的_default类。

Thus, you can work with something like: 因此,您可以使用类似的方法:

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

Btw - just last year I was working on a project to have configurations like this, with default values, but using a dictionary syntax - that is why I mentioned I am not sure the nested class would be a nice design. 顺便说一句-去年,我正在一个项目中进行这样的配置,使用默认值,但是使用字典语法-这就是为什么我提到我不确定嵌套类是否是一个不错的设计。 But since all the functionality can be provided by a metaclass with 3 LoC I guess this beats anything in the way. 但是由于所有功能都可以由具有3 LoC的元类提供,所以我猜想这胜过一切。

Also, that is why I think being able to nest whole default subtrees can be useful for what you want - I've been there. 另外,这就是为什么我认为能够嵌套整个默认子树对您想要的东西有用的原因-我去过那里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM