目錄 | 上一節 (2.6 列表推導式) | [下一節 (3 程序組織)]()python
本節介紹有關 Python 內部對象模型的更多詳細信息,並討論一些與內存管理,拷貝和類型檢查有關的問題。git
Python 中的許多操做都與賦值或者存儲值有關。github
a = value # Assignment to a variable s[n] = value # Assignment to a list s.append(value) # Appending to a list d['key'] = value # Adding to a dictionary
警告:賦值操做永遠不是值拷貝。全部的賦值操做都是引用拷貝(若是你樂意,也能夠說是指針拷貝)segmentfault
考慮該代碼片斷:app
a = [1,2,3] b = a c = [a,b]
如下是底層內存操做圖。在此示例中,只有一個列表對象 [1,2,3]
,可是有四個不一樣的引用指向它。
這意味着修改一個值會影響全部的引用。函數
>>> a.append(999) >>> a [1,2,3,999] >>> b [1,2,3,999] >>> c [[1,2,3,999], [1,2,3,999]] >>>
請注意,原始列表中的更改是如何在其它地方顯示的。這是由於從未進行任何拷貝,全部的東西都指向同一個東西。ui
從新賦值永遠不會重寫以前的值所使用的內存。spa
a = [1,2,3] b = a a = [4,5,6] print(a) # [4, 5, 6] print(b) # [1, 2, 3] Holds the original value
切記:變量是名稱,不是內存地址翻譯
若是你不知道這種(數據)共享(的方式),那麼在某些時候你會搬起石頭砸本身的腳。典型情景,你修改了一些數據,覺得它是本身的私有拷貝,可是它卻意外地損破壞了程序其它部分的某些數據。指針
說明:這就是爲何原始數據類型是不可變(只讀)的緣由之一
使用 is
操做符檢查兩個值是否真的是相同的對象。
>>> a = [1,2,3] >>> b = a >>> a is b True >>>
is
操做符比較對象的標識值(一個整數)。標識值可使用 id()
函數獲取。
>>> id(a) 3588944 >>> id(b) 3588944 >>>
注意:使用 ==
檢查對象是否相等幾乎老是更好,is
的結果一般會出乎意料:
>>> a = [1,2,3] >>> b = a >>> c = [1,2,3] >>> a is b True >>> a is c False >>> a == c True >>>
列表和字典自身具備用於拷貝的方法。
>>> a = [2,3,[100,101],4] >>> b = list(a) # Make a copy >>> a is b False
這是一個新列表,可是列表中的項是共享的。
>>> a[2].append(102) >>> b[2] [100,101,102] >>> >>> a[2] is b[2] True >>>
例如,內部列表 [100, 101, 102]
正在共享。這就是衆所皆知的淺拷貝。下面是圖示:
有時候,須要拷貝一個對象及其中所包含的全部對象,爲此,可使用 copy
模塊:
>>> a = [2,3,[100,101],4] >>> import copy >>> b = copy.deepcopy(a) >>> a[2].append(102) >>> b[2] [100,101] >>> a[2] is b[2] False >>>
變量名稱沒有類型,僅僅是一個名字。可是,值確實具備一個底層的類型。
>>> a = 42 >>> b = 'Hello World' >>> type(a) <type 'int'> >>> type(b) <type 'str'>
type()
函數將告訴你這是什麼。類型名稱一般用做建立或將值轉換爲該類型的函數。
如何判斷對象是否爲特定類型?
if isinstance(a, list): print('a is a list')
檢查是不是多種類型中的一種:
if isinstance(a, (list,tuple)): print('a is a list or tuple')
注意:不要過分使用類型檢查。這會致使過分的代碼複雜性。一般,若是這樣作可以阻止其餘人在使用你的代碼時犯常見錯誤,那麼就使用類型檢查。
數字,字符串,列表,函數,異常,類,實例等都是對象。這意味着全部能夠命名的對象均可以做爲數據傳遞、放置到容器中,而沒有任何限制。沒有特殊的對象。有時,能夠這樣說,全部的對象都是「一等對象」。
一個簡單的例子:
>>> import math >>> items = [abs, math, ValueError ] >>> items [<built-in function abs>, <module 'math' (builtin)>, <type 'exceptions.ValueError'>] >>> items[0](-45) 45 >>> items[1].sqrt(2) 1.4142135623730951 >>> try: x = int('not a number') except items[2]: print('Failed!') Failed! >>>
在這裏,items
是一個包含函數,模塊和異常的列表。能夠直接使用列表中的項代替原始名稱。
items[0](-45) # abs items[1].sqrt(2) # math except items[2]: # ValueError
權利越大,責任越大。只是由於你能夠作,但並意味這你應該這樣作。
在這組練習中,咱們來看看來自一等對象的威力。
在 Data/portfolio.csv
文件中,咱們把有組織的數據讀取爲列,以下所示:
name,shares,price "AA",100,32.20 "IBM",50,91.10 ...
在以前的代碼中,咱們使用 csv
模塊讀取文件,可是仍必須手動執行類型轉換。例如:
for row in rows: name = row[0] shares = int(row[1]) price = float(row[2])
也可使用一些列表基本操做以更巧妙的方式來執行這種轉換。
建立一個包含轉換函數名稱的 Python 列表,這些函數用來把每一列轉換成適當的類型。
>>> types = [str, int, float] >>>
能夠建立這樣的列表是由於在 Python 中一切皆一等對象。因此,若是想建立一個函數列表,也是能夠的。列表中建立的項用於將值 x
轉換爲給定的類型(如:str(x)
, int(x)
, float(x)
)。
如今,從上面文件的數據中讀取一行:
>>> import csv >>> f = open('Data/portfolio.csv') >>> rows = csv.reader(f) >>> headers = next(rows) >>> row = next(rows) >>> row ['AA', '100', '32.20'] >>>
如前所述,該行不足以進行計算,由於類型是錯誤的。例如:
>>> row[1] * row[2] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't multiply sequence by non-int of type 'str' >>>
可是,也許數據能夠與在 types
中指定的類型配對。例如:
>>> types[1] <type 'int'> >>> row[1] '100' >>>
嘗試轉換其中一個值:
>>> types[1](row[1]) # Same as int(row[1]) 100 >>>
嘗試轉換另外一個值:
>>> types[2](row[2]) # Same as float(row[2]) 32.2 >>>
嘗試使用轉換後的值進行計算:
>>> types[1](row[1])*types[2](row[2]) 3220.0000000000005 >>>
使用 zip() 函數將字段組合到一塊兒,而且查看結果:
>>> r = list(zip(types, row)) >>> r [(<type 'str'>, 'AA'), (<type 'int'>, '100'), (<type 'float'>,'32.20')] >>>
注意看,這會將類型轉換函數名稱與值配對。例如,int
和 '100'
配對。
若是要一個接一個地對全部值進行轉換,那麼合併後的列表頗有用。請嘗試:
>>> converted = [] >>> for func, val in zip(types, row): converted.append(func(val)) ... >>> converted ['AA', 100, 32.2] >>> converted[1] * converted[2] 3220.0000000000005 >>>
確保你理解上述代碼中所發生的事情。在循環中,func
變量是類型轉換函數(如str
, int
等 )之一且 val
變量是值('AA'
, '100'
)之一。表達式 func(val)
轉換一個值(相似於類型轉換)。
上面的代碼能夠轉換爲單個列表推導式。
>>> converted = [func(val) for func, val in zip(types, row)] >>> converted ['AA', 100, 32.2] >>>
還記得若是有一個鍵和值的序列,如何使用dict()
函數輕鬆地建立字典嗎?讓咱們從列標題建立一個字典吧:
>>> headers ['name', 'shares', 'price'] >>> converted ['AA', 100, 32.2] >>> dict(zip(headers, converted)) {'price': 32.2, 'name': 'AA', 'shares': 100} >>>
固然,若是你精通列表推導式,則可使用字典推導式一步完成整個轉換。
>>> { name: func(val) for name, func, val in zip(headers, types, row) } {'price': 32.2, 'name': 'AA', 'shares': 100} >>>
使用本練習中的技術,能夠編寫語句,輕鬆地將幾乎任何面向列的數據文件中的字段轉換爲 Python 字典。
爲了說明,假設你像下面這樣從不一樣的數據文件讀取數據,以下所示:
>>> f = open('Data/dowstocks.csv') >>> rows = csv.reader(f) >>> headers = next(rows) >>> row = next(rows) >>> headers ['name', 'price', 'date', 'time', 'change', 'open', 'high', 'low', 'volume'] >>> row ['AA', '39.48', '6/11/2007', '9:36am', '-0.18', '39.67', '39.69', '39.45', '181800'] >>>
讓咱們使用相似的技巧來轉換字段:
>>> types = [str, float, str, str, float, float, float, float, int] >>> converted = [func(val) for func, val in zip(types, row)] >>> record = dict(zip(headers, converted)) >>> record {'volume': 181800, 'name': 'AA', 'price': 39.48, 'high': 39.69, 'low': 39.45, 'time': '9:36am', 'date': '6/11/2007', 'open': 39.67, 'change': -0.18} >>> record['name'] 'AA' >>> record['price'] 39.48 >>>
附加題:如何修改本示例以進一步解析 date
條目到元組中,如(6, 11, 2007)
?
請花一些時間仔細思考你在練習中所作的事情。咱們稍後會再次討論這些想法。
目錄 | 上一節 (2.6 列表推導式) | [下一節 (3 程序組織)]()