Casting string to 'unit' object in Python [closed]

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-11 04:36:16

问题


In this question "unit(s)" refers to "unit(s) of measure" used in physical quantities.

I want to safely parse an untrusted user input string containing physical quantities (e.g. '23 kg'). I'd like to use one of the packages available for Python (like Unum or Quantities), because they take care of conversions between units, simplification of units contained in mathematical expression, reduction to base units, but I could not find a way to convert a string to an object (of the aforementioned packages) other than using eval(), which seems unsafe.

For example, assuming that kg and cm are bound to Unum package objects and refer to the kilogram and centimetre units, I need a function capable of returning an Unum package object from a string, something like:

cast_to_unit("100 kg/cm")

or

"100 kg/cm".asUnit()

  1. Is there a way to obtain such functionality from one the mentioned packages? And if not,
  2. Is there a way to safely use eval()? And if not,
  3. Is there any other package (even not in Python) having such functionality?

回答1:


Taking a quick look at Unum's documentation, it appears that you should be able to do something like this with eval:

from unum.units import * # Load a number of common units.
distance = 100*m
time = eval('9.683*s')
speed = distance / time
print(speed)

If you do this though, make sure that you read the documentation on eval and how you can pass local and global mappings in order to restrict the namespace access to make it safer -- especially if you ever plan on releasing this code to the outside world. You'll probably also want to check the strings for underscores (_) and not allow those to be passed to eval.


As noted by kindall, importing * is usually not good style. Here's a slightly more safe alternative which avoids importing from *:

import unum.units
safe_dict = dict((x,y) for x,y in unum.units.__dict__.items() if '__' not in x)
safe_dict['__builtins__'] = None

def convert(s):
    if '__' in s:
       raise ValueError("Won't do this with underscores...it's unsafe")
    return eval(s,safe_dict)

Note that I cut out the __ methods as advocated in this post




回答2:


Note: this is not a definitive answer. Please treat it as an example of how to build something similar to what you asked for. It is really a better idea to use Unum module, unless it does not fit your needs.

You can write something yourself, overloading the operators like that:

>>> class Unit(object):
    def __init__(self, unit_name):
        self.unit_name = unit_name
        self.unit_qty = 1
    def __div__(self, other):
        result = self.__class__(self.unit_name)
        result.unit_qty = self.unit_qty / other
        return result
    def __rdiv__(self, other):
        result = self.__class__(self.unit_name)
        result.unit_qty = other / self.unit_qty
        return result
    def __mul__(self, other):
        result = self.__class__(self.unit_name)
        result.unit_qty = self.unit_qty * other
        return result
    def __rmul__(self, other):
        return self.__mul__(other)
    def __repr__(self):
        return '%s %s' % (self.unit_qty, self.unit_name)


>>> kg = Unit('kg')
>>> 100 * kg
100 kg
>>> 100 * 10 * kg
1000 kg
>>> (10 * 20 * kg) / 2
100 kg

Then you could use eval() to evaluate expressions (like "100*kg"), but be careful about it. Probably this should help you get rid of some of the problems:

eval('100*kg', {'__builtins__': {}, 'kg': Unit('kg')})


来源:https://stackoverflow.com/questions/13611851/casting-string-to-unit-object-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!