全文共6226字,预计学习时长28分钟
图源:unsplash
各个学科领域和行业都在经历一场“Python热”。在观察生物医学领域中Python编程的运用情况之后,笔者意识到相当多的Python程序员,都有不同的编程使用经验,如Matlab、C语言、C++、Java、JavaScript和Swift,也有一些之前没有编程经验的人。
Python成了程序员的“外语”,他们可能没经过系统的Python编码培训,也可能并不知道Python开发的惯用方法。
虽然程序员依然可以通过不同的方式实现同样的功能,编写出优秀的代码,只要代码能够满足预期目的就OK。编写非惯用Python程序也没有问题。但就像我们不断练习英文的口音一样,也有一些人也想让自己的Python代码变得更地道。
本文中,我将分享自己在过去几年中积累的一些习惯用法,希望对提高你的Python编码水平有所帮助。
1.分割序列
常见的序列类型有列表、元组和字符串。通过分割另一个序列,可以创建一个新序列。以下功能用列表作为示例,不过它们也可以用于元组、字符串和字节等其他序列类型。
图源:unsplash
>>> a = [0, 2, 4, 6, 8,10, 12, 14, 16, 18, 20]
>>> # Using a range, [start, end)
>>> a[1:3]
[2, 4]
>>> # Using a range with a step
>>> a[1:9:2]
[2, 6, 10, 14]
>>> # Leave out the start = an implicit start of 0
>>> a[:5]
[0, 2, 4, 6, 8]
>>> # Leave out the stop = an implict end to the very last item
>>> a[9:]
[18, 20]
>>> # Entire list
>>> a[:]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
2.使用反向索引访问序列中的元素
如果想在序列的末尾访问一些元素,那么反向计数要容易得多。在Python序列中,最后一个元素的索引是-1,倒数第二个元素的索引是-2,以此类推。
>>> a = 'Hello World!'
>>> # instead of using a[len(a)-1]
>>> a[-1]
'!'
>>> # in combination with slicing
>>> a[-5:-1]
'orld'
3.多重赋值
在给几个变量赋值时,可以使用多重赋值。通过同样的习惯用法,可以交换同一列表中的两个变量或两个元素。这一特征与之后要介绍的元组解包密切相关。
>>> # instead of doing a =8; b = 5
>>> a, b = 8, 5
>>> print(f'a is {a}; b is {b}')
a is 8; b is 5
>>> # Swap two variables
>>> a, b = b, a
>>> print(f'a is {a}; b is {b}')
a is 5; b is 8
>>> # Swap the first and last elements in a list
>>> numbers = [1, 2, 3, 4, 5]
>>> numbers[0], numbers[-1] = numbers[-1], numbers[0]
>>> numbers
[5, 2, 3, 4, 1]
4.颠倒序列
有时需要颠倒序列。虽然可以用for循环语句来实现,但是还有一种更简单直接的方法。与上述情况类似,当某个功能可用于某个序列时,通常意味着字符串、元组和列表也都支持这个功能。
>>> a = (1, 2, 3, 4, 5)
>>> a[::-1]
(5, 4, 3, 2, 1)
>>> b = 'start'
>>> b[::-1]
'trats'
5.检查序列是否为空
只有序列不为空时,列表、元组等操作才行得通,因此需要在操作之前检查序列是否为空。
图源:unsplash
为此,可以用not关键字来否定序列(例如not[]),只要序列不为空,其值就为True。此外,还可以对另外两种常见的数据类型dict和set执行同样的操作。
>>> empty_list = [(), '',[], {}, set()]
>>> for item in empty_list:
... if not item:
... print(f'Do something with the{type(item)}')
...
Do something with the
Do something with the
Do something with the
Do something with the
Do something with the
6.集合推导式
集合推导式的用法与上述列表解析式的用法类似。不同之处在于集合推导式用的是花括号而不是方括号。并且,通过定义set 数据类型,可以删除重复的元素。
7.字典生成式
除了列表解析式和集合推导式外,解析式特征还可用于字典数据类型的创建。dict由键值对组成,因此字典生成式包含指定键和值,二者之间用冒号隔开。
>>> a = [1, 2, 3, 4, 5]
>>> {x: x*x for x in a}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
8.生成器表达式
Python中的生成器是创建迭代器的一种简便方法。因为生成器是“惰性的”(也就是说,只有当发出请求时才能生成需要的项)。生成器非常节省内存。
创建生成器的一种特殊方法称为生成器表达式。除了用圆括号而非方括号这一点外,生成器表达式在语法上与列表解析式类似。
图源:unsplash
下面的例子中,当生成器直接用于迭代函数时,圆括号可有可无。
>>> sum(x**2 for x inrange(100))
328350
>>> max((x*x for x in range(100)))
9801
9.列表解析式
Python中一个有用的特征是列表解析式。通过列表解析式,可以很方便地构造一个列表。列表解析式的一般格式为[some_expression for element initerable if some_condition]。
>>> a = [1, 2, 3, 4, 5]
>>> [x*2 for x in a]
[2, 4, 6, 8, 10]
>>> [x*3 for x in a if x%2 == 1]
[3, 9, 15]
10.解包元组
元组是Python中十分常见的数据结构。它们是一组组相关的值。元组的常见用法包括访问自身元素。虽然可以使用索引访问这些元素,但是解包是一种更为简便的方法。
与解包的用法有关,可以用下划线来表示不需要的元素,用星号给已命名元素之外的其他元素赋值。
>>> items = (0, 'b','one', 10, 11, 'zero')
>>> a, b, c, d, e, f = items
>>> print(f)
zero
>>> a, *b, c = items
>>> print(b)
['b', 'one', 10, 11]
>>> *_, a, b = items
>>> print(a)
11
11.在for循环语句中使用Reversed()函数
reversed()函数通常用在for循环语句中,是一种以与原始可迭代对象相反的顺序创建迭代器的方法。
>>> tasks = ['laundry','picking up kids', 'gardening', 'cooking']
>>> for task in reversed(tasks):
... print(task)
...
cooking
gardening
picking up kids
laundry
12.Zip()压缩函数
zip()函数在一对一匹配连接多个迭代器方面十分有用。如果某些迭代器超过最短的迭代器,就会被截断。zip()函数返回一个迭代器,因此在迭代中经常使用到。还可以用zip()函数解压缩带星号的迭代器,并将未压缩的项赋值给变量。
>>> students = ('John','Mary', 'Mike')
>>> ages = (15, 17, 16)
>>> scores = (90, 88, 82, 17, 14)
>>> for student, age, score in zip(students, ages, scores):
... print(f'{student}, age: {age},score: {score}')
...
John, age: 15, score: 90
Mary, age: 17, score: 88
Mike, age: 16, score: 82
>>> zipped = zip(students, ages, scores)
>>> a, b, c = zip(*zipped)
>>> print(b)
(15, 17, 16)
13.用Lambdas排序
lambdas表达式为匿名函数,可以用单行表达式接收多个参数。lambdas的一个常见用法是将其设置为内置sorted()函数中的key参数。
图源:unsplash
除此之外,lambdas还经常用于max(),map()等函数中。在这些函数中,可以用单行表达式来替换使用def关键字的常规函数。
>>> students = [{'name':'John', 'score': 98}, {'name': 'Mike', 'score': 94}, {'name': 'Jennifer','score': 99}]
>>> sorted(students, key=lambda x: x['score'])
[{'name': 'Mike', 'score': 94}, {'name': 'John', 'score': 98}, {'name':'Jennifer', 'score': 99}]
14.速记条件赋值
该特征基本上是个语法糖。在根据特定条件为变量赋值时,可以用以下通用速记赋值:y = x if condition_met elseanother_x。
>>> some_condition = True
>>> # the expanded format
>>> if some_condition:
... x = 5
... else:
... x = 3
>>> print(f'x is {x}')
x is 5
>>> # the shorthand way
>>> x = 5 if some_condition else 3
>>> print(f'x is {x}')
x is 5
15.在for循环语句中使用Enumerate()枚举函数
用enumerate()函数获取可迭代对象来创建迭代器。此外,enumerate()函数还可以跟踪迭代的次数。可以随意设置计数初始值。默认的计数初始值为0。
>>> students = ('John','Mary', 'Mike')
>>> for i, student in enumerate(students):
... print(f'Iteration: {i}, Student:{student}')
...
Iteration: 0, Student: John
Iteration: 1, Student: Mary
Iteration: 2, Student: Mike
>>> for i, student in enumerate(students, 35001):
... print(f'Student Name: {student},Student ID #: {i}')
...
Student Name: John, Student ID #: 35001
Student Name: Mary, Student ID #: 35002
Student Name: Mike, Student ID #: 35003
16.用Get()方法检索字典中的值
通常情况下,可以在方括号中指定键来检索键的值。但是,当键不在字典中时,就可能出错。当然,也可以用try/except异常处理机制来解决这个问题。不过,当键不在字典中时,还可以通过get()方法设置默认值。
图源:unsplash
>>> number_dict = {0:'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> number_dict[5]
Traceback (most recent call last):
File "", line 1,in
KeyError: 5
>>> number_dict.get(5, 'five')
'five'
17.获取字典中最大值对应的键
有时需要在字典中找出最大值对应的键。首先,在所有值列表中找到最大值的索引,然后从另一个存储所有键的列表中找到对应的键。或者,也可以用一种更简单的方法,就是在max()函数中指定key参数。
简便起见,不考虑最大值可能重复的情况。同样地,还可以用min()函数查找最小值对应的键。
>>> model_scores ={'model_a': 100, 'model_z': 198, 'model_t': 150}
>>> # workaround
>>> keys, values = list(model_scores.keys()),list(model_scores.values())
>>> keys[values.index(max(values))]
'model_z'
>>> # one-line
>>> max(model_scores, key=model_scores.get)
'model_z'
18.用Print()函数进行调试
对于较小的项目,可以用print()函数进行调试。此函数也经常用在教学中。print()函数中,经常会用到一些技巧。第一个是结束字符串而不是默认的换行符;第二个是使用字符串f-string,创建包含一些表达式的字符串。
>>> for i in range(5):
... print(i, end=', ' if i < 4else '\n')
...
0, 1, 2, 3, 4
>>> for i in range(5):
... print(f'{i} & {i*i}', end=',' if i < 4 else '\n')
...
0 & 0, 1 & 1, 2 & 4, 3 & 9, 4 & 16
19.集合元素存在性测试
有时,在对集合或匹配项进行操作之前,需要测试集合中是否存在某个元素。惯用的方法是使用关键字in。
>>> a = ('one', 'two','three', 'four', 'five')
>>> if 'one' in a:
... print('The tuple contains one.')
...
The tuple contains one.
>>> b = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> if 2 in b.keys():
... print('The dict has the key of2.')
...
The dict has the key of 2.
20.海象运算符
海象运算符(:=)是Python 3.8版本中的新功能。它不过是赋值表达式(给表达式中的变量赋值)的另一个名称。
通常,当表达式使用变量时,变量声明须提前。使用海象运算符,变量赋值可以包含在表达式中,并且可以立即使用该变量。
>>> a = ['j', 'a', 'k','d', 'c']
>>> if (n := len(a))%2 == 1:
... print(f'The number of letters is{n}, which is odd.')
...
The number of letters is 5, which is odd.
21.分割字符串
v
在处理字符串时,通常要将字符串分隔成单词列表。这种情况下,可以使用split()函数的分隔符,并且可选最大分隔。相关的函数有rsplit()函数,和split()函数功能类似,只是在设置时从右侧开始分割,以满足最大分割要求。
>>> sentence = 'this is, apython, tutorial, about, idioms.'
>>> sentence.split(', ')
['this is', 'a python', 'tutorial', 'about', 'idioms.']
>>> sentence.split(', ', 2)
['this is', 'a python', 'tutorial, about, idioms.']
>>> sentence.rsplit(', ')
['this is', 'a python', 'tutorial', 'about', 'idioms.']
>>> sentence.rsplit(', ', 2)
['this is, a python, tutorial', 'about', 'idioms.']
22.Map()映射函数
map()函数是个高阶函数(即使用函数作为参数或返回一个值作为其输出的函数)。其通用格式为map(function, iterables)。map()函数将可迭代对象作为参数并返回一个map 对象,map 对象又是一个迭代器。可迭代对象的数量应与函数所需的参数数量匹配。
以下示例中,内置函数pow()需要两个参数。当然,也可以使用自定义函数。顺便说明一下,在使用map()函数创建列表时,应该可以使用列表解析式达到相同的效果。
>>> numbers = (1, 2, 4, 6)
>>> indices = (2, 1, 0.5, 2)
>>> # use map()
>>> list(map(pow, numbers, indices))
[1, 2, 2.0, 36]
>>> # list comprehensions
>>> [pow(x, y) for x, y in zip(numbers, indices)]
[1, 2, 2.0, 36]
23.Filter()过滤函数
filter()函数使用指定的函数或lambda函数过滤序列。该函数返回一个filter对象,filter对象是个迭代器。总的来说,该函数的用法与map()函数非常相似。
>>> def good_word(x: str):
... has_vowels = not set('aeiou').isdisjoint(x.lower())
... long_enough = len(x) > 7
... good_start =x.lower().startswith('pre')
... return has_vowels &long_enough & good_start
...
>>> words = ['Good', 'Presentation', 'preschool', 'prefix']
>>> list(filter(good_word, words))
['Presentation', 'preschool']
24.连接可迭代对象中的字符串
在处理字符串时,有时需要通过连接列表、元组等可迭代对象内的一系列字符串来创建单个字符串。这种情况下,可以用所需分隔符调用的join()函数。
>>> words = ('Hello','Python', 'Programmers')
>>> '!'.join(words)
'Hello!Python!Programmers'
>>> words_dict = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> '&'.join(words_dict.values())
'zero&one&two&three'
25.查找列表中最常见的元素
在用列表记录一些具有重复元素的内容时,例如跟踪一系列游戏的获胜者,这与查找哪位游戏玩家赢得的次数最多有关。可以通过 max()函数指定key参数,对集合中元素的计数来找出最大值。
图源:unsplash
>>> winnings = ['John','Billy', 'Billy', 'Sam', 'Billy', 'John']
>>> max(set(winnings), key = winnings.count)
'Billy'
26.检验对象类型
检验对象类型是Python内省功能的一部分。有时,在应用对应函数之前,需要知道对象是否为某种类型。于是,可以用type()函数或isinstance()函数,后者更为灵活,可以进行一对多的检验。
>>> def check_type(number):
... if type(number) == int:
... print('do something with anint')
... if isinstance(number, (int,float)):
... print('do something with anint or float')
...
>>> check_type(5)
do something with an int
do something with an int or float
>>> check_type(4.2)
do something with an int or float
27.Any() 函数
假设有一张记录列表,记录了John到达工作地点的时间。如果想知道他这周是否迟到过,那么用any()函数就十分方便。如果布尔型列表中有一个元素为True,则any()函数返回True。
>>> arrival_hours ={'Mon': 8.5, 'Tue': 8.75, 'Wed': 9, 'Thu': 8.5, 'Fri': 8.5}
>>> arrival_checks = [x>8.75 for x in arrival_hours.values()]
>>> any(arrival_checks)
True
28.跟踪列表中元素的频率
如果还想知道非冠军玩家在比赛中表现如何,根据上面的例子,就可以知道第二名和第三名玩家的情况。要想做到这一点,需要找出每位玩家的奖励。可以用字典生成式和带有lambda函数功能的sorted()函数。
>>> winnings = ['John','Billy', 'Billy', 'Sam', 'Billy', 'John']
>>> tracked = {item: winnings.count(item) for item in set(winnings)}
>>> sorted(tracked.items(), key=lambda x: x[1], reverse=True)
[('Billy', 3), ('John', 2), ('Sam', 1)]
29.集体判断函数All()
还是用上面的例子,如果还想知道他一周内到达工作地点的时间是否都在9:30之前,就可以使用all()函数。只有布尔型列表中所有的元素均为True时,all()函数才返回True。
>>> arrival_checks_all =[x<9.5 for x in arrival_hours.values()]
>>> all(arrival_checks)
True
30.用With关键字读取文件
处理文件时,需要打开文件,处理文件数据,然后关闭文件。如果在使用后没有关闭文件,过了一段时间后,就没法儿读取该文件。这种情况下,with 关键字非常有用。如下所示,使用后文件将自动关闭。
>>> withopen('a_file.txt') as file:
... pass
...
>>> file.closed
True
图源:unsplash
本文并不打算把Python编程中的习惯用法详尽地罗列出来,只是想给读者提供一些常见的习惯用法,日常编码中都用得到。你还有其他的Python地道用法吗?欢迎在评论区分享!
我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”
(添加小编微信:dxsxbb,加入读者圈,一起讨论最新鲜的人工智能科技哦~)