翻譯:《實用的Python編程》02_04_Sequences

目錄| [上一節 (2.3 格式化)]() | 下一節 (2.5 Collections模塊)python

2.4 序列

序列數據類型

Python 有三種序列數據類型。git

  • 字符串:如 'Hello'。字符串是字符序列
  • 列表:如 [1, 4, 5]
  • 元組:如 ('GOOG', 100, 490.1)

全部的序列都是有序的,由整數進行索引,而且具備長度。github

a = 'Hello'               # String
b = [1, 4, 5]             # List
c = ('GOOG', 100, 490.1)  # Tuple

# Indexed order
a[0]                      # 'H'
b[-1]                     # 5
c[1]                      # 100

# Length of sequence
len(a)                    # 5
len(b)                    # 3
len(c)                    # 3

序列能夠經過重複操做符 * 進行重複:s * nsegmentfault

>>> a = 'Hello'
>>> a * 3
'HelloHelloHello'
>>> b = [1, 2, 3]
>>> b * 2
[1, 2, 3, 1, 2, 3]
>>>

相同類型的序列能夠經過加號 + 進行拼接:s + t函數

>>> a = (1, 2, 3)
>>> b = (4, 5)
>>> a + b
(1, 2, 3, 4, 5)
>>>
>>> c = [1, 5]
>>> a + c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate tuple (not "list") to tuple

切片

切片是指着從序列中提取子序列。切片的語法爲 s[start:end]startend 是想要的子序列的索引。oop

a = [0,1,2,3,4,5,6,7,8]

a[2:5]    # [2,3,4]
a[-5:]    # [4,5,6,7,8]
a[:3]     # [0,1,2]
  • 索引 startend 必須是整數。
  • 切片不包括結尾值。這就像數學上的半開區間。
  • 若是省略索引,則它們默認爲序列的開頭或結尾。

切片與從新賦值

在列表上,切片能夠被從新賦值和刪除。編碼

# Reassignment
a = [0,1,2,3,4,5,6,7,8]
a[2:4] = [10,11,12]       # [0,1,10,11,12,4,5,6,7,8]

注意:從新賦值的切片不須要具備相同的長度。翻譯

# Deletion
a = [0,1,2,3,4,5,6,7,8]
del a[2:4]                # [0,1,4,5,6,7,8]

序列的縮減

有一常見的函數用於把序列縮減爲單個值。code

>>> s = [1, 2, 3, 4]
>>> sum(s)
10
>>> min(s)
1
>>> max(s)
4
>>> t = ['Hello', 'World']
>>> max(t)
'World'
>>>

迭代序列

可使用 for 循環對序列中的元素進行迭代。索引

>>> s = [1, 4, 9, 16]
>>> for i in s:
...     print(i)
...
1
4
9
16
>>>

在循環的每次迭代中,會獲取一個新的項來處理。這個新的值會被放到迭代變量中。在此示例中,迭代變量爲 x:

for x in s:         # `x` is an iteration variable
    ...statements

在每次迭代中,迭代變量的先前值會被覆蓋(若是有)。循環結束後,迭代變量保留最後一個值。

break 語句

可使用 break 語句提早跳出循環。

for name in namelist:
    if name == 'Jake':
        break
    ...
    ...
statements

break 語句執行時,它退出循環而且進入下一個語句。break 語句僅應用於最內部的循環。若是此循環在另外一個循環的內部,那麼 break 不會中斷外部循環。

continue 語句

要跳過一個元素而且進入到下一個,請使用 continue 語句。

for line in lines:
    if line == '\n':    # Skip blank lines
        continue
    # More statements
    ...

若是當前項不重要或者是在處理時須要忽略,那麼使用 continue 語句頗有用。

遍歷整數

若是須要計數,請使用 range() 函數。

for i in range(100):
    # i = 0,1,...,99

range() 函數的語法是range([start,] end [,step])

for i in range(100):
    # i = 0,1,...,99
for j in range(10,20):
    # j = 10,11,..., 19
for k in range(10,50,2):
    # k = 10,12,...,48
    # Notice how it counts in steps of 2, not 1.
  • 不包括結尾值。這與切片相似。
  • start 是可選的 , 默認值是 0
  • step 是可選的,默認值是 1
  • 當須要的值時候 range()才計算值,實際上,它不存儲大範圍的數。

enumerate() 函數

enumerate 函數爲迭代添加一個額外的計數值。

names = ['Elwood', 'Jake', 'Curtis']
for i, name in enumerate(names):
    # Loops with i = 0, name = 'Elwood'
    # i = 1, name = 'Jake'
    # i = 2, name = 'Curtis'

通常格式爲enumerate(sequence [, start = 0])start是可選的,一個很好的使用示例:讀取文件時跟蹤行數。

with open(filename) as f:
    for lineno, line in enumerate(f, start=1):
        ...

enumerate能夠當作如下語句的簡寫:

i = 0
for x in s:
    statements
    i += 1

使用 enumerate 函數能夠減小輸入,運行速度也稍快一些。

For 與元組

能夠迭代多個變量:

points = [
  (1, 4),(10, 40),(23, 14),(5, 6),(7, 8)
]
for x, y in points:
    # Loops with x = 1, y = 4
    #            x = 10, y = 40
    #            x = 23, y = 14
    #            ...

當使用多個變量時,每一個元組被拆包爲一組迭代變量。變量的數目必須與每一個元組中的項數匹配。

zip() 函數

zip 函數採用多個序列,而且生成將它們組合在一塊兒的迭代器。

columns = ['name', 'shares', 'price']
values = ['GOOG', 100, 490.1 ]
pairs = zip(columns, values)
# ('name','GOOG'), ('shares',100), ('price',490.1)

要得到結果,必須進行迭代。能夠如先前所示的那樣使用多個變量對元組進行拆包。

for column, value in pairs:
    ...

zip 函數的常見用法是建立用於構造字典的鍵值對。

d = dict(zip(columns, values))

練習

練習 2.13:計數

嘗試一些基本的計數示例:

>>> for n in range(10):            # Count 0 ... 9
        print(n, end=' ')

0 1 2 3 4 5 6 7 8 9
>>> for n in range(10,0,-1):       # Count 10 ... 1
        print(n, end=' ')

10 9 8 7 6 5 4 3 2 1
>>> for n in range(0,10,2):        # Count 0, 2, ... 8
        print(n, end=' ')

0 2 4 6 8
>>>

練習 2.14:更多序列操做

交互地試驗一些序列縮減操做。

>>> data = [4, 9, 1, 25, 16, 100, 49]
>>> min(data)
1
>>> max(data)
100
>>> sum(data)
204
>>>

嘗試遍歷數據。

>>> for x in data:
        print(x)

4
9
...
>>> for n, x in enumerate(data):
        print(n, x)

0 4
1 9
2 1
...
>>>

有時候,for 語句,len()range() 函數被初學者用於一些可怕的代碼片斷中,這些代碼看起來像來自於古老的 C 程序。

>>> for n in range(len(data)):
        print(data[n])

4
9
1
...
>>>

不要那樣作。閱讀這些代碼不只辣眼睛,並且內存效率低,運行慢。若是想要迭代數據,使用普通的for 循環便可。若是碰巧由於某些緣由須要使用索引,請使用 enumerate()函數。

練習 2.15:enumerate() 函數使用示例

回想一下,Data/missing.csv 文件包含一個股票投資組合的數據,可是有一些行缺乏值。請使用 enumerate() 函數修改 pcost.py 程序,以便在遇到錯誤的輸入時,打印帶有警告信息的行號。

>>> cost = portfolio_cost('Data/missing.csv')
Row 4: Couldn't convert: ['MSFT', '', '51.23']
Row 7: Couldn't convert: ['IBM', '', '70.44']
>>>

爲此,須要修改部分代碼。

...
for rowno, row in enumerate(rows, start=1):
    try:
        ...
    except ValueError:
        print(f'Row {rowno}: Bad row: {row}')

練習 2.16:使用 zip() 函數

Data/portfolio.csv 文件中,第一行包含列標題。在以前全部代碼中,咱們把它丟棄了。

>>> f = open('Data/portfolio.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name', 'shares', 'price']
>>>

可是,若是標題要用於其它有用的事情呢?這就涉及到 zip() 函數了。首先,嘗試把文件標題和數據行配對。

>>> row = next(rows)
>>> row
['AA', '100', '32.20']
>>> list(zip(headers, row))
[ ('name', 'AA'), ('shares', '100'), ('price', '32.20') ]
>>>

請注意 zip() 函數是如何把列標題與列值配對。在這裏,咱們使用 list() 函數把結果轉換爲列表,以便查看。一般,zip() 函數建立一個必須由 for 循環使用的迭代器。

這種配對是構建字典的中間步驟。如今嘗試:

>>> record = dict(zip(headers, row))
>>> record
{'price': '32.20', 'name': 'AA', 'shares': '100'}
>>>

在處理大量數據文件時,這種轉換是最有用的技巧之一。例如,假設須要使 pcost.py 程序處理各類輸入文件,可是不考慮名稱,份額,價格所在列的編號。

修改 pcost.py 程序中的 portfolio_cost(),使其看起來像這樣:

# pcost.py

def portfolio_cost(filename):
    ...
        for rowno, row in enumerate(rows, start=1):
            record = dict(zip(headers, row))
            try:
                nshares = int(record['shares'])
                price = float(record['price'])
                total_cost += nshares * price
            # This catches errors in int() and float() conversions above
            except ValueError:
                print(f'Row {rowno}: Bad row: {row}')
        ...

如今,在一個徹底不一樣的數據文件 Data/portfoliodate.csv(以下所示)上嘗試 portfolio_cost() 函數。

name,date,time,shares,price
"AA","6/11/2007","9:50am",100,32.20
"IBM","5/13/2007","4:20pm",50,91.10
"CAT","9/23/2006","1:30pm",150,83.44
"MSFT","5/17/2007","10:30am",200,51.23
"GE","2/1/2006","10:45am",95,40.37
"MSFT","10/31/2006","12:05pm",50,65.10
"IBM","7/9/2006","3:15pm",100,70.44
>>> portfolio_cost('Data/portfoliodate.csv')
44671.15
>>>

若是操做正確,會發現程序仍然可以正常運行,即便數據文件的列格式與以前的徹底不一樣,這很酷!

此處所作的更改是微妙的,可是卻意義重大。新版的 portfolio_cost()能夠讀取任何 CSV 文件,並從中選擇須要的值,而不是硬編碼去讀取單個固定文件格式。只要文件有必要的列,代碼就能正常運行。

修改在 2.3 節編寫的 report.py 程序,以便可以使用相同的技術挑選出列標題。

嘗試以 Data/portfoliodate.csv 文件做爲輸入,運行 report.py 程序,並觀察是否生成和以前同樣的答案。

練習 2.17:翻轉字典

字典將鍵映射到值。例如,股票價格字典。

>>> prices = {
        'GOOG' : 490.1,
        'AA' : 23.45,
        'IBM' : 91.1,
        'MSFT' : 34.23
    }
>>>

若是使用字典的 items() 方法,那麼能夠獲取到鍵值對 (key,value)

>>> prices.items()
dict_items([('GOOG', 490.1), ('AA', 23.45), ('IBM', 91.1), ('MSFT', 34.23)])
>>>

可是,若是想要獲取 (value, key) 鍵值對列表呢?

提示:使用 zip()函數。

>>> pricelist = list(zip(prices.values(),prices.keys()))
>>> pricelist
[(490.1, 'GOOG'), (23.45, 'AA'), (91.1, 'IBM'), (34.23, 'MSFT')]
>>>

爲何這樣操做?首先,這容許對字典數據執行確切類型的數據處理。

>>> min(pricelist)
(23.45, 'AA')
>>> max(pricelist)
(490.1, 'GOOG')
>>> sorted(pricelist)
[(23.45, 'AA'), (34.23, 'MSFT'), (91.1, 'IBM'), (490.1, 'GOOG')]
>>>

其次,這也說明了元組的一個重要特徵,當在比較中使用元組時,從第一項開始,逐元素進行比較,相似於字符串中字符與字符逐個比較。

zip() 函數常常應用於須要從不一樣的地方把數據進行配對。例如,爲了使用已命名的值構建字典,將列名和列值進行配對。

請注意,zip() 函數不限於一對。例如,可使用任意數量的列表做爲輸入。

>>> a = [1, 2, 3, 4]
>>> b = ['w', 'x', 'y', 'z']
>>> c = [0.2, 0.4, 0.6, 0.8]
>>> list(zip(a, b, c))
[(1, 'w', 0.2), (2, 'x', 0.4), (3, 'y', 0.6), (4, 'z', 0.8))]
>>>

另外,請注意,一旦最短的輸入序列耗盡,zip() 函數將會中止。

>>> a = [1, 2, 3, 4, 5, 6]
>>> b = ['x', 'y', 'z']
>>> list(zip(a,b))
[(1, 'x'), (2, 'y'), (3, 'z')]
>>>

目錄| [上一節 (2.3 格式化)]() | 下一節 (2.5 Collections模塊)

注:完整翻譯見 https://github.com/codists/practical-python-zh

相關文章
相關標籤/搜索