Python编码风格困境

时间:2015-07-08 09:55:27

标签: python performance coding-style

有几个众所周知的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只能部分解决问题,因为在函数调用期间仍需要列出所有参数。此外,它还没有解决性能问题,因为参数仍然被复制。如评论中所提出的,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:
                    ...

1 个答案:

答案 0 :(得分:0)

  1. 有人在评论中已经提到过:如果您有两个或更多嵌套for循环,请尝试使用itertools.product()

  2. 如果无法完成上述操作,至少可以减少if语句导致的缩进级别。例如:

    for abc in ..
        if (condition):
            for xyz in ...
    

    可以更改为:

    for abc in ...
        if not (condition):
            continue
        for xyz in ...
    
  3. 如果有许多参数要传递给方法,请尝试使用*args(元组中的所有参数)或**kwargs(字典中的所有参数)。

  4. 编辑: 根据您的具体要求,我编辑了给定的代码。这可能不会产生与你的完全相同的结果,但会让你大致了解如何实现上述3点。

    def my_function(**kwargs):
        process_var_list = ['var1', 'var2', 'var3', 'var4', 'var5', 'var6',
                            'var7', 'var8', 'var9', 'var10']
        #Open all 3 files at one go. This will improve performance and reduce nested loops
        with open(kwargs['file_a'], 'r') as input1, open(kwargs['file_0'], 'r') as input2, open('output.txt') as output1:
            #following point 1
            for line1, line2, item in itertools.product(input1, input2, output1):
                if kwargs['number'] % 9 == 0:
                    #following point 2
                    if line1[0] not in ["#", "!"]:
                        kwargs['threats'].append(line1)
                        continue
                    elif line1.startswith('!'):
                        kwargs['wonders'].count += 1
                        continue
                    if line2[0] not in ['A', 'B']:
                        print('error')
                        continue
                    elif line2.startswith('B'):
                        print('warning')
                        continue
                    if item in kwargs['good']:
                        for sub in item:
                            if sub < 0:
                                var_args = {var: kwargs[var] for var in process_var_list}
                                #following point 3
                                result = process_vars(**var_args)
                                output1.write(result)
                            elif 1 < sub >= 0:
                                output1.write('hello')
                            else:
                                output1.write('byebye')
                    elif len(item) > 20:
                        item = item[: 20]
                    else:
                        output1.write(line2)
                elif kwargs['number'] % 3 == 0:
                    kwargs['exceptions'].append(line1)
                elif kwargs['number'] % 3 == 1:
                    with open(kwargs['file_1'], 'r') as input2:
                        pass
                elif kwargs['number'] % 3 == 2:
                    with open(kwargs['file_2'], 'r') as input2:
                        pass