5个常见但难以发现的错误。 错误1. 没有使用if name == 'main':结论在脚本文件中,应该使用if __name__ == '__main__' 。如: # utils.py def print_hello(): print('hello!')
if __name__ == '__main__': print_hello()
例子例如,我们创建了一个utils.py ,定义了一个简单的功能: def print_hello(): print('hello!')
print_hello()
执行python utils.py ,程序会执行print_hello(),输出语句hello! 。 当我们想在 main.py 中import utils ,然后调用print_hello(): import utils utils.print_hello()
执行python main.py ,会发现程序输出了2行hello! 。 这是因为在import utils 时,执行了utils.py中的语句,执行了1次print_hello() 。main.py 中utils.print_hello() 又调用了一次print_hello() 。 在main.py 中 import utils ,我们只是想引入其中的函数,而不执行其中的调用语句。只需要将utils.py 中的调用语句放在if __name__ == '__main__': 条件下: def print_hello(): print('hello!')
#可以在这里print(__name__) 验证__name__的值 if __name__ == '__main__': print_hello()
__name__ 是一个特殊变量,只有在执行该脚本时,__name__ 才为__main__ ;在被import 时,__name__ 为脚本名utils
错误2. bare except结论不应该使用裸except,这会捕获所有异常,包括SystemExit 和KeyboardInterrupt ,导致无法使用Ctrl+C 中断程序,并且会隐藏潜在的问题。 尽量捕获具体的异常。 例子import time
while True: try: print('hello!') time.sleep(0.1) except: print('... 捕获异常 ...')
执行该程序,会一直打印hello!,即使用Ctrl+C 也无法中断。 修改方法很简单,将裸except 改为except Exception ,这样就不会捕获KeyboardInterrupt ,从而可以用Ctrl+C 中断程序。 import time
while True: try: print('hello!') time.sleep(0.1) except Exception: print('... 捕获异常 ...')
错误3. 没有输出完整异常信息结论捕获异常时,应该用traceback 输出完整的异常信息,也就是异常溯源。只是简单的print异常,往往无法定位到真正出错的地方。 import traceback
try: raise Exception('一个异常') except Exception as e: print('简单的print e:') print(e)
# 使用traceback 打印异常溯源 print() print('使用trackback:') traceback.print_exc()
# 如果想输出异常信息到其它地方,可用traceback.format_exc()获得异常溯源的字符串 print() print('使用traceback.format_exc()获得异常溯源的字符串:') str = traceback.format_exc() # 可以将字符串输出到其它地方,如日志文件。 print(str)
错误4. 在应该用set/dict 的地方用了list结论在频繁查找某个元素是否在某个集合中时,应该用set/dict,而不该用list。 因为set/dict的查找时间是O(1),list的查找时间是O(N)。 例子import time
numbers = [i for i in range(0,100_000,2)] numbers_set = set(numbers)
def find_in_list(): ret = [] for i in range(10_000): ret.append(i in numbers) return ret
def find_in_set(): ret = [] for i in range(10_000): ret.append(i in numbers_set) return ret
print('find in list') s = time.time() find_in_list() print(f'用时:{time.time() - s}')
print('find in set') s = time.time() find_in_set() print(f'用时:{time.time() - s}')
运行结果: find in list 用时:2.1997861862182617 find in set 用时:0.000942230224609375
错误5. 给可变类型参数提供默认值结论默认参数值仅在模块加载时的函数定义期间计算一次。这可能会导致动态/可变值(如 {}、[] 或 datetime.now())出现奇怪的行为。使用 None 作为任何具有动态值的关键字参数的默认值。 def get_numbers_list(numbers, lst=None): if lst is None: lst = [] for x in numbers: lst.append(x) return lst
例子def get_numbers_list(numbers, lst=[]): for x in numbers: lst.append(x) return lst
numbers = [1,2,3,4] lst = get_numbers_list(numbers) print(lst)
numbers2 = [5,6,7,8] lst2 = get_numbers_list(numbers2) print(lst2)
输出: [1, 2, 3, 4] [1, 2, 3, 4, 5, 6, 7, 8]
应该改为 def get_numbers_list(numbers, lst=None): if lst is None: lst = [] for x in numbers: lst.append(x) return lst
numbers = [1,2,3,4] lst = get_numbers_list(numbers) print(lst)
numbers2 = [5,6,7,8] lst2 = get_numbers_list(numbers2) print(lst2)
参考: 1. https://www./watch?v=fMRzuwlqfzs 2. 《Effecitve Python 第二版》的Item 24: Use None and Docstrings to Specify Dynamic Default Arguments
|