我尝试在类中收集我的一些公式,这样我就不需要再浏览函数的名称了,而只需要确定要计算的数量,然后检查哪些方法是可以确定的。这是基于astropy.units模块的。我只想举个小例子:
import astropy.units as u
class Length(object):
unit = u.m
@classmethod
#@quantitydecorator # I'll explain this later
def from_velocity_time(cls, velocity, time):
"""
Calculate length from a given velocity and time.
Parameters
----------
velocity: `astropy.units.Quantity`
The velocity of the object.
time: `astropy.units.Quantity`
The time the object moved at this velocity
Returns
-------
length: `astropy.units.Quantity`
The determined length based on velocity * time
"""
return velocity * time很好,我试过:
Length.from_velocity_time(100*u.km/u.h, 100*u.h)
# gives 10000 km但是,当单位是等价的但不同的时候,有一些缺点:
Length.from_velocity_time(100*u.km/u.s, 100*u.h)
# 10000 h*km/s因此,我决定创建一个通用装饰器,将结果单元转换为类中定义的单元,或者为每个方法调用增加一个参数result_unit:
def quantitydecorator(f):
"""
Allows for an extra argument "result_unit" for all methods
and converts the result to the unit specified there or if not
given to the unit specified in the class.
"""
@wraps(f)
def wrapper(*args, **kwds):
# Check if result_unit is given otherwise use the class default
# (args[0] is the class)
if 'result_unit' in kwds:
result_unit = u.Unit(kwds.pop('result_unit'))
else:
result_unit = args[0].unit
# Calculate the result
result = f(*args, **kwds)
# Convert to result unit if the result has a unit
if not hasattr(result, 'unit'):
# No unit attribute so we have a plain numpy array
# or number, only let it pass when target unit is
# dimensionless
if args[0].unit != u.dimensionless_unscaled:
raise ValueError('Got dimensionless quantity but needed'
' quantity with unit: {0}'.format(args[0].unit))
# Result has a different unit than wanted, convert it to wanted unit
elif result.unit != result_unit:
result = result.to(result_unit)
return result
return wrapper这很好(这一次必须在类中取消注释):
Length.from_velocity_time(100*u.km/u.s, 100*u.h)
# 3.6e10m
Length.from_velocity_time(100*u.km/u.s, 100*u.h, result_unit=u.AU)
# 0.24064514AU但我不禁怀疑这很尴尬。我使用了大约300个函数,但只有大约30-40个参数(比如例子中的长度,但更复杂的是“自由落体时间”,“半光半径”)。“红移”等,我在任何计算中所需要的所有东西,通常有2-10种方法来计算这样的数量),所以将它们收集到类中,并且在result_unit中没有重复的逻辑是一个很大的好处。您会考虑这种“良好的编码”风格吗?还是我应该放弃这种尝试,选择一种不同的方法(如果是的话,可以选择哪种方法)?
发布于 2016-02-22 18:38:10
你的提议…
Length.from_velocity_time(100*u.km/u.s,100*u.h,result_unit=u.AU)
…不那么传统,也没有比
Length.from_velocity_time(100*u.km/u.s, 100*u.h).to(u.AU)此外,猴子修补from_velocity_time()函数,以支持一个特殊的result_unit命名参数感觉粘糊不清。
.to(…)的唯一缺点是纯数字不支持这样的方法。但这将意味着,这种计算无论如何都是胡说八道。
发布于 2016-02-23 01:04:38
我同意“200成功”的回答中所说的一切。它似乎也可能不是为所需的单元传递一个猴子补丁的关键字参数,而是使用一些不需要修饰的simplification关键字参数来自动简化单元,然后允许用户在需要时转换到他们想要的任何单元。
总的来说,似乎您正在做额外的计算工作,以满足用户不一定具备的需求,但完全能够自己完成。
我还想解决一下我在你写的文章中看到的一种小的代码味道。
你似乎总是在做事情之前先征得许可(如hasattr、x in l等)。这并不一定是坏事,但在Python中,如果需要的话,我们更愿意乞求宽恕。特别是Python2,尤其是hasattr以引起问题而闻名。我会重写你的包装函数,让它看起来像这样。
result_unit = u.Unit(kwds.pop('result_unit', args[0].unit))
result = f(*args, **kwds)
try:
if result.unit != result_unit:
result = result.to(result_unit)
except AttributeError:
# No unit attribute so we have a plain numpy array
# or number, only let it pass when target unit is
# dimensionless
if result_unit != u.dimensionless_unscaled:
raise ValueError('Got dimensionless quantity but needed'
' quantity with unit: {0}'.format(args[0].unit))
return result你也(似乎)有一个小错误--你说“只有当目标单元是无量纲的时候,才让它通过”,但是比较了args[0].unit而不是result_unit,这似乎并不总是正确的。
https://codereview.stackexchange.com/questions/120799
复制相似问题