首頁>技術>

賦值

Python 中的許多操作都與賦值或者儲存值有關。

a = value         # Assignment to a variables[n] = value      # Assignment to a lists.append(value)   # Appending to a listd['key'] = value  # Adding to a dictionary

警告:賦值操作永遠不是值複製。所有的賦值操作都是引用複製(如果你樂意,也可以說是指標複製)

賦值示例

考慮該程式碼片段:

a = [1,2,3]b = ac = [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]]>>>

請注意,原始列表中的更改是如何在其它地方顯示的。這是因為從未進行任何複製,所有的東西都指向同一個東西。

重新賦值

重新賦值永遠不會重寫之前的值所使用的記憶體。

a = [1,2,3]b = aa = [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 bTrue>>>

is 運算子比較物件的標識值(一個整數)。標識值可以使用 id() 函式獲取。

>>> id(a)3588944>>> id(b)3588944>>>

注意:使用 == 檢查物件是否相等幾乎總是更好,is的結果通常會出乎意料:

>>> a = [1,2,3]>>> b = a>>> c = [1,2,3]>>> a is bTrue>>> a is cFalse>>> a == cTrue>>>
淺複製

列表和字典自身具有用於複製的方法。

>>> a = [2,3,[100,101],4]>>> b = list(a) # Make a copy>>> a is bFalse

這是一個新列表,但是列表中的項是共享的。

>>> 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)       # absitems[1].sqrt(2)    # mathexcept items[2]:    # ValueError

權利越大,責任越大。只是因為你可以做,但並意味這你應該這樣做。

練習

在這組練習中,我們來看看來自一等物件的威力。

練習 2.24:一等資料

在 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]>>>
練習 2.25:建立字典

還記得如果有一個鍵和值的序列,如何使用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}>>>
練習 2.26:全域性

使用本練習中的技術,可以編寫語句,輕鬆地將幾乎任何面向列的資料檔案中的欄位轉換為 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)?

7
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 企業微服務技術中臺落地實踐