Python仙术 第8期 你应该避免的一些错误或不良习惯

手动释放资源

在Python中,我们经常要临时调用一些资源来实现某些目的,例如打开文件、连接数据库等。如果因为包括但不限于出错中断、忘了写等各种原因没有正确释放资源,那么资源可能会一直被占用,可能导致其他函数/进程无法使用,或导致资源泄露。

Python的上下文管理器是一种用于管理资源分配和释放的机制,它可以确保资源在使用后能够被正确地关闭或释放,即使运行过程中出现异常。

例子1:文件读写

错误的实现:手动打开和关闭

f = open('a.txt', "w")
f.write('Hello')
f.close()

正确的实现:使用with上下文管理器

with open('a.txt', "w") as f:
    f.write('Hello')

例子2:数据库连接

错误的实现:手动打开和关闭数据库连接和游标

import pymysql

# 假设config已经声明了连接配置
connection = pymysql.connect(**config)
cursor = connection.cursor()

cursor.execute("SELECT * FROM users")
result = cursor.fetchall()
cursor.close()
connection.close()

正确的实现:使用with上下文管理器

import pymysql

with pymysql.connect(**config) as connection:
    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM users")
        result = cursor.fetchall()

混用 == 和 is

==用于判断值是否相等,is判断两个对象是否是同一个对象。

在Python中,False == 0成立。如果我们需要严格判断某个对象是Bool False,那么应该使用is,例如

def foo() -> int|bool:
    pass

result = foo()
if result == False:
    print("运行失败")
else:
    print(f"运行结果为{result}")

假如foo()正确运行并返回了0,那么也会被错误地归类于运行失败的情况。

如果我们需要匹配所有 Bool False 等价的值,那,那 也 不 能 用 ==
因为a==False无法匹配空列表、空字符串、空元组、None等更多的 Bool False 等价的值。
你应该使用

not a

哦对了,我亲爱的上帝,这么写真的很蠢 :rage:

bool(a) is not True
len(a) is 0

如果非要总结一下的话,那就是,对于单例对象实例(如TrueNone等内置的永生对象)的判断,请直接使用is

函数默认实参采用可变对象

我相信,如果没有刻意了解或者被IDE敲打过,大部分初学者都会掉进这个坑里,而且还百思不得其解。
但是上期讲过啦,复习一下吧 :wink:

使用try…except捕获所有异常

部分人甚至不愿意使用except Exception as e然后日志记录
这样只会让你默默地无视掉这个错误,然后默默地排查到底哪出错,最后默默地emo。

更有甚者:

import time

while True:
    try:
        print('Hi')
        time.sleep(1)
    except:
        print('Bye')

恭喜你,ctrl+c杀不掉这个进程,因为KeyboardInterrupt被except代码块捕获并无视掉了。
当然你可以赌一下在except代码块的运行期间引发异常

正确的方式,只捕获有限的、已知的、可能的异常类型

try:
    foo()
exception ConnectionError:
    print("gg")
try:
    foo()
exception (ConnectionError, KeyboardInterrupt):
    print("Bye")

有生成器和迭代器我不用,诶就是玩

正确的行为

for i in range(1, 11):
    print(i)

错误的行为

for i in [1,2,3,4,5,6,7,8,9,10]:
    print(i)

外星人行为

for i in [j for j in range(1, 11)]:
    print(i)

—分割线—
正确的行为

for i in "Hello, world!":
    print(i)

cpp大手子行为

a = "Hello, world!"
for i in range(len(a)):
    print(a[i])

—分割线—
写cpp的外星人行为

a = [j for j in range(1, 11)]
for i in range(len(a)):
    print(a[i])

生成器和迭代器能够避免在初始化阶段构建整个列表,避免了无谓的内存和CPU消耗,尤其是面对大量数据时。

逻辑表达式混乱

为了你的身心健康,请使用德摩根定律化简逻辑表达式

if !((A and B) or C):
    print("OK")

对于上式,可以化简:

not (A and B) and C
(not A or not B) and C

这样清楚多了
当然建议在判断块中再做一次拆分:

if (not A) or (not B):
    if C:

另外,哪怕你再熟悉运算次序,也建议通过括号显式地指明

not '' == 0
(not '') == 0
not ('' == 0)
33 Likes

可惜我是java,我应该选python的 :joy:

4 Likes

不知不觉第八期了

1 Like

现在也来得及

2 Likes

现在我只想躺平,我甚至不想在下班后看到一行代码(不管是啥语言 :joy:),都是上班看一座座shi山惹的祸,看得我头皮发麻······

4 Likes

写了那么多 看也看不懂

是否能帮我 优化代码呢

4 Likes

py在摸鱼的时候搞点东西玩还是很有意思的

2 Likes

我写Python纯粹是为了,消遣娱乐?

2 Likes

你知道吗,最最最要命的是,居然要我不影响shi山情况下,在上面雕花 :face_vomiting: :face_vomiting: :face_vomiting:

4 Likes

这很好,兴趣是第一老师

5 Likes

原来此,我说怎么有些python程序跑起来,ctrl+c无效 :smiling_face_with_tear:

2 Likes

感谢,又学到了

2 Likes

你说的对
但我们学校面向 Furry 抓抓乐语言编程
我的课外班面向 C艹编程
所以我在看什么(

1 Like

你中考到底什么时候?

4 Likes

很好的代码,使我的Python旋转

6 Likes

要主打《又不是不能用》原则

2 Likes

我每次改这种耦合性太高的代码,手都会抖,我很怕shi山塌了,溅我一身。到时候人在家中坐,bug从天上来。一个方法5,6+处调用点,方法本身又长。。我真的无语

4 Likes

为什么你这个帖子没有编辑记录

2 Likes

更要《又不是不能用》了

3 Likes

你是说我的么,我也不知道呀

4 Likes