Python中5个常见的错误

5个常见但难以发现的错误。

错误1. 没有使用if name == 'main':

结论

在脚本文件中,应该使用if __name__ == '__main__'如:

例子

例如,我们创建了一个utils.py,定义了一个简单的功能:

def print_hello():
    print("hello!")

print_hello()

执行python utils.py,程序会执行print_hello(),输出语句hello!

当我们想在 main.pyimport utils,然后调用print_hello():

import utils
utils.print_hello()

执行python main.py,会发现程序输出了2行hello!
这是因为在import utils时,执行了utils.py中的语句,执行了1次print_hello()main.pyutils.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,这会捕获所有异常,包括SystemExitKeyboardInterrupt,导致无法使用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.youtube.com/watch?v=fMRzuwlqfzs
2. https://mp.weixin.qq.com/s/qNIOISpCoTBT0vBH4Eq-og
3.《Effecitve Python 第二版》的Item 24: Use None and Docstrings to Specify Dynamic Default Arguments

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值