python

Python 新手应该避免哪些坑

文章暂存

systemime
2021-03-02
8 min

摘要.

对于 Python 新手来说,写代码很少考虑代码的效率和简洁性,因此容易造成代码冗长、执行慢,这些都是需要改进的地方。

本文是想通过几个案列给新手一点启发,怎样写 Python 代码更优雅。

# 新人躺坑之一:不喜欢使用高级数据结构

# sets(集合)

很多新手忽视 sets(集合)和 tuple(元组)的强大之处

例如,取两个列表交集:

def common\_elements(list1, list2):    
    common = \[\]        
    for item1 in list1:                
        if item1 in list2:                        
            common.append( item1 )        、
    return common

这样写会更好:

def common\_elements(list1, list2):
    common = set(list1).intersection(set(list2))
    return list(common)

# dic(字典)

新手枚举(访问和取出)字典的键和对应值,认为对应值必须通过键来访问,往往会这样做:

my\_dict = {'a':1,'b':2}
for key in my\_dict:        
    print(key, my\_dict\[key\])

有一个更优雅的方法可以实现:

my\_dict = {'a':1,'b':2}
    for key, value in my\_dict.items():    
    print(key, value)

对大部分项目来说,这样写会更加有效率。

# tuple(元组)

元组一旦创建就无法更改元素,看似没有什么用处,其实元组的作用大着呢!

很多函数方法都会返回元组,比如**_ enumerate( )** 和** dict.items( )_**,并且可以在函数中使用元组,返回多个值。

还能够很方便地从元组中提取信息:

a,b = ('cat','dog')

上面元组中有两个元素,分别被赋给 a,b。如果有多个值,同样可以提取:

a,b,c = ('cat','dog','tiger')
print(a,b,c)

提取首、尾两个元素:

first,\*\_,end = (1,2,3,4,5,6)
print(first,end)

输出:1、6

提取首、中、尾三部分:

first,\*middle,end = (1,2,3,4,5,6)
print(first,middle,end)

输出:1、[2, 3, 4, 5]、6

元组还可以用来交换变量:

 = 

上面 a 变成之前的 c,b 变成之前的 a,c 变成之前的 b

元组也能作为字典的键,所以如果你需要存储数据,可以使用带有元组键的字典,比如说经纬度数据。

# 新人躺坑之二:不喜欢使用上下文管理器

新手可能会习惯这样进行读取文件操作:

if os.path.exists(data\_file\_path):    
    data\_file = open(data\_file\_path,'r')
else:    
    raise OSERROR
print( data\_file.read())
data.close()

这样写会有几个明显的问题:

  • 可能出现文件存在,但文件被占用,无法读取的情况
  • 可能出现文件可以被读取,但操作文件对象出现报错的情况
  • 可能出现忘记关闭文件的情况

如果使用**_ with... 语句_**,问题就迎刃而解了:

with open(data\_file\_path,'r') as data\_file:
    print(data\_file.read)

这样可以捕获任何打开文件或处理数据时的异常情况,并且在任务处理完后自动关闭文件。

Python 初学者可能不太了解上下文管理器的神奇之处,它真的能带来巨大的便利。

# 新人躺坑之三:不喜欢使用标准库

标准库**_ itertools_** 和**_ collections_** 仍然很少被初学者使用

# itertools

如果你看到下面的任务:

list1 = range(1,10)
list2 = range(10,20)
for item1 in list1:
    for item2 in list1:
        print(item1\*item2)

这是一个嵌套循环操作,为提高代码效率,完全可以用**_ product()_** 函数替代嵌套循环:

from itertools import product
list1 = range(1,10)
list2 = range(10,20)
for item1,item2 in product(list1, list2):
    print(item1\*item2)

这两段代码的结果完全一样,但使用标准库函数明显更加简洁高效。

itertools 还有很多方便操作迭代对象的函数,比如:

  • count( ) 函数会创建一个无限迭代器
  • cycle( ) 函数会把传入的序列无限重复下去
  • chain( ) 可以把多个迭代对象串联起来
  • group( ) 函数可以把迭代其中相邻的重复元素挑出来,放在一起
  • ......

有兴趣可以详细看看**_ itertools_** 库的各种神奇函数

# collections

新手对 Python 集合模块了解的可能并不多,你可能会遇到这样的情形:

consolidated\_list = \[('a',1),('b',2),('c',3),('b',4)\]
items\_by\_id = {}
for id\_, item in consolidated\_list:
    if id\_ not in items\_by\_id: 
        items\_by\_id\[id\_\] = \[\]
    if id\_ in items\_by\_id:
        items\_by\_id\[id\_\].append(item)

上面代码构建了一个字典,依次向字典中添加信息,如果某个键已经存在,则以某种方式修改该键的值;如果某个键不存在,则添加对应键值对。

这种算法非常常见,你可以用**_ collects_** 模块的**_ defaultdict()_** 函数来实现同样效果:

from collections import defaultdict
 
items\_by\_id = defaultdict(list)
consolidated\_list = \[('a',1),('b',2),('c',3),('b',4)\]

for id\_, item in consolidated\_list:
    items\_by\_id\[id\_\].append(item)

在此列中,defaultdict() 接受一个 list 作为参数,当键不存在时,则返回一个空列表作为对应值。

有时候我们会遇到统计词频的案例,比如:

#统计词频
colors = \['red', 'blue', 'red', 'green', 'blue', 'blue'\]
result = {}
for color in colors:
    if result.get(color)==None:
        result\[color\]=1else:
        result\[color\]+=1
print (result)
# 输出 {'red': 2, 'blue': 3, 'green': 1}

完全可以用**_ defaultdict()_** 函数实现上面的计数功能:

colors = \['red', 'blue', 'red', 'green', 'blue', 'blue'\]
d = defaultdict(int)
for color in colors:
    d\[color\] += 1
print(d)

更简单的方法用**_ collections_** 模块的**_ Counter( )_** 函数:

from collections import Counter
colors = \['red', 'blue', 'red', 'green', 'blue', 'blue'\]
c = Counter(colors)
print (dict(c))

对于备份文件,新人往往会用**_ system_** 模块:

from  os import system
system("xcopy e:\\\\sample.csv  e:\\\\newfile\\\\")

其实shutil 模块更好用:

import shutil
shutil.copyfile('E:\\\\q.csv', 'e:\\\\movie\\\\q.csv')

因为shutil 会很详细地报告错误和异常。

# 新人躺坑之四:不喜欢使用异常处理

无论老手新手都应该在写代码的时候进行异常处理操作,这样可以使代码更加健壮。

异常处理一般会用try...except 语句,具体使用方法可见:

# 新人躺坑之五:不喜欢使用生成器

除非你的 list 十分复杂,并且频繁调用,否则都建议使用生成器,因为它非常节省内存,举个例子:

def powers\_of\_two(max\=20000):
    i = 0powers = \[\]while 2\*\*i < max:
        powers.append\[2\*\*i\]
        i += 1return powers

对于使用次数少、占据大量内存、且容易生成的数据,可以用生成器替代列表存储:

from itertools import count, takewhile
def powers\_of\_two(max\=20000):for index in takewhile(lambda i: 2\*\*i < max, count(start=0)):
        yield 2\*\*index

注:本文翻译自 Tony Flury 在 Quora 的回答,节选部分内容。

更多关于 python 文件读写和上下文管理器的使用,可以看下面的文章

如果文章对你有帮助,别忘记点赞、评论、Get!

—————————————————————

公众号:Python 大数据分析(ID:pydatas)

分享 python 编程、可视化设计、大数据分析、机器学习等技术以及数据分析案例,包括但不限于 pandas、numpy、spark、matplotlib、sklearn、tensorflow、keras、tableau 等 https://m.zhipin.com/mpa/html/get/column?contentId=962a67b9f4ca1211qxB509g~&identity=0&userId=95827921 https://m.zhipin.com/mpa/html/get/column?contentId=962a67b9f4ca1211qxB509g~&identity=0&userId=95827921

上次编辑于: 5/20/2021, 7:26:49 AM