Switch/Case 用法,如何高效的使用if-elif-else功能?
Switch/Case usage, how to efficiently use if-elif-else functionality?
我正在尝试构建一个有效的方案来检查各种情况下的一个值,每个情况代表需要执行的不同功能。
具体来说,我有一个带有功能按钮的键盘矩阵,它们都应该提供不同的功能,但代码应该以高效的方式执行,所以在得出结论之前不要浪费太多时间检查每条语句。
这是我想做的事情的一个例子,但由于 lambda 的使用/lambda 的创建,它似乎仍然效率低下:
def data(value):
global combination
functions = {
'A' : print('Arm'),
'B' : pass,
'C' : lambda x: return False,
'D' : print('Disarm'),
'*' : lambda x: return x, #Asterisk=Enter
'#' : lambda x: x='', #Hashtag=InputCorrection
}(combination)
if type(value) is int:
##Collect numbers, irrelevant##
pass
if type(value) is str:
##Function Buttons##
return functions.get(value, 'Default = Ignore Input')
据我所知,此 if-elif-else 场景的最快方法是字典,但如果我错了请纠正我。
我正在寻找比 lambda x:
更有效的方法,因为我读到这些 lambda 函数是在每次调用 data(value)
函数时生成的,因此会消耗时间。
我需要一个省时的程序,因为它不应该减慢在键盘上检测 "Buttons being pressed" 的过程。
另请注意:不可能有不正确的值,因此不会失败。 失败会在提供的函数被调用之前被拾取,但一般来说在这种情况下它们不存在。
From my knowledge the fastest approach to this if-elif-else scenario is a dictionary
不要假设,基准。 timeit
模块是你的朋友。
I'm looking for a more efficient way than lambda x:, since I've read that those lambda functions are generated on each call of the data(value) function and thus consume time
没错,但在全局命名空间中查找名称也需要时间。
另请注意,问题不在于 lambda 本身,内部函数也会出现同样的问题 - 当我们讨论它时,如果您的 lambda 只是调用另一个函数(即您有 {"A": lambda x: funcA(x)}
),您甚至不需要 lambda,只需使用 {"A": funcA, "B": funcB, }
,您不仅可以节省 lambda 实例化时间,还可以(更重要的是)节省一次涉及所有堆栈操作的函数调用。
所以再一次,不要假设,基准。
实际上,在您担心基准测试之前,先检查是否真的是性能问题,然后分析找出瓶颈所在is/are。我们人类通常不擅长猜测这类事情,而且我不止一次看到开发人员在代码的错误部分上浪费了好几天 "optimizing" - 使其变得不可读和不可维护 - 而没有获得任何显着的性能改进,同时使用探查器,您会发现一些 "quick wins" 需要一天时间才能实现,对可读性没有影响,并且显着提高了性能(有时提高了一个数量级)。
所以回到你的用例——你主要有 3 种可能的实现,一个普通的 if/elif/,一个本地字典和一个全局字典。您可以对每个解决方案进行模拟实施并使用 timeit
对它们进行基准测试,然后您就会知道哪个是您的 python 版本最快的。如果您计划支持不同的 Python 版本,请确保对所有版本重复测试。
我已经更新了代码并提出了一个可行的解决方案,仅供将来参考。
这目前有效,稍后我会做一些基准测试,然后再回来查看它是否是性能最佳的方法。
更新,基准:
好的,我对代码进行了基准测试,使用的代码和输出如下。
我需要检查如何正确编码,以便我以可读的格式多次运行和比较时间,必须手动调整它。
但正如上面所建议的,在这种情况下,性能时间确实不是一个大问题。
via Functions, global variables
import timeit
import logging
global combination,functions
combination = ''
MATRIX = [ [1,2,3,'A'],
[4,5,6,'B'],
[7,8,9,'C'],
['*',0,'#','D'] ]
## KEYPAD FUNCTIONS ##
def A():
print ('Arm')
def B():
print ('Function B')
def C():
global combination
print ('Cancel')
combination = None
def D():
print ('Disarm')
def asterisk():
print ('Enter')
def hashtag():
print ('Input Correction')
global combination
combination = ''
## DICTIONARY FOR FUNCTION ACCESS ##
functions = {
'A' : A, #Do Function A
'B' : B, #Do Function B
'C' : C, #Do Function C
'D' : D, #Do Function D
'*' : asterisk, #Asterisk=Enter
'#' : hashtag #Hashtag=InputCorrection
}
## DATA EVALUATION FROM BUTTONS ##
def data(value):
global combination
if isinstance(value, int):
#Collect numbers#
combination += str(value)
return False
if isinstance(value, str):
#Function Buttons#
return functions[value]()
## MAIN ##
print('Starting...')
try:
print('Execution time [#Numbers#]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][0]', number=1, globals=globals()))
# print('Execution time [2]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][1]', number=1, globals=globals()))
# print('Execution time [3]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][2]', number=1, globals=globals()))
# print('Execution time [4]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][0]', number=1, globals=globals()))
# print('Execution time [5]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][1]', number=1, globals=globals()))
# print('Execution time [6]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][2]', number=1, globals=globals()))
# print('Execution time [7]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][0]', number=1, globals=globals()))
# print('Execution time [8]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][1]', number=1, globals=globals()))
# print('Execution time [9]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][2]', number=1, globals=globals()))
# print('Execution time [0]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][1]', number=1, globals=globals()))
print('Execution time [A]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][3]', number=1, globals=globals()))
print('Execution time [B]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][3]', number=1, globals=globals()))
print('Execution time [C]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][3]', number=1, globals=globals()))
print('Execution time [D]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][3]', number=1, globals=globals()))
print('Execution time [*]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][0]', number=1, globals=globals()))
print('Execution time [#]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][2]', number=1, globals=globals()))
except:
logging.exception('Exception:')
print('Done.')
difference with if-elif, still global variables
## DATA EVALUATION FROM BUTTONS ##
def data(value):
global combination
if isinstance(value, int):
combination += str(value)
if isinstance(value, str):
if value == 'A':
print ('Arm')
elif value == 'B':
print ('Function B')
if value == 'C':
print ('Cancel')
combination = None
elif value == 'D':
print ('Disarm')
if value == '*':
print ('Enter')
elif value == '#':
combination = ''
with functions, local variables
## DICTIONARY FOR FUNCTION ACCESS ##
functions = {
'A' : A, #Do Function A
'B' : B, #Do Function B
'C' : C, #Do Function C
'D' : D, #Do Function D
'*' : asterisk, #Asterisk=Enter
'#' : hashtag #Hashtag=InputCorrection
}
## DATA EVALUATION FROM BUTTONS ##
def data(value):
combination = ''
if isinstance(value, int):
#Collect numbers#
combination += str(value)
return False
if isinstance(value, str):
#Function Buttons#
return functions[value]()
Output, manually edited for readability
debug_timing_ver1 - with functions, global
Starting...
Execution time [#Numbers#]: 0.0000276039991149446
Arm
Execution time [A]: 0.0010460909998073475
Function B
Execution time [B]: 0.0007861440008127829
Cancel
Execution time [C]: 0.0007383310003206134
Disarm
Execution time [D]: 0.0005742169996665325
Enter
Execution time [*]: 0.0007173410012910608
Input Correction
Execution time [#]: 0.0006719249995512655
Done.
debug_timing_ver2 - if-elif
Starting...
Execution time [#Numbers#]: 0.000028021000616718084
Arm
Execution time [A]: 0.0007630699983565137
Function B
Execution time [B]: 0.000840361999507877
Cancel
Execution time [C]: 0.001447234999432112
Disarm
Execution time [D]: 0.0002588010011095321
Enter
Execution time [*]: 0.0008585909999965224
Execution time [#]: 0.000026667001293390058
Done.
debug_timing_ver3 - with functions, local
Starting...
Execution time [#Numbers#]: 0.00002343800042581279
Arm
Execution time [A]: 0.0012339030017756158
Function B
Execution time [B]: 0.0009442159989703214
Cancel
Execution time [C]: 0.00036010300027555786
Disarm
Execution time [D]: 0.0002615100002003601
Enter
Execution time [*]: 0.0007838519995857496
Input Correction
Execution time [#]: 0.0002430200001981575
Done.
我正在尝试构建一个有效的方案来检查各种情况下的一个值,每个情况代表需要执行的不同功能。 具体来说,我有一个带有功能按钮的键盘矩阵,它们都应该提供不同的功能,但代码应该以高效的方式执行,所以在得出结论之前不要浪费太多时间检查每条语句。
这是我想做的事情的一个例子,但由于 lambda 的使用/lambda 的创建,它似乎仍然效率低下:
def data(value):
global combination
functions = {
'A' : print('Arm'),
'B' : pass,
'C' : lambda x: return False,
'D' : print('Disarm'),
'*' : lambda x: return x, #Asterisk=Enter
'#' : lambda x: x='', #Hashtag=InputCorrection
}(combination)
if type(value) is int:
##Collect numbers, irrelevant##
pass
if type(value) is str:
##Function Buttons##
return functions.get(value, 'Default = Ignore Input')
据我所知,此 if-elif-else 场景的最快方法是字典,但如果我错了请纠正我。
我正在寻找比 lambda x:
更有效的方法,因为我读到这些 lambda 函数是在每次调用 data(value)
函数时生成的,因此会消耗时间。
我需要一个省时的程序,因为它不应该减慢在键盘上检测 "Buttons being pressed" 的过程。
另请注意:不可能有不正确的值,因此不会失败。 失败会在提供的函数被调用之前被拾取,但一般来说在这种情况下它们不存在。
From my knowledge the fastest approach to this if-elif-else scenario is a dictionary
不要假设,基准。 timeit
模块是你的朋友。
I'm looking for a more efficient way than lambda x:, since I've read that those lambda functions are generated on each call of the data(value) function and thus consume time
没错,但在全局命名空间中查找名称也需要时间。
另请注意,问题不在于 lambda 本身,内部函数也会出现同样的问题 - 当我们讨论它时,如果您的 lambda 只是调用另一个函数(即您有 {"A": lambda x: funcA(x)}
),您甚至不需要 lambda,只需使用 {"A": funcA, "B": funcB, }
,您不仅可以节省 lambda 实例化时间,还可以(更重要的是)节省一次涉及所有堆栈操作的函数调用。
所以再一次,不要假设,基准。
实际上,在您担心基准测试之前,先检查是否真的是性能问题,然后分析找出瓶颈所在is/are。我们人类通常不擅长猜测这类事情,而且我不止一次看到开发人员在代码的错误部分上浪费了好几天 "optimizing" - 使其变得不可读和不可维护 - 而没有获得任何显着的性能改进,同时使用探查器,您会发现一些 "quick wins" 需要一天时间才能实现,对可读性没有影响,并且显着提高了性能(有时提高了一个数量级)。
所以回到你的用例——你主要有 3 种可能的实现,一个普通的 if/elif/,一个本地字典和一个全局字典。您可以对每个解决方案进行模拟实施并使用 timeit
对它们进行基准测试,然后您就会知道哪个是您的 python 版本最快的。如果您计划支持不同的 Python 版本,请确保对所有版本重复测试。
我已经更新了代码并提出了一个可行的解决方案,仅供将来参考。 这目前有效,稍后我会做一些基准测试,然后再回来查看它是否是性能最佳的方法。
更新,基准: 好的,我对代码进行了基准测试,使用的代码和输出如下。 我需要检查如何正确编码,以便我以可读的格式多次运行和比较时间,必须手动调整它。 但正如上面所建议的,在这种情况下,性能时间确实不是一个大问题。
via Functions, global variables
import timeit
import logging
global combination,functions
combination = ''
MATRIX = [ [1,2,3,'A'],
[4,5,6,'B'],
[7,8,9,'C'],
['*',0,'#','D'] ]
## KEYPAD FUNCTIONS ##
def A():
print ('Arm')
def B():
print ('Function B')
def C():
global combination
print ('Cancel')
combination = None
def D():
print ('Disarm')
def asterisk():
print ('Enter')
def hashtag():
print ('Input Correction')
global combination
combination = ''
## DICTIONARY FOR FUNCTION ACCESS ##
functions = {
'A' : A, #Do Function A
'B' : B, #Do Function B
'C' : C, #Do Function C
'D' : D, #Do Function D
'*' : asterisk, #Asterisk=Enter
'#' : hashtag #Hashtag=InputCorrection
}
## DATA EVALUATION FROM BUTTONS ##
def data(value):
global combination
if isinstance(value, int):
#Collect numbers#
combination += str(value)
return False
if isinstance(value, str):
#Function Buttons#
return functions[value]()
## MAIN ##
print('Starting...')
try:
print('Execution time [#Numbers#]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][0]', number=1, globals=globals()))
# print('Execution time [2]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][1]', number=1, globals=globals()))
# print('Execution time [3]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][2]', number=1, globals=globals()))
# print('Execution time [4]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][0]', number=1, globals=globals()))
# print('Execution time [5]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][1]', number=1, globals=globals()))
# print('Execution time [6]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][2]', number=1, globals=globals()))
# print('Execution time [7]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][0]', number=1, globals=globals()))
# print('Execution time [8]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][1]', number=1, globals=globals()))
# print('Execution time [9]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][2]', number=1, globals=globals()))
# print('Execution time [0]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][1]', number=1, globals=globals()))
print('Execution time [A]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[0][3]', number=1, globals=globals()))
print('Execution time [B]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[1][3]', number=1, globals=globals()))
print('Execution time [C]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[2][3]', number=1, globals=globals()))
print('Execution time [D]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][3]', number=1, globals=globals()))
print('Execution time [*]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][0]', number=1, globals=globals()))
print('Execution time [#]:',timeit.timeit(stmt='data(checker)', setup='checker=MATRIX[3][2]', number=1, globals=globals()))
except:
logging.exception('Exception:')
print('Done.')
difference with if-elif, still global variables
## DATA EVALUATION FROM BUTTONS ##
def data(value):
global combination
if isinstance(value, int):
combination += str(value)
if isinstance(value, str):
if value == 'A':
print ('Arm')
elif value == 'B':
print ('Function B')
if value == 'C':
print ('Cancel')
combination = None
elif value == 'D':
print ('Disarm')
if value == '*':
print ('Enter')
elif value == '#':
combination = ''
with functions, local variables
## DICTIONARY FOR FUNCTION ACCESS ##
functions = {
'A' : A, #Do Function A
'B' : B, #Do Function B
'C' : C, #Do Function C
'D' : D, #Do Function D
'*' : asterisk, #Asterisk=Enter
'#' : hashtag #Hashtag=InputCorrection
}
## DATA EVALUATION FROM BUTTONS ##
def data(value):
combination = ''
if isinstance(value, int):
#Collect numbers#
combination += str(value)
return False
if isinstance(value, str):
#Function Buttons#
return functions[value]()
Output, manually edited for readability
debug_timing_ver1 - with functions, global
Starting...
Execution time [#Numbers#]: 0.0000276039991149446
Arm
Execution time [A]: 0.0010460909998073475
Function B
Execution time [B]: 0.0007861440008127829
Cancel
Execution time [C]: 0.0007383310003206134
Disarm
Execution time [D]: 0.0005742169996665325
Enter
Execution time [*]: 0.0007173410012910608
Input Correction
Execution time [#]: 0.0006719249995512655
Done.
debug_timing_ver2 - if-elif
Starting...
Execution time [#Numbers#]: 0.000028021000616718084
Arm
Execution time [A]: 0.0007630699983565137
Function B
Execution time [B]: 0.000840361999507877
Cancel
Execution time [C]: 0.001447234999432112
Disarm
Execution time [D]: 0.0002588010011095321
Enter
Execution time [*]: 0.0008585909999965224
Execution time [#]: 0.000026667001293390058
Done.
debug_timing_ver3 - with functions, local
Starting...
Execution time [#Numbers#]: 0.00002343800042581279
Arm
Execution time [A]: 0.0012339030017756158
Function B
Execution time [B]: 0.0009442159989703214
Cancel
Execution time [C]: 0.00036010300027555786
Disarm
Execution time [D]: 0.0002615100002003601
Enter
Execution time [*]: 0.0007838519995857496
Input Correction
Execution time [#]: 0.0002430200001981575
Done.