【问题标题】:Python dictionary switch goes through other functions instead of just the one calledPython 字典切换通过其他函数,而不仅仅是调用的函数
【发布时间】:2015-10-14 17:43:58
【问题描述】:

我正在制作一个极其简单的计算器风格应用程序,其中使用 5 个参数调用控制函数。第一个是设置计算器模式的字符串,其他 4 个是正在使用的数字。我用字典来方便各种模式之间的切换。

但是,应用程序似乎正在使用数字检查所有功能,而不仅仅是我想要的功能。例如,如果我另外使用负数,我会在平方根函数中得到数学域错误(显然不能使用负数)。使用正常的正数没有问题。

感谢您提供有关此问题发生原因以及解决方法的任何帮助或信息。

这是我的原始代码:

import math

def control(a, x, y, z, k):
    return {
        'ADDITION': addition(x, y),
        'SUBTRACTION': subtraction(x, y),
        'MULTIPLICATION': multiplication(x, y),
        'DIVISION': division(x, y),
        'MOD': modulo(x, y),
        'SECONDPOWER': secondPower(x),
        'POWER': power(x, y),
        'SECONDRADIX': secondRadix(x),
        'MAGIC': magic(x, y, z, k)
    }[a]

def addition(x, y):
    return float(x) + float(y)

def subtraction(x, y):
    return float(x) - float(y)

def multiplication(x, y):
    return float(x) * float(y)

def division(x, y):
    return float(x) / float(y)

def modulo(x, y):
    return float(x) % float(y)

def secondPower(x):
    return math.pow(float(x),2.0)

def power(x, y):
    return math.pow(float(x),float(y))

def secondRadix(x):
    return math.sqrt(float(x))

def magic(x, y, z, k):
    l = float(x) + float(k)
    m = float(y) + float(z)
    return (l / m) + 1.0

a = input()
x = input()
y = input()
z = input()
k = input()

try:
    control(a, x, y, z, k)
except ValueError:
    print("This operation is not supported for given input parameters")

out = control(a, x, y, z, k)
print(out)

这是日志:

C:\<path>\Python\Python35-32\python.exe C:/<path>/calculator.py
ADDITION
-6.0
5
3
2
This operation is not supported for given input parameters
Traceback (most recent call last):
  File "C:/<path>/calculator.py", line 67, in <module>
    out = control(a, x, y, z, k)
  File "C:/<path>/calculator.py", line 13, in control
    'SECONDRADIX': secondRadix(x),
  File "C:/<path>/calculator.py", line 47, in secondRadix
    return math.sqrt(float(x))
ValueError: math domain error

Process finished with exit code 1

【问题讨论】:

  • 所以在您的示例中,您是否除了输出为 -1.0,因为您提供了键“ADDITION”并且前两个数字是 -6.0 和 5,问题是您传入了负数作为输入,你不能取负数的平方,而且由于你的代码返回你创建的字典,它会运行每个定义的方法,即使你只返回一个项目的值。跨度>

标签: python function math dictionary switch-statement


【解决方案1】:

您需要延迟评估您的功能:

from functools import partial

def control(a, x, y, z, k):
    return {
        'ADDITION':       partial(addition, x, y),
        'SUBTRACTION':    partial(subtraction, x, y),
        'MULTIPLICATION': partial(multiplication, x, y),
        'DIVISION':       partial(division, x, y),
        'MOD':            partial(modulo, x, y),
        'SECONDPOWER':    partial(secondPower, x),
        'POWER':          partial(power, x, y),
        'SECONDRADIX':    partial(secondRadix, x),
        'MAGIC':          partial(magic, x, y, z, k)
    }[a]()

在每次调用 control 时创建柯里化函数效率低下,所以这里有第二种方法,只创建一次字典:

def control(a, x, y, z, k):
    return control.funcs[a](x, y, z, k)
control.funcs = {
    'ADDITION':       (lambda x, y, z, k: addition(x, y)),
    'SUBTRACTION':    (lambda x, y, z, k: subtraction(x, y)),
    'MULTIPLICATION': (lambda x, y, z, k: multiplication(x, y)),
    'DIVISION':       (lambda x, y, z, k: division(x, y)),
    'MOD':            (lambda x, y, z, k: modulo(x, y)),
    'SECONDPOWER':    (lambda x, y, z, k: secondPower(x)),
    'POWER':          (lambda x, y, z, k: power(x, y)),
    'SECONDRADIX':    (lambda x, y, z, k: secondRadix(x)),
    'MAGIC':          (lambda x, y, z, k: magic(x, y, z, k))
}

此时,取消control 函数并直接使用字典可能会更简单。

【讨论】:

  • 在这种情况下使用 partials 而不是 lambdas 有好处吗?我们对柯里化不太感兴趣,所以使用 lambdas 可能更容易(但不一定更干净)
  • @SnakesandCoffee:我考虑过lambas,但我认为它们太冗长了。
  • 由于您担心每次都创建部分和 dict,您也可以将它们绑定到控制(不确定是否是个好主意,虽然可以)。在这种情况下,Python 缺少 C 风格的静态函数有点烦人。
  • @SnakesandCoffee:我添加了一个变体。
  • 这对我来说非常有效。也感谢您的所有其他回复。我以为它正在调用所有函数,但不确定为什么以及如何处理它(不诉诸if-elif-elif 结构)。
【解决方案2】:

最简单的做法是重写代码,代码更多,但更容易维护,您不会运行所有方法,只运行您想要的方法,因为看起来您只是返回单个键.如果要运行多个操作,则将操作作为字符串传递,例如 a = input() --> "ADDITION SUBTRACTION",然后 split(" ") 创建一个列表。遍历该列表并将 a 传递给方法控件。

import math

def control(a, x, y, z, k):
    if a == 'ADDITION':
        return addition(x, y)
    elif a == 'SUBTRACTION':
        return subtraction(x, y)
    elif a == 'MULTIPLICATION':
        return multiplication(x, y)
    elif a == 'DIVISION':
        return division(x, y),
    elif a == 'MOD':
        return  modulo(x, y)
    elif a == 'SECONDPOWER':
        return secondPower(x)
    elif a == 'POWER':
        return power(x, y)
    elif a == 'SECONDRADIX':
        return secondRadix(x)
    elif a == 'MAGIC':
        return magic(x, y, z, k)

【讨论】:

  • 我同意最简单的方法可能是最好的。在我的回答中,partial 被每个可能的函数调用。这里不止一次进行比较,但它仍然胜过多个函数调用。
  • 你仍然可以在没有开销和if-elif-elif-... 块的情况下进行调度,我在风格上真的很挣扎。
  • 我可以,是的,它不漂亮,但是如果 OP 想要现在可以工作的东西,那就是,我按照让它工作,然后在以后打破它的过程哈哈。
  • @StevenRumbalski 更好吗?
  • 很好的反馈。当你刚开始时,这绝对更具可读性,并且很有用。如果您正在研究原型或问题集。但是,如果您希望它成为企业或真正解决方案的一部分,那么必须有一个最佳解决方案,
【解决方案3】:

当您将所有参数传递给control 时,每个单独的函数都会被评估,因为您附加了括号。您最终会尝试计算math.sqrt(float(-6)),这是一个虚数。为了避免这种情况,您必须使用通用的*args 来设计控件,例如

def control(a, *args):
    return {
        'ADDITION': addition,
        'SUBTRACTION': subtraction,
        'MULTIPLICATION': multiplication,
        'DIVISION': division,
        'MOD': modulo,
        'SECONDPOWER': secondPower,
        'POWER': power,
        'SECONDRADIX': secondRadix,
        'MAGIC': magic
    }[a](*args)

这允许您检索您感兴趣的 one 函数,然后使用您的参数调用它。

【讨论】:

  • 不幸的是,它比这要复杂一些,因为这会在被调用的函数上产生错误,因为你传递了太多的变量,例如addition(*args) -> addition(x,y,z,k) 是一个错误。
  • 确实如此,OP 必须只传递所需的参数,或者定义他需要多少个并将其余的扔掉,例如def secondRadix(x, *args)
  • 你也可以在 dispatch 字典中保存 args 的数量。
【解决方案4】:

我不是大 if-elif-elif-else 的忠实粉丝,但我理解避免每次都构建字典的必要性。
扩展 @C.B. 的答案 - 您仍然可以使用字典调度机制,但只需将函数和参数数量存储在字典中。
现在可以在函数之外创建一次,例如:

dispatch = {
    'ADDITION': (addition, 2),
    'SUBTRACTION': (subtraction, 2),
    'MULTIPLICATION': (multiplication, 2),
    'DIVISION': (division, 2),
    'MOD': (modulo, 2),
    'SECONDPOWER': (secondPower, 1),
    'POWER': (power, 2),
    'SECONDRADIX': (secondRadix, 1),
    'MAGIC': (magic, 4)
}

def control(a, *args):
    func, count = dispatch[a] 
    return func(*args[:count])

【讨论】:

    猜你喜欢
    • 2016-12-28
    • 2014-09-25
    • 2021-08-01
    • 2016-05-27
    • 2018-02-10
    • 2016-03-31
    • 1970-01-01
    • 2022-12-04
    相关资源
    最近更新 更多