【发布时间】:2020-02-20 22:59:07
【问题描述】:
上下文
假设我有以下 Python 代码:
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
for _ in range(n_iters):
number = halve(number)
sum_all += number
return sum_all
ns = [1, 3, 12]
print(example_function(ns, 3))
example_function 这里只是简单地遍历ns 列表中的每个元素并将它们减半 3 次,同时累积结果。运行这个脚本的输出很简单:
2.0
因为 1/(2^3)*(1+3+12) = 2。
现在,假设(出于任何原因,可能是调试或日志记录),我想显示一些关于example_function 正在执行的中间步骤的信息。也许我会把这个函数改写成这样的:
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
print(number)
sum_all += number
print('sum_all:', sum_all)
return sum_all
现在,当使用与以前相同的参数调用时,会输出以下内容:
Processing number 1
0.5
0.25
0.125
sum_all: 0.125
Processing number 3
1.5
0.75
0.375
sum_all: 0.5
Processing number 12
6.0
3.0
1.5
sum_all: 2.0
这正是我想要的。然而,这有点违背函数应该只做一件事的原则,现在example_function 的代码稍微更长更复杂。对于这样一个简单的函数,这不是问题,但是在我的上下文中,我有相当复杂的函数相互调用,并且打印语句通常涉及比这里显示的更复杂的步骤,导致我的代码的复杂性大大增加(对于一个在我的函数中,与日志记录相关的代码行多于与其实际用途相关的代码行!)。
此外,如果我后来决定不再需要在我的函数中使用任何打印语句,我将不得不通过 example_function 并手动删除所有 print 语句以及与此功能相关的任何变量,这个过程既繁琐又容易出错。
如果我希望在函数执行期间始终有打印或不打印的可能性,情况会变得更糟,导致我要么声明两个极其相似的函数(一个带有 print 语句,一个没有),哪个维护或定义类似的东西很糟糕:
def example_function(numbers, n_iters, debug_mode=False):
sum_all = 0
for number in numbers:
if debug_mode:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
if debug_mode:
print(number)
sum_all += number
if debug_mode:
print('sum_all:', sum_all)
return sum_all
这会导致函数臃肿且(希望)不必要地复杂,即使在我们的 example_function 的简单情况下也是如此。
问题
有没有一种 Python 方法可以将打印功能与 example_function 的原始功能“分离”?
更一般地说,有没有一种 Python 方法可以将可选功能与函数的主要目的分离?
到目前为止我所做的尝试:
我目前找到的解决方案是使用回调进行解耦。例如,可以像这样重写example_function:
def example_function(numbers, n_iters, callback=None):
sum_all = 0
for number in numbers:
for i_iter in range(n_iters):
number = number/2
if callback is not None:
callback(locals())
sum_all += number
return sum_all
然后定义一个回调函数来执行我想要的任何打印功能:
def print_callback(locals):
print(locals['number'])
并像这样调用example_function:
ns = [1, 3, 12]
example_function(ns, 3, callback=print_callback)
然后输出:
0.5
0.25
0.125
1.5
0.75
0.375
6.0
3.0
1.5
2.0
这成功地将打印功能与example_function 的基本功能分离。然而,这种方法的主要问题是回调函数只能在example_function 的特定部分运行(在这种情况下,在将当前数字减半之后),并且所有的打印都必须在那里发生。这有时会迫使回调函数的设计变得相当复杂(并使某些行为无法实现)。
例如,如果想要实现与我在问题的前一部分中所做的完全相同的打印类型(显示正在处理的数字及其相应的减半),则结果回调将是:
def complicated_callback(locals):
i_iter = locals['i_iter']
number = locals['number']
if i_iter == 0:
print('Processing number', number*2)
print(number)
if i_iter == locals['n_iters']-1:
print('sum_all:', locals['sum_all']+number)
这导致与之前完全相同的输出:
Processing number 1.0
0.5
0.25
0.125
sum_all: 0.125
Processing number 3.0
1.5
0.75
0.375
sum_all: 0.5
Processing number 12.0
6.0
3.0
1.5
sum_all: 2.0
但是编写、阅读和调试都很痛苦。
【问题讨论】:
-
查看 python
logging模块 -
@Chris_Rands 是对的.. 使用日志记录模块.. 这样您就可以打开和关闭日志记录.. 使用以下链接。 stackoverflow.com/questions/2266646/…
-
我看不出
logging模块在这里有什么帮助。尽管我的问题在设置上下文时使用了print语句,但我实际上是在寻找一种解决方案,以解决如何将任何类型的可选功能与函数的主要目的分离。例如,也许我想要一个函数在运行时绘制事物。在那种情况下,我相信logging模块甚至不适用。 -
@Pythonic 是一个形容词,描述了 Python 的语法/样式/结构/用法,以维护 Python 的哲学。这不是句法或设计规则,而是需要负责任地维护以生成干净且可维护的 Python 代码库的方法。在您的情况下,只有几行跟踪或打印语句会增加可维护性的价值,然后拥有它;不要对自己太苛刻。考虑上述任何您认为理想的方法。
-
这个问题太笼统了。我们或许能够解决特定问题(正如使用
logging的建议所展示的那样),但不能解决如何分离任意代码。
标签: python