Python小技巧×100(1)

本文收录了10个Python的小技巧和有用的模块。学会这些技巧,让你的代码更Pythonic!希望对你有用^_^ 

1.用列表推导式代替for循环

# for 循环
lst = []
for a in range(1,101):
    lst.append(a%10)

# 列表推导式
lst = [a%10 for a in range(1,101)]

同样的,还有字典推导式、集合推导式、元组推导式: 

d = {k:k**2 for k in range(1,11)}
s = {x for x in range(1,11)}
t = (x for x in range(1,11))

第一行代码生成一个键是1到10、值是键的平方的字典;第二行代码生成一个集合,内容是1到10;第三行代码生成的是一个元组……哎等等,如果你真的去尝试过,你会发现,它生成的是一个迭代器generator,需要用tuple进行转化,这点千万牢记!

正确使用推导式,可以让代码变得简洁、高效。But,如果用的上瘾 ,那就不好了……

a = [x * y + z for x in range(1,51,3)
    for y in range(1,51,5) if y % 3 != 0
    for z in range(1,51,4) if z % 7 != 0]

2.海象运算符(Python3.8及以上)

这个运算符:=叫海象运算符。对,你没看错,这个运算符就是一个冒号和一个等号。横着来看,像不像一只海象呢?它可以算是一种特殊的赋值运算符。解释的不清晰,上例子吧!

# 原版
pwd = input('请输入密码:')
while pwd != 'abcde':
    print('密码错误,请重新输入!')
    pwd = input('请输入密码:')

# 海象运算符
while (pwd := input('请输入密码:')) != 'abcde':
    print('密码错误,请重新输入!')

 原版中,先请求输入一次密码。如果密码不等于'abcde',就重新输入,这一版要4行代码;使用海象运算符的加强版中,先将请求输入的密码赋值给pwd,然后判断,如果错掉了就执行代码并重新将输入赋值给pwd,十分简洁高效。

3.使用glob访问目录下文件(夹)

先看一段代码。看完后想想,你要是自己写这个函数要用多少代码……

import glob
pngs = glob.glob('*.png') # 筛选出所有PNG文件
A_pngs = glob.glob('A*.png') # 筛选出所有文件名以A开头的PNG文件
files = glob.glob('*.*') # 筛选出所有文件,过滤掉文件夹
two_char_files = glob.glob('??.*') # 筛选出所有除后缀名外只有两个字符的文件
num_files = glob.glob('[1-9].txt') # 筛选出文件名是1~9范围内的数字的TXT文件
letter_files = glob.glob('[a-z].txt') # 筛选出文件民是一个字母的TXT文件

详解:*的意思是任意长度字符,包括长度为0的字符;?表示一个字符;[0-9]表示1~9范围内的数字,[1-4]是1~4范围内的数字,以此类推,注意不能出现[8-3]倒序的或[3-12]出现了两位数字的;[a-z]表示一个字母,[d-k]表示d~k的任意字母,以此类推,同样不能倒序。(我咋感觉有点像正则表达式)其中,我最常用的是*,其它的出现概率略小,你可千万不要写错了哦~。其他符号网上都能搜到,不再赘述。

4.使用map

map的功能是将一个函数作用于一个序列的每一个对象,返回值是一个map对象,可以通过list等工厂函数转化成序列。一般情况下,可以使用列表推导式实现相同功能(至少我还没碰到过不能互相转换的)如下例:

l1 = [34,23,65,41,27,48]
a = list(map(lambda x:2*x/5,l1)
# 上面这行代码与下面功能一样
# [2*x for x in l1]

l2 = [37,23,56,32,42]
l3 = [73,42,34,54,13]
b = list(map(lambda x,y:(x+y)//2,l2,l3))
# 上面这行代码与下面功能一样
# [(x+y)//2 for x in l2 for y in l3]

通过此例,你也肯定发现了,map函数第一个参数是要使用的可调用对象(这里是lambda表达式),这个对象可以有一个参数,也可以有多个,后面的多个序列,就是要作用的序列,可调用对象的参数有n个,map就有(n+1)个参数(第一个是可调用对象)。

5.使用reduce

reduce作用是将一个两个参数的函数从左到右依次作用于一个序列,返回的结果作为下次计算的第一个参数。可能你没听懂,我说不清楚,还是上例子吧。

from functools import reduce
a = [1,2,3,4,5,6,7,8,9] # 可以用a = list(range(1,10))代替
s = reduce(lambda x,y:x+y,a)

首先,reduce函数是标准库functools的一个函数,需要导入。然后,定义序列a,内容为1~9。最后,调用reduce函数,第一个参数是要作用的可调用对象,第二个参数是序列。reduce作用过程:首先,我们要知道,这里的可调用对象是相加的lambda表达式。Python首先把序列的前两项1和2相加,得到3;再把3和3相加,得到6;6再加4,得到10;10再加5,得到15……以此类推,最后是36+9=45,于是,s里面就存储了45这个数字。如图所示,黄色是未处理的数据:

1

2

3

4

5

6

7

8

9

3

3

4

5

6

7

8

9

6

4

5

6

7

8

9

10

5

6

7

8

9

15

6

7

8

9

21

7

8

9

28

8

9

36

9

45

6.lambda表达式

在前面两项技巧中,我们都用到了lambda表达式。那么,什么是lambda表达式呢?你可以理解为,它是一个小型的函数,同样是可调用对象,同样可以可以有参数,可以有默认参数,必须有返回值(大部分的函数都是有返回值的,有些不返回,可以理解为返回值是None)。但是,它只能有一条语句,否则会报错。

除了上面讲的map、reduce函数外,tkinter的Button组件的command属性需要用print输出,就需要lambda表达式,否则,tkinter先对print进行求值,输出后返回值为None,于是点击按钮时什么都不做。很明显,这不是我们想要的效果。利用lambda表达式,将这个输出封为一个函数,即可正常调用。

lambda表达式标准语法:lambda [参1,参2,参3,...]:[返1,返2,返3,...]

可以设置默认参数,如lambda a="ABC",b="DEF":a+b

# 求曼哈顿距离
distance = lambda x1,y1,x2,y2:abs(x2-x1)+abs(y2-y1)
d1 = distance(0,0,15,15)# 求平面上点(0,0)和点(15,15)的曼哈顿距离
d2 = distance(1,2,3,4)# 求平面上点(1,2)和点(3,4)的曼哈顿距离

7.求多个最大值和最小值(最大的n个或最小的n个)

我们都知道,用max和min可以求出一个列表中的最大值和最小值。那么,如果要求列表中前3大/前3小的数字,那怎么办呢?傻一点的办法,不断求出max或min值并存储进一个列表里,然后将该值从原列表删除,继续此方法,直至新列表长度等于需要的长度。代码如下,想想看,如果要求多个最小值,应该怎么写代码呢?

def n_max(n,lst):
    maxLst = []
    for x in range(n):
        m = max(lst)
        maxLst.append(m)
        lst.remove(m)
    return maxLst
m = n_max(3,[14,23,55,22,46,62])

而有一种更快的办法,可以快速得到这个结果:

import heapq
m = heapq.nlargest(3,[14,23,55,22,46,62])

利用的是heapq提供的特殊数据结构“堆”和封装的函数nlargest,可以快速求解这个问题。当然,heapq也提供了求多个最小值的函数nsmallest,用法相同,不再赘述。

8.特殊数据结构——队列,栈

Python启动时,自带一些数据结构,如list,set,tuple,dict,iter。有时,这些数据结构无法满足我们的需求,比如队列、栈和上文提到的堆,那该怎么办呢?Python十分贴心,它的标准库将这些常见的数据结构封装了起来,供我们使用。这里详述队列和栈。

队列的特点是FIFO,LILO(先进先出,后进后出)。首先,我们尝试自己编写代码,模拟此结构:

class Queue:
    def __init__(self,lst=None):
        if lst is None:
            lst = []
        self.lst = lst
    def put(self,val):
        self.lst.append(val)
    def get(self):
        if self.lst == []:
            return None
        return self.lst.pop(0)

q = Queue([1,2,3,4])
q.put(5)
print(q.lst) # [1,2,3,4,5]
print(q.get()) # 1
print(q.get()) # 2
print(q.lst) # [3,4,5]

所幸,Python列表功能非常强大,所以要模拟也不难。但是,作为“合格懒人”,能省就尽量省代码。标准库有的代码,我们不必再去重现——又不是考试,问你如何模拟此结构。(*^▽^*)

import queue
q = queue.Queue(5) # 注意,参数是maxsize,即最大容量,不是初始化的列表。
q.put(1) # 往队列里“扔”个1
q.put(2) # 

以上是队列。下面讲栈:

栈的特点是LIFO,FILO(先进后出,后进先出),话不多说,这次我们不模拟了,直接上代码!

import queue
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
q.get() # 3
q.get() # 2
q.get() # 1
q.get() # 阻塞,原因同队列

通过对标准库queue的使用,简化了我们大量代码,是不是很爽呢?再次感受到Python的魅力了吧!

9.循环后的else语句

for-else和while-else都是可以存在的。当for语句里的break语句被执行(不正常退出),就会跳过else语句。而当for语句正常运行完成,则会运行else语句下级代码。很多情况下,break都是跟if搭配的,这里以此为例,你可以理解为,循环中if语句被执行n多次,最后有个else,如果if全部不通过,才轮到使用else语句,如果有一个if语句条件成立break掉了,那会跳过以下所有其他if语句和else语句。先上个代码,先细品一下。然后我再以我的方式讲解一下。

# 60
for a in range(100):
	if a % 50 == 10 and a != 10:
		print(a)
		break
else:
	print('NO')

# NO
for b in range(50):
    if b % 50 == 10 and b != 10:
        print(b)
        break
else:
    print('NO')
# for-else
for a in range(2,5):
    if 5 % a == 0:
        print(a)
        break
else:
    print('NO')


# 我的理解:以上代码可以替换为下面的if-elif-else:
if 5 % 2 != 0:
    print(2)
elif 5 % 3 != 0:
    print(5)
elif 5 % 4 != 0:
    print(4)
else:
    print('NO')

我是这么理解记忆的,各位看官若有其它记忆方式,可以无视我的方式哦。这里讲个for循环,while循环差不多用法,不再赘述。

10.合理使用itertools

itertools是个很有用的工具,使用它,可以省去很多时间!

itertools.cycle:可以将字符连成一个环。注意,如果你不小心导入了itertools,又不小心用了cycle,又不小心用了list,还不小心print了一下,那么,你就会惊喜地发现,电脑死机了(其实不print也会死机的),本人亲测有效。不要问我怎么办,我只有一个解决办法:强制关机,我也是这么解决的。好了好了,偏题太久了,还是上代码吧。

import itertools
c = itertools.chain('abc')
print(next(c)) # a
print(next(c)) # b
print(next(c)) # c
print(next(c)) # a
print(next(c)) # b
print(next(c)) # c
print(next(c)) # a
print(next(c)) # b
# 以此类推,c中存储的就是一个迭代器,内容是('a','b','c','a','b','c','a','b',...)

itertools.permutations:(全)排列。参数一是要(全)排列的序列。参数二为默认参数,意思是要从参数一里取几个进行排列,默认是全排列。

import itertools
print(list(itertools.permutations([1,2,3,4,5]))) # 全排列[1,2,3,4,5]
print(list(itertools.permutations([1,2,3,4,5],4))) # 从[1,2,3,4,5]中抽4个进行排列

itertools.repeat:每次返回参数一,最多参数二次,默认无限次。同样,不填参数二时,不要手贱去print(list(itertools.repeat(a))),否则会和cycle一样,死机!!!

import itertools
a = itertools.repeat([1,2,3,4],4)
print(next(a)) # [1,2,3,4]
print(next(a)) # [1,2,3,4]
print(next(a)) # [1,2,3,4]
print(next(a)) # [1,2,3,4]
print(next(a)) # 报错:StopIteration

下篇文章,将继续讲10个Python小技巧

====================未完待续====================

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值