【发布时间】:2012-05-31 13:59:42
【问题描述】:
我的代码有异味。也许我只是需要让它通风一下,但现在它让我很烦。
我需要创建三个不同的输入文件来运行三个辐射传递模型 (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
...
}
参数很多,可以很干净的归类,所以想到了dict里面的dicts:
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar: {
azimuth_angle: 73, #degrees
zenith_angle: 17, #degrees
...
},
...
}
我喜欢这样。但是有很多多余的属性。例如,如果知道另一个方位角和天顶角,就可以找到太阳方位角和天顶角,那么为什么要对两者都进行硬编码呢?所以我开始研究python的内置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代码不漂亮!我一定是做错了什么!
【问题讨论】:
标签: python object properties inner-classes