Python 有没有一种方法可以动态更新子类中的变量?
我有两个配置类,基类Python 有没有一种方法可以动态更新子类中的变量?,python,python-3.x,flask,Python,Python 3.x,Flask,我有两个配置类,基类config和子类ProdConfig,代码如下所示: 类配置: URL=”http://127.0.0.1" 另一个URL=URL+“/home” 类ProdConfig(配置): URL=”http://test.abc.com" #另一个URL=URL+“/home” 打印(Config.URL)#”http://127.0.0.1" 打印(配置另一个URL)#“http://127.0.0.1/home" 打印(ProdConfig.URL)#”http://test
config
和子类ProdConfig
,代码如下所示:
类配置:
URL=”http://127.0.0.1"
另一个URL=URL+“/home”
类ProdConfig(配置):
URL=”http://test.abc.com"
#另一个URL=URL+“/home”
打印(Config.URL)#”http://127.0.0.1"
打印(配置另一个URL)#“http://127.0.0.1/home"
打印(ProdConfig.URL)#”http://test.abc.com"
打印(ProdConfig.other_URL)#http://127.0.0.1/home
如果我没有在ProdConfig中重写变量另一个_URL
,或者将其声明为@property
,则该值与基类相同,我得到它的原因是在导入基类时分配了值,但我可以用新值将其与新URL
对齐吗?有没有办法使用元类
或setattr
或getattr
技巧来解决此问题
多谢各位 如问题和随后的讨论所述,
属性
的使用不能直接用于类定义,强制子类也定义属性以维护每个属性的定义“协议”可能会变得很麻烦。还建议使用格式字符串,但对于标准类型
元类在类定义中完成的赋值,我们仍然存在相同的问题,即它不会被重新计算。考虑另一种方法:
class Config:
URL = "http://127.0.0.1"
ANOTHER_URL = f"{URL}/home"
class ProdConfig(Config):
URL = "http://test.abc.com"
运行以下操作将无法产生所需的结果:
>>> conf = ProdConfig()
>>> print(conf.URL)
http://test.abc.com
>>> print(conf.ANOTHER_URL)
http://127.0.0.1/home
只是因为另一个_URL
没有在ProdConfig
的范围内重新分配。但是,可以使用以下元类解决此问题:
class ConfigFormatMeta(type):
def __init__(cls, name, bases, attrs):
# create and store a "private" mapping of original definitions,
# for reuse by subclasses
cls._config_map = config_map = {}
# merge all config_maps of base classes.
for base_cls in bases:
if hasattr(base_cls, '_config_map'):
config_map.update(base_cls._config_map)
# update the config_map with original definitions in the newly
# constructed class, filter out all values beginning with '_'
config_map.update({
k: v for k, v in vars(cls).items() if not k.startswith('_')})
# Now assign the formatted attributes to the class
for k in config_map:
# Only apply to str attributes; other types of attributes
# on the class will need additional work.
if isinstance(config_map[k], str):
setattr(cls, k, config_map[k].format(**config_map))
super().__init__(name, bases, attrs)
class Config(metaclass=ConfigFormatMeta):
URL = 'http://example.com'
ANOTHER_URL = '{URL}/home'
class ProdConfig(Config):
URL = 'http://abc.example.com'
这样,请尝试使用新元类将Config
类作为基础:
class ConfigFormatMeta(type):
def __init__(cls, name, bases, attrs):
# create and store a "private" mapping of original definitions,
# for reuse by subclasses
cls._config_map = config_map = {}
# merge all config_maps of base classes.
for base_cls in bases:
if hasattr(base_cls, '_config_map'):
config_map.update(base_cls._config_map)
# update the config_map with original definitions in the newly
# constructed class, filter out all values beginning with '_'
config_map.update({
k: v for k, v in vars(cls).items() if not k.startswith('_')})
# Now assign the formatted attributes to the class
for k in config_map:
# Only apply to str attributes; other types of attributes
# on the class will need additional work.
if isinstance(config_map[k], str):
setattr(cls, k, config_map[k].format(**config_map))
super().__init__(name, bases, attrs)
class Config(metaclass=ConfigFormatMeta):
URL = 'http://example.com'
ANOTHER_URL = '{URL}/home'
class ProdConfig(Config):
URL = 'http://abc.example.com'
现在再试一次:
>>> conf = ProdConfig()
>>> print(conf.URL)
http://abc.example.com
>>> print(conf.ANOTHER_URL)
http://abc.example.com/home
请注意,ProdConfig
的范围中没有重新定义另一个URL,但是只具有URL
的期望行为被重新定义为具有的期望值http://abc.example.com/home“
已实现
另外值得注意的是,使用元类会干扰,并且与原始映射有一些重复,这些重复通过类本身的
\u config\u map
属性略微隐藏,因此,请主要将其视为概念证明。如果您想继承站点的默认主目录,但也可以在创建子类实例时覆盖它。你可以这样做
这显示了子类如何继承默认状态,或根据传递给构造函数的参数自定义其状态
>>> class Config:
... LOCALHOST = "http://127.0.0.1"
... DEFAULT_HOME = "/home"
... def __init__(self):
... self._home = self.DEFAULT_HOME
...
... @property
... def url(self):
... return self.LOCALHOST + self.home
...
... @property
... def home(self):
... return self._home
...
... @home.setter
... def home(self, h):
... self._home = h
...
>>> class ProdConfig(Config):
...
... def __init__(self, home=None):
... super().__init__()
... if home:
... self.home = home
>>>
>>> c = Config()
>>> d = ProdConfig()
>>> e = ProdConfig("/someotherhome")
>>>
>>> c.url
'http://127.0.0.1/home'
>>> d.url
'http://127.0.0.1/home'
>>> e.url
'http://127.0.0.1/someotherhome'
>>>
我上面的想法是展示一个良好的实践,提供子类对其继承状态的访问,而不需要直接敏感地访问基类的变量。私有
\u home
基类变量由子类通过属性访问。使用另一种语言,我可能会声明home
为受保护的
属性,而\u home
为private由proerty
定义,你是指@property
装饰者吗?当在类级别访问另一个\u URL
的赋值时,即使这样也不能解决您的问题-一个属性
通常只对类的实例产生影响(即访问ProdConfig。另一个_URL
将返回那里的精确赋值,这将是属性
对象定义,而不是应用程序可能期望的值).Metaclass可能有帮助,但其额外的复杂性可能会导致额外的复杂性。在任何情况下,如果您正在考虑使用元类实现类属性,则有一个解决方案,但请注意所需的额外复杂性。是的,谢谢,它是@property
,我编辑了它。尽管它不会直接解决我的问题,但我可以初始化Config
或ProdConfig
,然后将其用作一个对象,也许它作为一个配置类很难看,我想,然后简单地定义一个名为的函数,另一个URL
返回self.URL+'/home'
,并用@property
装饰器装饰它。是的,它可能很好,但我认为它不是谨慎地在配置类中编写方法,如果我重写子类中的字段,我认为这太多的编写是不必要的,所以这就是我混淆的地方。。。