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 = 300when 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.sizeThe last line access a property which does not exist inEmbedding class 'Char'. That should invokegetattr() which should return200 in this case. I am not familiar with metaclasses enough to make a good judgement, but I gues I need to define the__new__() of the metaclass.
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- 1I thing metaclass is ok, you should overwritegetattr in the metaclass and then get the value somehow. (Overwritingnew and just saving the kwargs in your case)MegaIng– MegaIng2018-01-16 19:17:43 +00:00CommentedJan 16, 2018 at 19:17
2 Answers2
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 ispossible for something like this to old all flexibility you need.
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.
So - for your problem, you need that in the final class, a__getattr__ method exists that can fetch default values for atributes. 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.
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 accessConfig.Embedding.Char.size with a non exisitngChar should return the default "size". Not that a__getattr__ in "Embedding" can return you a fake "Char" object - but that object is the one that have to yield asize attribute. So, our__getattr__ have yet to yield an object that has itself a propper__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. 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). But by just using the nested classes all the way, with a reserved name, a custom__getattr__ on the metac class will work. 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.
Thus, you can work with something like:
class ConfigMeta(type): def __getattr__(cls, attr): return cls._defaultclass Base(metaclass=ConfigMeta): passclass Config(Base): class Embed(Base): class _default(Base): size = 200 class Word(Base): size = 300assert Config.Embed.Char.size == 200assert Config.Embed.Word.size == 300Btw - 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.
Also, that is why I think being able to nest whole default subtrees can be useful for what you want - I've been there.
Comments
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 thesize attribute, it would be set to 300 (change this to meet your need).
1 Comment
class Log(BaseConfig, level=logging.DEBUG) for instanceExplore related questions
See similar questions with these tags.
