【问题标题】:A decorator which converts string to number [duplicate]将字符串转换为数字的装饰器[重复]
【发布时间】:2017-08-20 05:11:52
【问题描述】:

我想使用装饰器将字符串格式的数字转换为 int/float 值,这就是我的尝试

def str_to_int(func):
     """
        This wrapper converts string value into integer
     """
     def wrapper(*args, **kwargs):
         for arg in args:
             arg = int(arg)
         return func(*args, **kwargs)
     return wrapper
 @str_to_int
 def number_as_string(a, b, c):
     return a,b,c
 print (number_as_string('1', '2', '3'))

输出

('1', '2', '3')

但是,我想要像下面这样的东西

(1, 2, 3)

上面的输出我可以用下面的代码生成

def str_to_int(func):
     """
        This wrapper converts string value into integer
     """
     def wrapper(x, y, z):
         return int(x), int(y), int(z)
     return wrapper
 @str_to_int
 def number_as_string(a, b, c):
     return a,b,c
 print (number_as_string('1', '2', '3'))

但是上面的代码首先破坏了使用装饰器的目的,因为我想转换字符串值而不管原始函数的参数。

任何人都可以建议第一个程序有什么问题以及如何解决它。

【问题讨论】:

  • 您没有在第一个示例中更改 args。在循环中更改arg 不会做任何事情,除了使arg 成为int。您需要建立一个新的args 列表并将其传递给func 或者您可以执行return func(*map(int, args), **kwargs)
  • 谢谢,成功了!!
  • 你明白它为什么有效吗?为什么您的原始代码不起作用?
  • 我部分理解了,我想如果我将每个 arg 更改为 int,那么新的 args 将是 int。你能解释一下吗?

标签: python decorator python-decorators


【解决方案1】:

它没有按预期工作的原因是因为您从未更新传入的args,您只是不断覆盖循环中的值。

考虑这个修订版:

>>> def str_to_int(f):
...   def wrapper(*args, **kwargs):
...     args = map(int, args)
...     return f(*args, **kwargs)
...   return wrapper
...
>>> @str_to_int
... def foo(a,b,c):
...   return a,b,c
...
>>> foo('1','2','3')
(1, 2, 3)

关键是列表行:

args = map(int, args)

它确保原始 args 元组被完全替换,在您的循环中,您默默地丢弃了该值。

对于 Python3,您应该使用:

args = tuple(map(int, args))

因为地图会返回一个地图对象。

当然,我希望你意识到这个装饰器只有在你传递它而不是实际转换为数字时才有效,如果你传递一个字符串,那么它会引发一个ValueError

>>> foo('hello')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in wrapper
ValueError: invalid literal for int() with base 10: 'hello'

如果你想防止这种情况,你必须忽略那些不能转换为整数的值:

def wrapper(*args, **kwargs):
   _temp = []
   for i in args:
     try:
       _temp.append(int(i))
     except ValueError:
       _temp.append(i)
   args = tuple(_temp)
   return f(*args, **kwargs)

现在它会简单地忽略它无法转换的东西:

>>> foo('hello', '1', '2')
('hello', 1, 2)

您可以稍微缩短代码,因为* 会自动扩展参数,您可以利用它来发挥自己的优势:

 # args = tuple(_temp)
 return f(*_temp, **kwargs)

【讨论】:

  • 为什么要创建args = tuple(_temp) 只是为了在f() 调用中解压它。 f(*_temp, ...) 还不够吗?即使tuple(map()) 是不必要的* 也会解压生成器(Py3 中的map())。
  • 是的,它会 - 但在这里我是明确的 - 可能这个人可能不太了解它。有效点,我应该添加它。
  • 谢谢,现在我明白了。以前我也尝试用 args 的更新 int 值构建一个数组,但它不会作为元组传递。不知道*的这个属性
猜你喜欢
  • 2019-10-27
  • 2016-09-06
  • 1970-01-01
  • 2014-11-15
  • 1970-01-01
  • 2019-02-21
相关资源
最近更新 更多