1.4 回到Python:决策与控制
到目前为止,小程序有了一些有趣的特征:处理语言的能力和通过自动化节省人力的潜力。程序设计的一个关键特征是让机器能按照我们的意愿决策,在遇到特定条件时执行特定命令,或者对文本数据从头到尾不断循环直到条件满足。这一特征被称为控制,这是本节的重点。
条件
Python广泛支持多种运算符,如:<和> =,可以测试值之间的关系。全部的关系运算符见表1-3。
可以使用这些从新闻文本句子中选出不同的词。下面是一些例子——注意行与行之间只是运算符不同。它们都使用sent7,第一句话来自text7(华尔街日报)。像以前一样,如果得到错误结果,sent7没有定义,需要首先输入:from nltk.book import *。
>>> sent7
['Pierre', 'Vinken', ',', '61', 'years', 'old', ',', 'will', 'join', 'the',
'board', 'as', 'a', 'nonexecutive', 'director', 'Nov.', '29', '.']
>>> [w for w in sent7 if len(w) < 4]
[',', '61', 'old', ',', 'the', 'as', 'a', '29', '.']
>>> [w for w in sent7 if len(w) <= 4]
[',', '61', 'old', ',', 'will', 'join', 'the', 'as', 'a', 'Nov.', '29', '.']
>>> [w for w in sent7 if len(w) == 4]
['will', 'join', 'Nov.']
>>> [w for w in sent7 if len(w) != 4]
['Pierre', 'Vinken', ',', '61', 'years', 'old', ',', 'the', 'board',
'as', 'a', 'nonexecutive', 'director', '29', '.']
>>>```
所有这些例子都有一个共同的模式:[w for w in text if condition],其中condition是一个Python“测试”,得到真(true)或者假(false)。在前面的代码例子中,条件始终是数值比较。然而,也可以使用表1-4中列出的函数测试词汇的各种属性。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/a0df671871308ed84fe1a322d7f8d79628ccba88.png" width="" height="">
</div>
下面是一些从文本中选择词汇运算符的例子:以-ableness结尾的词,包含gnt的词,首字母大写的词,完全由数字组成的词。
sorted([w for w in set(text1) if w.endswith('ableness')])
['comfortableness', 'honourableness', 'immutableness', 'indispensableness', ...]
sorted([term for term in set(text4) if 'gnt' in term])
['Sovereignty', 'sovereignties', 'sovereignty']
sorted([item for item in set(text6) if item.istitle()])
['A', 'Aaaaaaaaah', 'Aaaaaaaah', 'Aaaaaah', 'Aaaah', 'Aaaaugh', 'Aaagh', ...]
sorted([item for item in set(sent7) if item.isdigit()])
['29', '61']
还可以创建更复杂的条件。如果c是一个条件,那么not c也是一个条件。如果有两个条件c1和c2,那么还可以使用合取和析取将它们合并形成一个新的条件:c1 and c2,c1 or c2。
提示 轮到你来:
运行下面的例子,尝试解释每一条指令中所发生的事情。然后,试着自己组合一些条件。
>> sorted([w for w in set(text7) if '-' in w and 'index' in w])
>> sorted([wd for wd in set(text3) if wd.istitle() and len(wd) > 10])
>> sorted([w for w in set(sent7) if not w.islower()])
>> sorted([t for t in set(text2) if 'cie' in t or 'cei' in t])```
对每个元素进行操作
在1.3节中,列举了计数词汇以外的其他项目的一些例子。让我们仔细看看之前所使用的符号。
[len(w) for w in text1]
[1, 4, 4, 2, 6, 8, 4, 1, 9, 1, 1, 8, 2, 1, 4, 11, 5, 2, 1, 7, 6, 1, 3, 4, 5, 2, ...]
[w.upper() for w in text1]
['[', 'MOBY', 'DICK', 'BY', 'HERMAN', 'MELVILLE', '1851', ']', 'ETYMOLOGY', '.', ...]
表达式形式为[f(w) for ...]或[w.f() for ...],其中f是一个函数,用来计算词长或将字母转换为大写。现阶段还不需要理解两种表示方法f(w)与w.f()之间的差异,而只需学习Python习惯用法(idiom),即对链表上的所有元素执行相同的操作。在前面的例子中,遍历text1中的每一个词,依次地赋值给变量w并在变量上执行指定的操作。
提示
上述的表示法被称为“链表推导”,这是第一个Python习惯用法的例子,是一种固定的表示法,我们习惯使用该方法,这样省去了每次分析的烦恼。掌握这些习惯用法是成为一流Python程序员的一个重要组成部分。
回到计数词汇的问题上,这里使用相同的习惯用法。
>>> len(text1)
260819
>>> len(set(text1))
19317
>>> len(set([word.lower() for word in text1]))
17231
>>>```
由于不重复计算像This和this这样仅仅大小写不同的词,就这样从词汇表计数中抹去了2000个!更进一步,还可以通过过滤掉所有非字母元素,从词汇表中消除数字和标点符号。
len(set([word.lower() for word in text1 if word.isalpha()]))
16948
这个例子稍微有些复杂:将所有纯字母组成的词小写。也许只计数小写的词会更简单一些,但这却是一个错误的答案(为什么?)。
如果你对链表推导不是很有信心,请不要担心,因为在下面的章节中你会学习到更多的例子及解释。
嵌套代码块
大多数编程语言允许我们在条件表达式或者说if语句条件满足时执行代码块。例如[w for w in sent7 if len(w) < 4]这样的条件测试的例子。在下面的程序中,我们创建了一个叫word的变量包含字符串值“cat”。在if语句中检查len(word)< 5是否为真。cat的长度确实小于5,所以if语句下的代码块被调用,print语句被执行,向用户显示一条消息。别忘了要缩进,在print语句前输入4个空格。
>>> word = 'cat'
>>> if len(word) < 5:
... print 'word length is less than 5'
... ①
word length is less than 5
>>>```
使用Python解释器时,必须添加一个额外的空白行①,这样它才能检测到嵌套块结束。
如果改变测试条件为len(word) >= 5,检查词的长度是否大于或等于5,那么测试将不再为真。此时,if语句后面的代码段将不会被执行,没有消息显示给用户。
if len(word) >= 5:
... print 'word length is greater than or equal to 5'
...
if语句被看作是控制结构,因为它控制缩进块中的代码是否运行。另一个控制结构是for循环。尝试下面的代码,请记住输入冒号和4个空格。
>>> for word in ['Call', 'me', 'Ishmael', '.']:
... print word
...
Call
me
Ishmael
.
>>>```
这叫做循环,因为Python以循环的方式执行里面的代码。它从word='Call'赋值开始,有效地使用变量word命名链表的第一个元素。然后,显示word的值给用户。接下来回到for语句,执行word = 'me'赋值,然后把这个新值显示给用户,以此类推。它以这种方式不断运行,直到链表中所有项都被处理完。
条件循环
现在,可以将if语句和for语句结合。循环链表中的每一项,只输出结尾字母是l的词。我们将为变量挑选另一个名字以表明Python并不在意变量名的意义。
sent1 = ['Call', 'me', 'Ishmael', '.']
for xyzzy in sent1:
... if xyzzy.endswith('l'):
... print xyzzy
...
Call
Ishmael
你会发现在if和for语句所在行末尾——缩进开始之前——有一个冒号。事实上,所有的Python控制结构都以冒号结尾。冒号表示当前语句与后面的缩进块有关联。
也可以指定当if语句的条件不满足时采取的行动。在这里,我们看到elif(else if)语句和else语句。请注意,这些语句在缩进代码前也有冒号。
>>> for token in sent1:
... if token.islower():
... print token, 'is a lowercase word'
... elif token.istitle():
... print token, 'is a titlecase word'
... else:
... print token, 'is punctuation'
...
Call is a titlecase word
me is a lowercase word
Ishmael is a titlecase word
. is punctuation
>>>```
正如你看到的,只具备少量的Python知识,就可以构建多行的Python程序。分块开发程序,在整合之前测试每一块代码是否达到你的预期是很重要的。这也是Python交互式解释器的价值所在,也是为什么你必须适应它。
最后,让我们把一直在探索的习惯用法组合起来。首先,创建一个包含cie或者cei词汇的链表,然后循环输出其中的每一项。请注意print语句结尾处的逗号,以便使结果在同一行输出。
tricky = sorted([w for w in set(text2) if 'cie' in w or 'cei' in w])
for word in tricky:
... print word,
ancient ceiling conceit conceited conceive conscience
conscientious conscientiously deceitful deceive ...