我一直在为项目设计元类布局,其中所有类都使用一个自定义元类来加载其定义的配置以及父级。基本上,每个类都定义一个嵌套的Config
类,该类将加载到dict,然后子级也可以定义一个,并且该类使用所有父配置,并且覆盖所有新值。
当我将Config类加载到dict之后不删除Config类时,它很好用,但是现在我试图重构和清除名称空间,这会导致问题。新的(断开的)代码如下:
class AbstractConfigMeta(ABCMeta): """Parse nested Config classes and fill a new classes config dict.""" def __new__(mcs, name, bases, namespace): """Traverse the MRO backwards from object to load all Config classes. Any config declared in sub classes overwrites base classes. """ # get any config defined in parent classes first config = {} for parent in reversed(bases): if hasattr(parent, "config"): config.update(parent.config) # pop Config class and add values if defined config_class = namespace.pop("Config", None) if config_class: # get all non-magic (i.e. user-defined) attributes attributes = { key: value for key, value in config_class.__dict__.items() if not key.startswith("__") } config.update(attributes) namespace["config"] = config return super().__new__(mcs, name, bases, namespace)
使用时解析
Config
类,但现在不使用父级的任何配置。实例化后仍保留嵌套类的旧代码是:
class AbstractConfigMeta(ABCMeta): """Parse nested Config classes and fill a new classes config dict.""" def __new__(mcs, name, bases, namespace): """Traverse the MRO backwards from object to load all Config classes. Any config declared in sub classes overwrites base classes. """ new_class = super().__new__(mcs, name, bases, namespace) new_class.config = {} # type: ignore for parent in reversed(new_class.__mro__): config_class = getattr(parent, "Config", None) if config_class: # get all non-magic attributes from each Config class values = { key: value for key, value in config_class.__dict__.items() if not key.startswith("__") } new_class.config.update(values) # type: ignore return new_class
似乎现在尝试使用元类创建的字典来访问配置,父配置已被丢弃。任何帮助将不胜感激。
该问题原来是由一些使用嵌套Config类但不使用元类的Mixins引起的。在旧代码块中,这很好,但是当从config dict而不是嵌套类更改为获取父config时,任何不使用元类的都将没有此定义,因此将使用不使用其值的Config类。 >
最终工作代码,包括修复和jsbueno建议的边缘案例:
class AbstractConfigMeta(ABCMeta): """Parse nested Config classes and fill a new classes config dict.""" def __new__(mcs, name, bases, namespace): """Traverse the MRO backwards from object to load any config dicts. Any Config class declared in sub classes overwrites parent classes. """ # pop Config class and add its attributes if defined config_class = namespace.pop("Config", None) if config_class: # get all non-magic (i.e. user-defined) attributes attributes = { key: value for key, value in config_class.__dict__.items() if not key.startswith("__") } if namespace.get("config"): warnings.warn( f"A config dict and a config class are defined for {name}." + " Any values in the config dict will be overwritten." ) namespace["config"] = attributes new_class = super().__new__(mcs, name, bases, namespace) # get any config dicts defined in the MRO (including the current class) config = {} for parent in reversed(new_class.__mro__): if hasattr(parent, "config"): config.update(parent.config) # type: ignore new_class.config = config # type: ignore return new_class
我一直在为项目设计元类布局,其中所有类都使用一个自定义元类来加载其定义的配置以及父级。基本上每个类都定义一个嵌套的Config ...
问题是,在新代码中,您正在类显式bases
上进行交互,而旧的(有效)代码在__mro__
上进行了迭代。
[bases
将只产生显式声明的祖先,并且不会访问任何更复杂的层次结构中的“祖父母”或类。