Python Do And Dont(中文版)

󰃭 2017-08-03

不应该的用法

from module import *

这种方式在python中一般是合法的, 但我们不推荐这么做, 为什么呢

  1. 运行更慢

因为编译器不确定哪些变量是local的, 哪些是global的, 而且在早期的python(2.1之前)版本里, 这种用法还会报错

  1. 不确定地函数,变量, 类等引入导致覆盖本地的原有定义

比如, 我们可以如果想打开一个文件

f = open("www")
f.read()

一般情况下, 以上代码是可以正常运行的(假设www文件存在), 但如果在文件的头部添加from os import open

from os import *
f = open("www")
f.read()

这种情况下就有问题了, 因为os模块内也有个open函数, 而其返回的是一个整形, 并不是一个文件描述对象

这里可以看出内置的的open定义已经被os内的open 覆盖了

你永远无法确定一个模块内定义了什么名称的对象, 所以用你需要的from module1 import name1, name2 或者 import module1; print(module1.name1)

from module import name1, name2

这种用法虽然不推荐, 但没有像from module import *那么强烈

为什么不推荐这种用法呢, 简单来说: 命名空间混乱

foo.py :

a = 1

bar.py

from foo import a
print(a) # foo.a != a

foobar.py

from foo import a
a = 3
import bar

运行 foobar.py

foobar.py 中发生修改的是 foobar.a, 并不是foo.a,

因为 from foo import a 是将foo.py模块命名空间内的a 引入 foobar.py, a 就属于foobar.py模块命名空间了, 与原有的模块命名空间就没有关系了

那该如何修改呢

foo.py :

a = 1

bar.py

from foo import a
print(a) # foo.a == a

foobar.py

import foo
foo.a = 3
import bar

再运行foobar.py, 可以看到, foo.a 也变了(1==>3)

except:

Python 有个能抓取所有异常的方式except:, 但这是个很不好的习惯, 因为python中所有的错误都会以异常的方式跑出来, 如果使用except:的方式获取异常, 可能会掩盖真实的错误信息, 给debug造成不便

如下

try:
    foo = opne("file") # misspelled "open"
except:
    sys.exit("could not open file!")

上面的第二行的open函数拼写错误(写成了opne), 但我们得到的异常输出却是could not open file!

比较好的方式是下面的方式

try:
    foo = opne("file")
except IOError:
    sys.exit("could not open file")

open的拼写错误异常NameError会被抛出,并显示调用栈, 方便调试

一般会尽量避免使用excpet抓取异常, 但是在系统框架里可能需要抓取所有未被捕获的异常, 做一些回调处理

异常机制

python 中的发生错误时总会抛出异常, 可以通过抓取异常做响应的处理

以下是编程时的一个反例

def get_status(file):
    if not os.path.exists(file):
        print("file not found")
        sys.exit(1)
    return open(file).readline()

在第二行运行后到最后的return之间, 假如文件被删除了, 就可能抛出一个异常, 或者文件权限错误, 但文件还是存在的, 也会抛出一个异常以及相应的调用栈

这在开发环境还好, 上线后就在进程输出里就会看到比较难看的traceback调用栈

相对较好的方式如下

def get_status(file):
    try:
        return open(file).readline()
    except EnvironmentError as err:
        print("Unable to open file: {}".format(err))
        sys.exit(1)

但上面的方式仅适用于单次运行脚本, 如果是以服务形式长时间运行呢, 如下形式(基于第一个版本)

try:
    status = get_status(log)
except SystemExit:
    status = None

但还有一个更好的方式

def get_status(file):
    return open(file).readline()

我们应该认为在内部代码是可以正常运行, 尽量避免使用异常 然后在外部调用的地方由调用者进行抓取, 并进行处理

当然以上的代码最好的方式还是下面的方式

def get_status(file):
    with open(file) as fp:
        return fp.readline()

使用with语法可以保证在退出with块后, 打开的文件资源总能被关闭, 即便是中间出现了异常

标准库

我们应该尽量使用python的标准库, 而不是自己再造轮子

比如

# ugh!
return dir+"/"+file
# better
return os.path.join(dir, file)

第二种目录拼接的方式, 能够根据所在系统进行正确的拼接, 比如Windows系统用\ 分隔目录, 而Linux系统用/

换行

基于python使用换行符 表示一个语句的终结, 那么当我们的语句过长时怎么办呢

一般用\ 的方式

if foo.bar()['first'][0] == baz.quux(1, 2)[5:9] and \
   calculate_number(10, 20) != forbulate(500, 360):
      pass

但这种方式只要\ 后有空格就会报错, 而空格在编辑器中不容易被发下

最好的方式是

value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]
        + calculate_number(10, 20)*forbulate(500, 360))

\ 都省了, 这是利用了括号内的隐式连续性,