【发布时间】:2015-09-26 03:49:08
【问题描述】:
有几个众所周知的 Python 代码样式规则,它们被认为是默认的,我会尽量遵守:
换行,使它们不超过 79 个字符。
保持缩进 4 个空格。
另一个常见的编程建议是
避免全局变量
换句话说,应该始终使用接受所有变量作为参数的函数,并避免直接从更高范围读取的类似 Pascal 的过程。
但是,在某些情况下,我们绝对应该打破其中的一些规则。例如,如果涉及具有长参数列表的函数。它们有两个不同的问题:
首先,在大量缩进的块中,剩下的空间太少了。
def function(variable1, variable2, variable3, variable4, variable5,\
variable6, variable7, variable8, variable9):
return variable1 + variable2 + variable3 + variable4 + variable5 +\
variable6 + variable7 + variable8 + variable9
def...
for variable1...
if variable 2...
while variable3...
if variable4...
for variable5...
...
variable10 =\
function(\
variable1,\
variable2,\
variable3,\
variable4,\
variable5,\
variable6,\
variable7,\
variable8,\
variable9)
...
这里的过程作为闭包,虽然被认为是一种不好的做法,但可能会有所帮助:
def...
def procedure():
variable10 = variable1 + variable2 + variable3 + variable4 +\
variable5 + variable6 + variable7 + variable8 + variable9
for variable1...
if variable 2...
while variable3...
if variable4...
for variable5...
...
procedure()
...
另一个问题(实际上是特定于 python 的)是性能。如果函数参数很多,复制函数参数可能会变得非常昂贵:
import time
var1 = 1
var2 = 2
var3 = 3
var4 = 4
var5 = 5
var6 = 6
def function(var1, var2, var3, var4, var5, var6):
pass
def procedure():
pass
starttime = time.time()
for i in range(10000000):
function(var1, var2, var3, var4, var5, var6)
finishtime = time.time()
print('Classical function runtime: {:.3f} s'.format(finishtime - starttime))
starttime = time.time()
for i in range(10000000):
procedure()
finishtime = time.time()
print('Procedure runtime: {:.3f} s'.format(finishtime - start time))
这个输出:
Classical function runtime: 2.447 s
Procedure runtime: 1.180 s
因此,针对有经验的开发人员,我的问题是:
有什么可以证明在经典函数上使用类似 Pascal 的过程是合理的,或者应该不惜一切代价避免它们,即使它们会导致代码变得更庞大和更慢?
编辑:
使用*args 和**kwargs 只能部分解决问题,因为在函数调用期间仍需要列出所有参数。它也没有解决性能问题,因为参数仍在被复制。在 cmets 中提出的itertools 也并不总是适用。在某些情况下,解决嵌套问题非常棘手(考虑下面的代码)并且会花费大量开发时间,可能会导致代码非常纠结。
def my_function(**kwargs):
with open(kwargs['file_a'], 'r') as input1:
for line1 in input1:
if kwargs['number'] % 3 == 0:
if kwargs['number'] % 9 == 0:
with open(kwargs['file_0'], 'r') as input2:
for line2 in input2:
if line1.startswith('#'):
if line2.startswith('A'):
with open('output.txt') as output1:
for item in kwargs['items']:
if item is in kwargs['good']:
for sub in item:
if sub < 0:
result = process_vars(
kwargs['var1'],
kwargs['var2'],
kwargs['var3'],
kwargs['var4'],
kwargs['var5'],
kwargs['var6'],
kwargs['var7'],
kwargs['var8'],
kwargs['var9'],
kwargs['var10'])
output1.write(result)
elif sub >= 0 and sub < 1:
output1.write('hello')
else:
output1.write('byebye')
elif len(item) > 20:
item = item[: 20]
else:
output1.write(line2)
elif line2.startswith('B'):
print('warning')
else:
print('error')
elif line1.startswith('!'):
kwargs['wonders'].count += 1
else:
kwargs['threats'].append(line1)
else:
kwargs['exceptions'].append(line1)
elif kwargs['number'] % 3 == 1:
with open(kwargs['file_1'], 'r') as input2:
...
elif kwargs['number'] % 3 == 2:
with open(kwargs['file_2'], 'r') as input2:
...
【问题讨论】:
-
您应该真的考虑降低此处的嵌套级别。例如,使用
itertools.product()删除所有嵌套的for循环。 -
在
(..)括号内你不需要反斜杠。 -
经验法则:如果因为嵌套太深而需要打破行长规则:嵌套太深,请找出一种更有效的方法来避免嵌套。
-
最后但同样重要的是,使用
timeit()模块来计时性能。而您的procedure()与function()的情况更可能是由于查找太多全局变量而不是在函数调用中使用参数造成的。 -
一般来说,你的代码需要更广泛的重构。例如,不要使用
(variable1, variable2, ..., variableN),而是使用(*variables),或者将相关参数分组到某种容器中。将此归咎于样式规则是一种红鲱鱼。
标签: python performance coding-style