Python learning
編碼
# -*- coding: utf-8 -*-
計(jì)算
Python 支持的數(shù)字類型有:int、float、Decimal(十進(jìn)制)、Fraction(分?jǐn)?shù))、復(fù)數(shù)
>>> 8 / 5
1.6
>>> 17 // 5
3
>>> 5 * 2
10
>>> 5 ** 2
25
>>> 3.5 // 1.7
2.0
字符串
可以單雙引號(hào),特殊字符使用反斜杠轉(zhuǎn)義。
特殊的一些:
- 如果不想讓反斜杠當(dāng)做轉(zhuǎn)義輸出,可以用原始字符串,方法是在第一個(gè)引號(hào)前面加上一個(gè)
r,r"C:\some\name" - 字符串文本能夠分成多行。一種方法是使用三引號(hào):
"""..."""或者'''...'''。行尾換行符會(huì)被自動(dòng)包含到字符串中,但是可以在行尾加上\來避免這個(gè)行為(行尾加上則不換行) - 字符串可以由
+操作符連接(粘到一起),可以由*表示重復(fù),3 * 'un' + 'ium' == 'unununium' - 字符串也可以被截取,索引從0記起,如
word[2];索引也可以是負(fù)數(shù),這將導(dǎo)致從右邊開始計(jì)算,word[-1]表示最后一個(gè)字符 - 字符串還支持切片,如
word[2:5]表示獲取位置[2,5)的三個(gè)字符,如果word是Python,則獲取的字符串是tho,word[:5]表示從最開始到第4個(gè)位置的字符串,切片也支持負(fù)數(shù) - 索引過大會(huì)引發(fā)IndexError,但是切片過大不會(huì)報(bào)錯(cuò)
- Python字符串不可以被更改 — 它們是 不可變的 (整體賦值是可以的)。因此,賦值給字符串索引的位置會(huì)導(dǎo)致錯(cuò)誤。
word[1] = 'j'是錯(cuò)誤的!而word = 'aaa'可以
相關(guān)鏈接:
- Text Sequence Type — str
- String Methods: 轉(zhuǎn)換和查找
- String Formatting
- String Formatting Operations
列表
同樣支持索引和切片、所有的切片操作都會(huì)返回一個(gè)包含請(qǐng)求的元素的新列表。這意味著下面的切片操作返回列表一個(gè)新的(淺)拷貝副本
>>> sq1 = [1, 2, 3]
>>> sq2 = sq1
>>> sq3 = sq1[:]
>>> sq1
[1, 2, 3]
>>> sq2
[1, 2, 3]
>>> sq3
[1, 2, 3]
>>> sq2[1] = 10
>>> sq1
[1, 10, 3]
>>> sq2
[1, 10, 3]
>>> sq3[1] = 100
>>> sq1
[1, 10, 3]
>>> sq3
[1, 100, 3]
所以,我們知道,要想復(fù)制一個(gè)列表,需要使用[:]來獲取淺拷貝副本,而不能直接賦值。從上面的例子,也可以知道,列表支持對(duì)每個(gè)元素單獨(dú)修改
所以,還可以這樣:
>>> sq1
[1, 10, 3]
>>> sq1 + [1, 2]
[1, 10, 3, 1, 2]
>>> sq1
[1, 10, 3]
>>> sq1.append(10)
>>> sq1
[1, 10, 3, 10]
>>> sq1.extend([7, 6, 5])
>>> sq1
[1, 10, 3, 10, 7, 6, 5]
>>> # insert value
... sq1[1:1] = [40, 50]
>>> sq1
[1, 40, 50, 10, 3, 10, 7, 6, 5]
>>> # replace value
... sq1[1:3] = [60, 70]
>>> sq1
[1, 60, 70, 10, 3, 10, 7, 6, 5]
>>> # remove value
... sq1[1:3] = []
>>> sq1
[1, 10, 3, 10, 7, 6, 5]
一個(gè)例子
>>> # fibonacci
... a, b = 0, 1
>>> while b < 10:
... print(b)
... a, b = b, a+b
...
1
1
2
3
5
8
這里要注意的是:
注意a, b = 0, 1 和 a, b = b, a+b,這是 多重賦值 ,變量在賦值前,右邊的表達(dá)式從左到右計(jì)算,然后再分別賦值給左邊的每個(gè)變量。所以,將第二個(gè)多重賦值改成b, a = a+b, b也沒有問題
條件判斷:0、空序列(長(zhǎng)度為0就算)為false,標(biāo)準(zhǔn)比較操作符與 C 相同: < , > , == , <=, >= 和 !=
print()函數(shù)可以用,隔開變量輸出,并自動(dòng)會(huì)在兩個(gè)變量之間加一個(gè)空格;print('aaa', end=','),end的默認(rèn)值是\n,改掉這個(gè)之后,print不換行,而是以,結(jié)尾
流程控制
if語句
if x < 0:
print(x)
elif x == 0:
print("hello")
else:
print("+")
for語句
words = ['cat', 'window', 'defenestrate']
for w in words:
print(w, len(w))
這里需要注意的是,Python 的 for 語句依據(jù)任意序列(鏈表或字符串)中的子項(xiàng),按它們?cè)谛蛄兄械捻樞騺磉M(jìn)行迭代。所以,在迭代過程中修改迭代序列不安全。如果你想要修改你迭代的序列(例如,復(fù)制選擇項(xiàng)),你可以迭代它的復(fù)本
for w in words[:]:
if len(w) > 6:
words.insert(0, w)
print(words)
# ['defenestrate', 'cat', 'window', 'defenestrate']
在循環(huán)(for, while)中同樣可以使用break 和 continue,作用和 C 中相同
循環(huán)中,支持else子句,作用是在循環(huán)正常結(jié)束(break不算)后執(zhí)行
range()函數(shù)
用于生成一個(gè)等差級(jí)數(shù)鏈表(注意:這不是列表List,是迭代器),以下是例子:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
range(5, 10)
5 through 9
range(0, 10, 3)
0, 3, 6, 9
range(-10, -100, -30)
-10, -40, -70
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb
>>> print(range(10))
range(0, 10)
>>> list(range(5))
[0, 1, 2, 3, 4]
定義函數(shù)
關(guān)鍵字def引入一個(gè)函數(shù)定義,在其后必須跟有函數(shù)名和包括形式參數(shù)的圓括號(hào)
函數(shù)體的第一行語句可以是可選的字符串文本,這個(gè)字符串是函數(shù)的文檔字符串,或者稱為 docstring。有些工具通過 docstrings 自動(dòng)生成在線的或可打印的文檔,或者讓用戶通過代碼交互瀏覽;在你的代碼中包含 docstrings 是一個(gè)好的實(shí)踐,讓它成為習(xí)慣吧
def func():
""" 這是一個(gè)docstring """
...
沒有return語句的函數(shù)會(huì)返回None
在函數(shù)中引用全局的變量,可以用global語句聲明,如:global a
函數(shù)參數(shù)
默認(rèn)參數(shù)值
def func(num, str="hello"):
if num in (1, 2, 3):
print(num)
重要警告:默認(rèn)值只被賦值一次。這使得當(dāng)默認(rèn)值是可變對(duì)象時(shí)會(huì)有所不同,比如列表、字典或者大多數(shù)類的實(shí)例。例如,下面的函數(shù)在后續(xù)調(diào)用過程中會(huì)累積(前面)傳給它的參數(shù)!!
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
會(huì)輸出:
[1]
[1, 2]
[1, 2, 3]
取而代之的方法:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
關(guān)鍵字參數(shù)
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
# 接受一個(gè)必選參數(shù) (voltage) 以及三個(gè)可選參數(shù) (state, action, 和 type)。可以用以下的任一方法調(diào)用
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
可變參數(shù)列表
一個(gè)最不常用的選擇是可以讓函數(shù)調(diào)用可變個(gè)數(shù)的參數(shù)。這些參數(shù)被包裝進(jìn)一個(gè)元組。在這些可變個(gè)數(shù)的參數(shù)之前,可以有零到多個(gè)普通的參數(shù)
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
通常,這些可變參數(shù)是參數(shù)列表中的最后一個(gè)(只針對(duì)于非關(guān)鍵字參數(shù)),因?yàn)樗鼈儗阉械氖S噍斎雲(yún)?shù)傳遞給函數(shù)。任何出現(xiàn)在*args后的參數(shù)是關(guān)鍵字參數(shù),這意味著,他們只能被用作關(guān)鍵字,而不是位置參數(shù)
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
可變關(guān)鍵字參數(shù)列表
相當(dāng)于接收一個(gè)字典,必須在可變參數(shù)列表(如果有)的后面
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys:
print(kw, ":", keywords[kw])
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
""" result:
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
"""
在傳遞可變參數(shù)的時(shí)候可能遇到一種情況是,當(dāng)要傳遞的參數(shù)已經(jīng)是一個(gè)列表,而函數(shù)需要的是可變長(zhǎng)參數(shù),可以使用*來拆開。同理,字典拆成關(guān)鍵字參數(shù)可以用**
>>> args = [3, 6]
>>> list(range(*args))
[3, 4, 5]
>>> list(range(3, 6))
[3, 4, 5]
Lambda形式
通過lambda關(guān)鍵字,可以創(chuàng)建短小的匿名函數(shù)。這里有一個(gè)函數(shù)返回它的兩個(gè)參數(shù)的和: lambda a, b: a+b
lambda 形式可以從外部作用域引用變量:
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
pass語句
pass 語句什么也不做。它用于那些語法上必須要有什么語句,但程序什么也不做的場(chǎng)合
class MyCls:
pass
def func():
pass
while True:
pass
各種數(shù)據(jù)結(jié)構(gòu)
列表方法
>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print(a.count(333), a.count(66.25), a.count('x'))
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
>>> a.pop()
1234.5
>>> a
[-1, 1, 66.25, 333, 333]
可以看到,列表自帶pop和append方法,所以,可以直接把列表當(dāng)做堆棧使用
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
使用隊(duì)列:如果把列表當(dāng)做隊(duì)列來使用,效率不高(頭部插入和彈出很慢)??梢允褂?code>collections.deque
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.popleft() # The first to arrive now leaves
'Eric'
>>> queue.popleft() # The second to arrive now leaves
'John'
>>> queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
列表推導(dǎo)式為從序列中創(chuàng)建列表提供了一個(gè)簡(jiǎn)單的方法
常規(guī)的方法如下,副作用是x變量在循環(huán)完畢后依然存在,而且長(zhǎng)...
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
使用列表推導(dǎo)式:
>>> squares = [x ** 2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
列表推導(dǎo)式由包含一個(gè)表達(dá)式的括號(hào)組成,表達(dá)式后面跟隨一個(gè)for子句,之后可以有零或多個(gè)for或if子句。結(jié)果是一個(gè)列表,由表達(dá)式依據(jù)其后面的for和if子句上下文計(jì)算而來的結(jié)果構(gòu)成
另一個(gè)例子:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
del語句
del語句可以刪除列表的對(duì)應(yīng)的索引項(xiàng) 或 變量,與pop()方法的區(qū)別在于不返回值,并且可以刪除切片和整個(gè)變量
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
>>> del a
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
元組和序列
我們知道列表和字符串有很多通用的屬性,例如索引和切割操作。它們是序列類型中的兩種
元組也是一種標(biāo)準(zhǔn)序列類型,一個(gè)元組由數(shù)個(gè)逗號(hào)分隔的值組成:
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
... t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
上面的例子說明,元組在輸出的時(shí)候總是有括號(hào)的,但是輸入的時(shí)候可以不用。元組元素就像字符串元素,不可變。但是,如果元組中包含可變?cè)?,里面的元素是可變?如:列表)
特殊的情況,創(chuàng)建零個(gè)或一個(gè)元素的元組。零個(gè)元素直接用(),而一個(gè)元素的時(shí)候,需要額外加一個(gè)逗號(hào),丑但有效1, 或 (1,)
>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
集合
集合(set)是一個(gè)無序不重復(fù)元素的集,基本功能包括關(guān)系測(cè)試和消除重復(fù)元素。集合對(duì)象還支持 union(聯(lián)合),intersection(交),difference(差)和 sysmmetric difference(對(duì)稱差集)等數(shù)學(xué)運(yùn)算
大括號(hào){...}或set()函數(shù)可以用來創(chuàng)建集合。注意:想要?jiǎng)?chuàng)建空集合,你必須使用set()而不是{}。后者用于創(chuàng)建空字典
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # fast membership testing
True
>>> 'crabgrass' in basket
False
>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b # letters in either a or b
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # letters in both a and b
{'a', 'c'}
>>> a ^ b # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}
以上例子證明,集合支持自動(dòng)的去重(但亂序)和數(shù)學(xué)運(yùn)算
集合也支持推導(dǎo)式(并去重)
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
字典
字典在其他語言中,可能被稱為“關(guān)聯(lián)數(shù)組”(associative arrays)。字典以關(guān)鍵字為索引,關(guān)鍵字可以是任意不可變類型,通常用字符串或數(shù)值。如果元組中只包含字符串和數(shù)字,它可以做為關(guān)鍵字,如果它直接或間接的包含了可變對(duì)象,就不能當(dāng)做關(guān)鍵字。不能用列表做關(guān)鍵字,因?yàn)榱斜砜梢杂盟饕?、切割或?code>append()和extend()等方法改變
字典的主要操作是依據(jù)鍵來存儲(chǔ)和析取值。也可以用del來刪除鍵:值對(duì)(key:value)。如果你用一個(gè)已經(jīng)存在的關(guān)鍵字存儲(chǔ)值,以前為該關(guān)鍵字分配的值就會(huì)被遺忘。試圖從一個(gè)不存在的鍵中取值會(huì)導(dǎo)致錯(cuò)誤
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> list(tel.keys())
['irv', 'guido', 'jack']
>>> sorted(tel.keys())
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
dict()構(gòu)造函數(shù)可以直接從key-value對(duì)中創(chuàng)建字典。此外,字典推導(dǎo)式可以從任意的鍵值表達(dá)式中創(chuàng)建字典
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
循環(huán)技巧
循環(huán)字典,使用item()方法:
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
循環(huán)序列,使用enumerate():
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
同時(shí)循環(huán)兩個(gè)或更多序列,使用zip()整體打包:
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
""" result:
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
"""
逆向循環(huán),使用reversed():
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
排序去重后輸出,使用sorted():
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear
若要在循環(huán)內(nèi)部修改正在遍歷的序列(例如復(fù)制某些元素),建議您首先制作副本(!)
>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words[:]: # Loop over a slice copy of the entire list.
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
條件判斷
- 比較操作符
in和not in審核值是否在一個(gè)區(qū)間之內(nèi),1 not in [2, 3] - 比較操作符
is和is not比較兩個(gè)對(duì)象是否相同,[] is () - 比較操作可以傳遞,
1 <= 3 > 2,比較操作符具有相同的優(yōu)先級(jí) - 短路運(yùn)算符
and和or,參數(shù)從左向右解析,一旦結(jié)果可以確定即停止,短路操作符的返回值通常是最后一個(gè)變量0 and 1 -
not與and和or同屬于邏輯操作符,優(yōu)先級(jí)not>and>or - 總優(yōu)先級(jí):數(shù)值操作 > 比較操作 > 邏輯操作符
- 與
C不同,Python在表達(dá)式內(nèi)不能賦值
序列對(duì)象可以與相同類型的其他對(duì)象作比較,比較操作按字典序進(jìn)行:首先比較前兩個(gè)元素,如果不同,就決定了比較的結(jié)果;如果相同,就比較后兩個(gè)元素,依此類推,直到所有序列都完成比較。如果兩個(gè)元素本身就是同樣類 型的序列,就遞歸字典序比較。如果兩個(gè)序列的所有子項(xiàng)都相等,就認(rèn)為序列相等。如果一個(gè)序列是另一個(gè)序列的初始子序列,較短的一個(gè)序列就小于另一個(gè)。字符串的字典序按照單字符的ASCII順序
模塊
模塊是包括Python定義和聲明的文件。文件名就是模塊名加上.py后綴。模塊的模塊名(做為一個(gè)字符串)可以由全局變量__name__得到
# 先在桌面新建一個(gè)test.py,定義一個(gè)hello()方法
>>> os.getcwd()
'/home/my/Desktop'
>>> import test
>>> test.hello()
hello world
>>> test.__name__
'test'
除了包含函數(shù)定義外,模塊也可以包含可執(zhí)行語句。這些語句一般用來初始化模塊。他們僅在 第一次 被導(dǎo)入的地方執(zhí)行一次
# 在test.py中加入一個(gè)輸出語句
>>> import test
import mod test successfully!
每個(gè)模塊都有自己私有的符號(hào)表,被模塊內(nèi)所有的函數(shù)定義作為全局符號(hào)表使用。因此,模塊的作者可以在模塊內(nèi)部使用全局變量,而無需擔(dān)心它與某個(gè)用戶的全局變量意外沖突
模塊可以導(dǎo)入其他的模塊。一個(gè)(好的)習(xí)慣是將所有的import語句放在模塊的開始(或者是腳本),這并非強(qiáng)制。被導(dǎo)入的模塊名會(huì)放入當(dāng)前模塊的全局符號(hào)表中
import語句的一個(gè)變體直接從被導(dǎo)入的模塊中導(dǎo)入命名到本模塊的語義表中
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
作為腳本來執(zhí)行模塊
只需要添加一個(gè)if判定:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
然后用python fibo.py 50即可傳入?yún)?shù)并執(zhí)行
模塊的搜索路徑
導(dǎo)入一個(gè)叫spam的模塊時(shí),解釋器先在當(dāng)前目錄中搜索名為spam.py的文件。如果沒有找到的話,接著會(huì)到sys.path變量中給出的目錄列表中查找,變量的初始值如下:
- 輸入腳本的目錄(當(dāng)前目錄)
- 環(huán)境變量PYTHONPATH表示的目錄列表中搜索
- Python默認(rèn)安裝路徑中的搜索
>>> sys.path
['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']
需要注意的是由于這些目錄中包含有搜索路徑中運(yùn)行的腳本,所以這些腳本不應(yīng)該和標(biāo)準(zhǔn)模塊重名,否則在導(dǎo)入模塊時(shí)Python會(huì)嘗試把這些腳本當(dāng)作模塊來加載。這通常會(huì)引發(fā)錯(cuò)誤
dir()函數(shù)
內(nèi)置函數(shù)dir()用于按模塊名搜索模塊定義,它返回一個(gè)字符串類型的存儲(chǔ)列表
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
包
包通常是使用用“圓點(diǎn)模塊名”的結(jié)構(gòu)化模塊命名空間。例如,名為 A.B 的模塊表示了名為A的包中名為B的子模塊。可以避免全局變量之間的相互沖突
包的結(jié)構(gòu)可能是這樣的
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
當(dāng)導(dǎo)入這個(gè)包時(shí),Python通過sys.path搜索路徑查找包含這個(gè)包的子目錄
為了讓Python將目錄當(dāng)做內(nèi)容包,目錄中必須包含__init__.py文件。這是為了避免一個(gè)含有爛俗名字的目錄無意中隱藏了稍后在模塊搜索路徑中出現(xiàn)的有效模塊,比如string。最簡(jiǎn)單的情況下,只需要一個(gè)空的__init__.py文件即可。當(dāng)然它也可以執(zhí)行包的初始化代碼,或者定義稍后介紹的__all__變量
導(dǎo)入模塊的方法:
# 1
import sound.effects.echo
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
# 2
from sound.effects import echo
echo.echofilter(input, output, delay=0.7, atten=4)
# 3
from sound.effects.echo import echofilter
echofilter(input, output, delay=0.7, atten=4)
from sound.effects import *可能會(huì)很慢,或者出現(xiàn)一些意外(強(qiáng)烈不推薦的寫法)。但是這種情況難以避免,對(duì)于包的作者來說唯一的解決方案就是提供一個(gè)明確的包索引:執(zhí)行from package import *時(shí),如果包中的__init__.py代碼定義了一個(gè)名為__all__的列表,就會(huì)按照列表中給出的模塊名進(jìn)行導(dǎo)入(可以避免導(dǎo)入所有模塊)
輸入輸出
print()函數(shù)對(duì)于每個(gè)參數(shù),自動(dòng)用空格分開輸出,以下是比較優(yōu)雅的輸出方式(不是拼接的):
>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"
>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam
>>> print('This {food} is {adjective}.'.format(
... food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.
>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
other='Georg'))
The story of Bill, Manfred, and Georg.
>>> import math
>>> print('The value of PI is approximately {0:.3f}.'.format(math.pi))
The value of PI is approximately 3.142.
可以看到,{}用于指代格式化的內(nèi)容,里面可以規(guī)定順序、關(guān)鍵字等
字段名后允許可選的':'和格式指令。這允許對(duì)值的格式化加以更深入的控制
舊式的字符串格式化,使用%
>>> print("Name:%10s Age:%8d Height:%8.2f" % ("Aviad", 25, 1.83))
Name: Aviad Age: 25 Height: 1.83
文件讀寫
函數(shù)open()返回文件對(duì)象,這個(gè)函數(shù)通常需要兩個(gè)字符串參數(shù):文件名、打開模式
這里要特別注意的是,對(duì)于非文本文件,要在模式后面加上'b',否則會(huì)錯(cuò)誤當(dāng)做文本文件,修改一些平臺(tái)有關(guān)的行結(jié)束符字符(Python打開文本,會(huì)默認(rèn)會(huì)轉(zhuǎn)換成平臺(tái)相關(guān)的行結(jié)束符)
>>> f = open('openfile', 'w');
文件的對(duì)象方法:
f.read(size)方法用于讀取文件內(nèi)容,size是可選的數(shù)值,如果沒有指定size或者指定為負(fù)數(shù),就會(huì)讀取并返回整個(gè)文件。當(dāng)文件大小為當(dāng)前機(jī)器內(nèi)存兩倍時(shí),就會(huì)產(chǎn)生問題。反之,會(huì)盡可能按比較大的size讀取和返回?cái)?shù)據(jù)。如果到了文件末尾,f.read()會(huì)返回一個(gè)空字符串''
>>> f.read()
'This is the entire file.\n'
>>> f.read()
''
>>> f.read()
''
f.readline()從文件中讀取單獨(dú)一行,字符串結(jié)尾會(huì)自動(dòng)加上一個(gè)換行符\n,只有當(dāng)文件最后一行沒有以換行符結(jié)尾時(shí),這一操作才會(huì)被忽略。這樣返回值就不會(huì)有混淆,如果f.readline()返回一個(gè)空字符串,那就表示到達(dá)了文件末尾,如果是一個(gè)空行,就會(huì)描述為'\n',一個(gè)只包含換行符的字符串
>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''
高效的讀取方法
>>> for line in f:
... print(line, end='')
...
''' result:
This is the first line of the file.
Second line of the file
'''
將所有行讀到一個(gè)列表中
>>> f.seek(0)
>>> list(f)
['dfsafdsa\n', 'dfsafds\n', 'fdsagdfhfdh\n', 'hfgng\n', 'nh\n', 'trh\n', 'trh\n', 'tr\n', '\n', '\n', 'h\n', 'tr\n', 'htrhtr\n', 'h\n', 'tr\n', 'htr\n', 'h\n', 'tr\n', '\n', '\n', '\n', 'htrhtr\n']
>>> list(f)
[]
>>> f.seek(0)
>>> f.readlines()
['dfsafdsa\n', 'dfsafds\n', 'fdsagdfhfdh\n', 'hfgng\n', 'nh\n', 'trh\n', 'trh\n', 'tr\n', '\n', '\n', 'h\n', 'tr\n', 'htrhtr\n', 'h\n', 'tr\n', 'htr\n', 'h\n', 'tr\n', '\n', '\n', '\n', 'htrhtr\n']
f.write(string)方法將string的內(nèi)容寫入文件(是覆蓋還是追加,視打開文件的模式而定),Python3.5.2經(jīng)測(cè)試沒有返回值。如果想寫入非字符串的內(nèi)容,首先要轉(zhuǎn)換為字符串
>>> f.write('This is a test\n')
>>> value = ('the answer', 42)
>>> s = str(value)
>>> f.write(s)
文件對(duì)象的指針
f.tell()返回一個(gè)整數(shù),代表文件對(duì)象在文件中的指針位置,該數(shù)值計(jì)量了自文件開頭到指針處的比特?cái)?shù)。需要改變文件對(duì)象指針話話,使用f.seek(offset,from_what)。指針在該操作中從指定的引用位置移動(dòng)offset比特,引用位置由from_what參數(shù)指定。from_what值為0表示自文件起始處開始,1表示自當(dāng)前文件指針位置開始,2表示自文件末尾開始(配合的offset是負(fù)數(shù))。from_what可以忽略,其默認(rèn)值為0
>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'
當(dāng)你使用完一個(gè)文件時(shí),調(diào)用f.close()方法就可以關(guān)閉它并釋放其占用的所有系統(tǒng)資源。在調(diào)用f.close()方法后,試圖再次使用文件對(duì)象將會(huì)自動(dòng)失敗
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file
>>> f
<closed file 'test.txt', mode 'a+' at 0x7f8cd23ca5d0>
使用with處理文件對(duì)象的好習(xí)慣
使用關(guān)鍵字with的先進(jìn)之處在于文件用完后會(huì)自動(dòng)關(guān)閉,就算發(fā)生異常也沒關(guān)系。它是try-finally塊的簡(jiǎn)寫
>>> with open('test.txt', 'r') as f:
... read_data = f.read()
...
>>> read_data
'dfsafdsa\n'
>>> f.closed
True
以上的語句相當(dāng)于,先調(diào)用了open()函數(shù),并將返回值賦給f,然后里面的語句是try塊里面的內(nèi)容,finally的內(nèi)容隱藏在f對(duì)象的__exit__方法中,即f.close()。這里需要注意的是,這里出現(xiàn)異常的話,仍然需要自己定義try-except來處理。參考鏈接
使用json存儲(chǔ)格式化數(shù)據(jù)
需要使用
json標(biāo)準(zhǔn)模塊,import json
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]
>>> x
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> f = open("test.txt", "w+")
>>> f.read()
''
>>> f.tell()
0
>>> json.dump(x, f)
>>> f.seek(0)
>>> f.read()
'"[\\"foo\\", {\\"bar\\": [\\"baz\\", null, 1.0, 2]}]"'
>>> f.seek(0)
>>> y = json.load(f)
>>> y
u'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> z = json.loads(y)
>>> z[1]["bar"][2]
1.0
錯(cuò)誤和異常
語法錯(cuò)誤
語法錯(cuò)誤,也被稱作解析錯(cuò)誤(最常見)
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
異常
即使一條語句或表達(dá)式在語法上是正確的,當(dāng)試圖執(zhí)行它時(shí)也可能會(huì)引發(fā)錯(cuò)誤。運(yùn)行期檢測(cè)到的錯(cuò)誤稱為異常,并且程序不會(huì)無條件的崩潰:很快,你將學(xué)到如何在Python程序中處理它們。然而,大多數(shù)異常都不會(huì)被程序處理,像這里展示的一樣最終會(huì)產(chǎn)生一個(gè)錯(cuò)誤信息
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: int division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly
錯(cuò)誤信息的最后一行指出發(fā)生了什么錯(cuò)誤。異常也有不同的類型,異常類型做為錯(cuò)誤信息的一部分顯示出來
異常處理
try語句按如下方式工作:
- 首先,執(zhí)行
try子句 (在try和except關(guān)鍵字之間的部分) - 如果沒有異常發(fā)生,
except子句在try語句執(zhí)行完畢后就被忽略了 - 如果在
try子句執(zhí)行過程中發(fā)生了異常,那么該子句其余的部分就會(huì)被忽略。如果異常匹配于except關(guān)鍵字后面指定的異常類型,就執(zhí)行對(duì)應(yīng)的except子句。然后繼續(xù)執(zhí)行try語句之后的代碼 - 如果發(fā)生了一個(gè)異常,在
except子句中沒有與之匹配的分支,它就會(huì)傳遞到上一級(jí)try語句中。如果最終仍找不到對(duì)應(yīng)的處理語句,它就成為一個(gè)未處理異常,終止程序運(yùn)行,顯示提示信息
一個(gè)except子句可以在括號(hào)中列出多個(gè)異常的名字去匹配相應(yīng)的異常;如果省略括號(hào),則表示全部匹配
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
try-except語句可以帶有一個(gè)else子句,該子句只能出現(xiàn)在所有except子句之后。當(dāng)try語句沒有拋出異常時(shí),需要執(zhí)行一些代碼,可以使用這個(gè)子句
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
使用else子句比在try子句中附加代碼要好,因?yàn)檫@樣可以避免try-except意外的截獲本來不屬于它們保護(hù)的那些代碼拋出的異常
所以,except里面的代碼是錯(cuò)誤的時(shí)候執(zhí)行的;else里面的代碼是正確的時(shí)候執(zhí)行的;finally里面的代碼是無論如何都會(huì)執(zhí)行的(后面講到)
發(fā)生異常時(shí),可能會(huì)有一個(gè)附屬值,作為異常的參數(shù)存在。這個(gè)參數(shù)是否存在、是什么類型,依賴于異常的類型。通常可以為except子句指定(as)一個(gè)變量,可以直接print出來看
異常處理器不僅僅處理那些在 try 子句中立刻發(fā)生的異常,也會(huì)處理那些 try 子句中調(diào)用的函數(shù)內(nèi)部發(fā)生的異常
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as err:
... print('Handling run-time error:', err)
...
Handling run-time error: int division or modulo by zero
raise語句允許程序員強(qiáng)制拋出一個(gè)指定的異常(必須是一個(gè)異常實(shí)例或異常類)
如果你需要明確一個(gè)異常是否拋出,但不想處理它,raise語句可以讓你很簡(jiǎn)單的重新拋出該異常(一些監(jiān)控工具的實(shí)現(xiàn))
>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
''' result:
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
'''
用戶可以自定義異常,異常類通常應(yīng)該直接或間接從Exception類派生,下面是一個(gè)例子,更多例子看這里
>>> class MyError(Exception):
... def __init__(self, value):
... self.value = value
... def __str__(self):
... return repr(self.value)
...
>>> try:
... raise MyError(2*2)
... except MyError as e:
... print('My exception occurred, value:', e.value)
...
''' result:
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
'''
前面說到的finally語句,是無論什么情況都會(huì)執(zhí)行的功能,通常放的是清理行為,如果遇到異常,執(zhí)行完這段清理語句之后就結(jié)束了,下面是一個(gè)混合try-except-else-finally的代碼塊
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print("division by zero!")
... else:
... print("result is", result)
... finally:
... print("executing finally clause")
...
>>> divide(2, 1)
result is 2
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
在真實(shí)場(chǎng)景的應(yīng)用程序中,finally子句用于釋放外部資源(文件 或網(wǎng)絡(luò)連接之類的),無論它們的使用過程中是否出錯(cuò)
有些對(duì)象定義了標(biāo)準(zhǔn)的清理行為,無論對(duì)象操作是否成功,不再需要該對(duì)象的時(shí)候就會(huì)起作用。下面有兩段代碼:
# para 1
for line in open("myfile.txt"):
print(line)
# para 2
with open("myfile.txt") as f:
for line in f:
print(line)
區(qū)別在于,第一段代碼沒有關(guān)閉打開的文件,而with語句可以使文件之類的對(duì)象確??偰芗皶r(shí)準(zhǔn)確地進(jìn)行清理。在第二段語句執(zhí)行后,文件對(duì)象總會(huì)被關(guān)閉,即使是在處理文件中的數(shù)據(jù)時(shí)出錯(cuò)也一樣可以
類
關(guān)于作用域的示例(注意變量的查找順序),以下例子:
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam)
scope_test()
print("In global scope:", spam)
類定義語法
類定義就像函數(shù)定義,要先執(zhí)行才能生效
class ClassName:
<statement-1>
.
.
.
<statement-N>
Python的類對(duì)象支持屬性引用和實(shí)例化,如:
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
屬性引用: MyClass.i, MyClass.f, MyClass.__doc__
實(shí)例化: x = MyClass()
構(gòu)造函數(shù)的定義:
def __init__(self[, other params]):
pass
類方法的第一個(gè)參數(shù)一定是self,在調(diào)用的時(shí)候省略傳入,代指實(shí)例本身
Python的實(shí)例對(duì)象唯一可用的操作是屬性引用。和局部變量一樣,數(shù)據(jù)屬性不需要聲明,第一次使用時(shí)它們就會(huì)生成
在調(diào)用方法的時(shí)候,如:x.f()事實(shí)上是調(diào)用了MyClass.f(x),這就是要在定義類方法的時(shí)候加上參數(shù)self的原因
類和實(shí)例變量
一般來說,實(shí)例變量用于對(duì)每一個(gè)實(shí)例都是唯一的數(shù)據(jù),類變量用于類的所有實(shí)例共享的屬性和方法
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'
這樣會(huì)出現(xiàn),可變對(duì)象作共享數(shù)據(jù)的問題
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
正確的做法是,將這個(gè)列表作為一個(gè)實(shí)例變量,即放入構(gòu)造方法中賦值
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # creates a new empty list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
類的繼承
派生類的定義例子:
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
派生類定義的執(zhí)行過程和基類是一樣的。構(gòu)造派生類對(duì)象時(shí),就記住了基類。這在解析屬性引用的時(shí)候尤其有用:如果在類中找不到請(qǐng)求調(diào)用的屬性,就搜索基類
如果基類是由別的類派生而來,這個(gè)規(guī)則會(huì)遞歸的應(yīng)用上去。方法引用按如下規(guī)則解析:搜索對(duì)應(yīng)的類屬性,必要時(shí)沿基類鏈逐級(jí)搜索,如果找到了函數(shù)對(duì)象這個(gè)方法引用就是合法的
派生類可能會(huì)覆蓋其基類的方法。因?yàn)榉椒ㄕ{(diào)用同一個(gè)對(duì)象中的其它方法時(shí)沒有特權(quán),基類的方法調(diào)用同一個(gè)基類的方法時(shí),可能實(shí)際上最終調(diào)用了派生類中的覆蓋方法
派生類中調(diào)用基類方法,這時(shí)需要傳入self參數(shù)了,BaseClassName.methodname(self, arguments)
判斷類或者實(shí)例之間的關(guān)系
- 函數(shù)
isinstance()用于檢查實(shí)例類型:isinstance(obj, int)只有在obj.__class__是int或其它從int繼承的類型 - 函數(shù)
issubclass()用于檢查類繼承:issubclass(bool, int)為True,因?yàn)?code>bool是int的子類 - 然而,
issubclass(float, int)為False,因?yàn)?code>float不是int的子類
Python同樣有限的支持多繼承形式,例子如下:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
搜索順序:如果在DerivedClassName(示例中的派生類)中沒有找到某個(gè)屬性,就會(huì)搜索Base1,然后(遞歸的)搜索其基類,如果最終沒有找到,就搜索Base2,以此類推
上面的搜索順序有一個(gè)問題,如果多個(gè)Base擁有相同的基類,就會(huì)發(fā)生重復(fù)訪問基類的情況,這時(shí),可以用super()來動(dòng)態(tài)改變解析順序
為了防止重復(fù)訪問基類,通過動(dòng)態(tài)的線性化算法,每個(gè)類都按從左到右的順序特別指定了順序,每個(gè)祖先類只調(diào)用一次
私有變量
Python中不存在只能從對(duì)象內(nèi)部訪問的實(shí)例變量,一般規(guī)定私有變量以一個(gè)下劃線開頭命名,例如_spam。此外,形如__spam,以兩個(gè)下劃線開頭,后面至多只有一個(gè)下劃線的變量,會(huì)被替換為_classname__spam
>>> class Ms1:
... def _update(self):
... print(345)
... def __hello(self):
... print("hello world")
...
>>> m = Ms1()
>>> m._Ms1__hello()
hello world
>>> m.__hello()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Ms1' object has no attribute '__hello'
>>> m._Ms1__hello()
hello world
>>> m._update()
345
迭代器
大多數(shù)容器對(duì)象都可以用for遍歷,如for elem in [1, 2, 3]。在后臺(tái),for語句在容器對(duì)象中調(diào)用iter(),該函數(shù)返回一個(gè)定義了__next__()方法的迭代器對(duì)象,它在容器中逐一訪問元素,當(dāng)沒有后續(xù)的元素時(shí),__next__()拋出一個(gè)StopIteration異常來通知for語句結(jié)束循環(huán)
可以使用內(nèi)建的next()函數(shù)來調(diào)用__next__()方法
>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
next(it)
StopIteration
根據(jù)之前所說的原理,可以很容易的為自己的類添加迭代器行為,以下例子:
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
... print(char)
...
m
a
p
s
生成器
生成器是創(chuàng)建迭代器的簡(jiǎn)單而強(qiáng)大的工具,它們寫起來就像是正規(guī)的函數(shù),需要返回?cái)?shù)據(jù)的時(shí)候使用yield語句
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>> for char in reverse('golf'):
... print(char)
...
f
l
o
g
前面中描述了基于類的迭代器,它能做的每一件事生成器也能做到。因?yàn)樽詣?dòng)創(chuàng)建了__iter__()和__next__()方法,生成器顯得如此簡(jiǎn)潔。除了創(chuàng)建和保存程序狀態(tài)的自動(dòng)方法,當(dāng)發(fā)生器終結(jié)時(shí),還會(huì)自動(dòng)拋出StopIteration異常。綜上所述,這些功能使得編寫一個(gè)正規(guī)函數(shù)成為創(chuàng)建迭代器的最簡(jiǎn)單方法