我的代码有难闻的气味。也许我只需要让它通风一下,但现在它让我很烦。
我需要创建三个不同的输入文件来运行三个辐射传递模型 (RTM) 应用程序,以便比较它们的输出。这个过程将针对数千组输入重复,所以我使用 python 脚本将其自动化。
我想将输入参数存储为一个通用 python 对象,我可以将其传递给其他三个函数,每个函数都会将该通用对象转换为运行他们负责的 RTM 软件所需的特定参数。我认为这是有道理的,但请随意批评我的方法。
每个 RTM 软件都有许多可能的输入参数。他们中的许多人重叠。它们中的大多数都保持合理的默认值,但应该很容易更改。
我从一个简单的开始dict
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar_azimuth_angle: 73, #degrees
solar_zenith_angle: 17, #degrees
...
}
参数很多,而且可以很干净的归类,所以我想到了在 s 中使用dict
s dict
:
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar: {
azimuth_angle: 73, #degrees
zenith_angle: 17, #degrees
...
},
...
}
我喜欢。但是有很多多余的属性。例如,如果知道对方位角和天顶角,就可以找到太阳方位角和天顶角,那么为什么要对两者都进行硬编码呢?所以我开始研究 python 的 builtin property
。如果我将数据存储为对象属性,这让我可以对数据做一些漂亮的事情:
class Configuration(object):
day_of_year = 138,
time_of_day = 36000, #seconds
solar_azimuth_angle = 73, #degrees
@property
def solar_zenith_angle(self):
return 90 - self.solar_azimuth_angle
...
config = Configuration()
但现在我失去了第二个dict
例子中的结构。
请注意,某些属性没有我的solar_zenith_angle
示例那么简单,并且可能需要访问它所属的属性组之外的其他属性。例如,我可以计算solar_azimuth_angle
我是否知道一年中的哪一天、一天中的时间、纬度和经度。
我在找什么:
一种存储配置数据的简单方法,其值都可以以统一的方式访问,结构良好,可以作为属性(实际值)或属性(从其他属性计算)存在。
有点无聊的可能性:
将所有内容存储在我之前概述的 dicts 中,并让其他函数在对象上运行并计算可计算值?这听起来不好玩。或者干净。对我来说,这听起来很混乱和令人沮丧。
一个丑陋的作品:
经过很长时间尝试不同的策略并且大多无处可去,我想出了一个似乎可行的解决方案:
我的课:(闻起来有点古怪,呃,时髦。绝对是。)
class SubConfig(object):
"""
Store logical groupings of object attributes and properties.
The parent object must be passed to the constructor so that we can still
access the parent object's other attributes and properties. Useful if we
want to use them to compute a property in here.
"""
def __init__(self, parent, *args, **kwargs):
super(SubConfig, self).__init__(*args, **kwargs)
self.parent = parent
class Configuration(object):
"""
Some object which holds many attributes and properties.
Related configurations settings are grouped in SubConfig objects.
"""
def __init__(self, *args, **kwargs):
super(Configuration, self).__init__(*args, **kwargs)
self.root_config = 2
class _AConfigGroup(SubConfig):
sub_config = 3
@property
def sub_property(self):
return self.sub_config * self.parent.root_config
self.group = _AConfigGroup(self) # Stinky?!
我如何使用它们:(按我的意愿工作)
config = Configuration()
# Inspect the state of the attributes and properties.
print("\nInitial configuration state:")
print("config.rootconfig: %s" % config.root_config)
print("config.group.sub_config: %s" % config.group.sub_config)
print("config.group.sub_property: %s (calculated)" % config.group.sub_property)
# Inspect whether the properties compute the correct value after we alter
# some attributes.
config.root_config = 4
config.group.sub_config = 5
print("\nState after modifications:")
print("config.rootconfig: %s" % config.root_config)
print("config.group.sub_config: %s" % config.group.sub_config)
print("config.group.sub_property: %s (calculated)" % config.group.sub_property)
行为:(所有上述代码的执行输出,如预期的那样)
Initial configuration state:
config.rootconfig: 2
config.group.sub_config: 3
config.group.sub_property: 6 (calculated)
State after modifications:
config.rootconfig: 4
config.group.sub_config: 5
config.group.sub_property: 20 (calculated)
为什么我不喜欢它:
将配置数据存储在主对象内部的类定义__init__()
中并不优雅。特别是必须在像这样定义之后立即实例化它们。啊。当然,我可以为父类处理这个问题,但是在构造函数中进行......
在主Configuration
对象之外存储相同的类也感觉不优雅,因为内部类中的属性可能取决于Configuration
(或其内部的兄弟姐妹)的属性。
我可以处理定义一切之外的功能,所以在里面有类似的东西
@property
def solar_zenith_angle(self):
return calculate_zenith(self.solar_azimuth_angle)
但我不知道该怎么做
@property
def solar.zenith_angle(self):
return calculate_zenith(self.solar.azimuth_angle)
(当我试图变得聪明时,我总是遇到<property object at 0xXXXXX>
)
那么解决这个问题的正确方法是什么?我错过了一些基本的东西还是采取了非常错误的方法?有谁知道一个聪明的解决方案?
帮助!我的python代码不漂亮!我一定做错了什么!