首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将函数的结果转换为所需单位的装饰器

将函数的结果转换为所需单位的装饰器
EN

Code Review用户
提问于 2016-02-22 16:54:30
回答 2查看 138关注 0票数 2

我尝试在类中收集我的一些公式,这样我就不需要再浏览函数的名称了,而只需要确定要计算的数量,然后检查哪些方法是可以确定的。这是基于astropy.units模块的。我只想举个小例子:

代码语言:javascript
复制
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

很好,我试过:

代码语言:javascript
复制
Length.from_velocity_time(100*u.km/u.h, 100*u.h)
# gives 10000 km

但是,当单位是等价的但不同的时候,有一些缺点:

代码语言:javascript
复制
Length.from_velocity_time(100*u.km/u.s, 100*u.h)
# 10000 h*km/s

因此,我决定创建一个通用装饰器,将结果单元转换为类中定义的单元,或者为每个方法调用增加一个参数result_unit

代码语言:javascript
复制
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

这很好(这一次必须在类中取消注释):

代码语言:javascript
复制
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中没有重复的逻辑是一个很大的好处。您会考虑这种“良好的编码”风格吗?还是我应该放弃这种尝试,选择一种不同的方法(如果是的话,可以选择哪种方法)?

EN

回答 2

Code Review用户

回答已采纳

发布于 2016-02-22 18:38:10

你的提议…

Length.from_velocity_time(100*u.km/u.s,100*u.h,result_unit=u.AU)

…不那么传统,也没有比

代码语言:javascript
复制
Length.from_velocity_time(100*u.km/u.s, 100*u.h).to(u.AU)

此外,猴子修补from_velocity_time()函数,以支持一个特殊的result_unit命名参数感觉粘糊不清。

.to(…)的唯一缺点是纯数字不支持这样的方法。但这将意味着,这种计算无论如何都是胡说八道。

票数 2
EN

Code Review用户

发布于 2016-02-23 01:04:38

我同意“200成功”的回答中所说的一切。它似乎也可能不是为所需的单元传递一个猴子补丁的关键字参数,而是使用一些不需要修饰的simplification关键字参数来自动简化单元,然后允许用户在需要时转换到他们想要的任何单元。

总的来说,似乎您正在做额外的计算工作,以满足用户不一定具备的需求,但完全能够自己完成。

我还想解决一下我在你写的文章中看到的一种小的代码味道。

你似乎总是在做事情之前先征得许可(如hasattrx in l等)。这并不一定是坏事,但在Python中,如果需要的话,我们更愿意乞求宽恕。特别是Python2,尤其是hasattr以引起问题而闻名。我会重写你的包装函数,让它看起来像这样。

代码语言:javascript
复制
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,这似乎并不总是正确的。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/120799

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档