2018-01-16 Python學習第四天

2.相等運算符

3.is:同一性運算符

#避免將is運算符用于比較類似數值和字符串這類不可變值,由于Python內部操作這些對象的方式的原因,使用is運算符的結果是不可預測的。

4.in: 成員資格運算符

【代碼】

name = input('what

is your name?')

if 's' in name:


print('Your name contains the letter "s".')

else:


print ('Your name does not contain the letter

"s".')

【結果】

what is your name?nihao

Your name does not contain the letter "s".

5.字符串和序列比較

【代碼】

print("alpha"< "beta")

print(

'FnOrD'.lower())

print(

'FnOrD'.upper())

print([

1,2] < [2,1]) #參與比較的不是字符而是元素的其他類型print([2,[1,4]] <[2,[1,5]]) #如果一個序列中包含其他序列元素,比較規則同樣適用于序列元素

【結果】

True

fnord

FNORD

True

True

6.布爾運算符

【代碼】

#有時想要檢查一個以上的條件,可以這樣做:number = int(input('Enter

a number between 1 and 10:'))

if number <= 10:


if number

>=1:


print('Great!')


else:


print('Wrong!')

else:


print('Wrong')

#下面使用布爾思想進行優化if number <= 10 and number >= 1:


print('Great')

else:


print('Wrong')

#and運算符就是所謂的布爾運算符。它連接兩個布爾值,并且在兩者都為真時返回真,否則返回假。與它同類的還有兩個運算符,or和not。

#

布爾運算符有個有趣的特性:只有在需要求值時才進行求值。例如,表達式x and y需要兩個變量都為真時才為真,如果x為假,表達之會立刻返回false,不管y的值。這類短路邏輯可以用來實現C和Java中所謂的三元運算符。

【結果】

Enter a number between 1 and 10:111

Wrong

Wrong

5.4.7 斷言

【代碼】

#if語句有個非常有用的“近親”,它的工作方式像下面:

#if not condition

#?? crash program

#

就是因為其他程序在晚些時候崩潰,比如在錯誤條件出現時直接讓它崩潰。語句中使用的關鍵字是assertage = 10

assert 0 <

age < 100

age = -1

assert 0 <

age < 100

【結果】

Traceback (most recent call last):

? File"D:/python_file/20180113/test.py", line 8, in

??? assert 0 < age < 100

AssertionError

【代碼】

#如果需要確保程序中的某個條件一定為真才讓程序正常工作的話,assert語句就有用了,它可以在程序中置入檢查點,條件后添加字符串,用于解釋斷言age = -1

assert 0 <

age < 100 ,'The

age must be realistic'

【結果】

Traceback (most recent call last):

? File"D:/python_file/20180113/test.py", line 3, in

??? assert 0 < age < 100,'The age must be realistic'

AssertionError: The age must be realistic

5.5 循環

但是怎樣才能重復執行多次呢?比如,寫一個每月提醒付房租的程序,但是不使用循環,需要這樣寫:

發郵件

等一個月

發郵件

等一個月

(繼續下去……)

但是如果想讓程序繼續執行,直到認為停止它呢?

5.5.1 while循環

【代碼】

x = 1

while x <= 100:


print(x)

??? x+=

1

【結果】

不寫了吧

【代碼】

name = ''

while not name.strip(): #這回,連空格也逃脫不掉了

??? name = input('Please

enter your name:')

print('Hello,%s!'% name)

【結果】

不寫了吧

5.5.2 for循環

【代碼】

#while語句非常靈活,它可以用來在任何條件為真的情況下重復執行一個代碼塊。但是,有時還得量體裁衣。比如要為一個集合(序列或其他可迭代對象)的每個元素都執行一個代碼塊words = ['this','is','an','ex','parrot']

for word in words:


print(word)

#range函數的工作方式類似于分片,它包含下限,但不包含上限。

#range()

函數返回的是一個可迭代對象(類型是對象),而不是列表類型, 所以打印的時候不會打印列表。

# #list()

函數是對象迭代器,把對象轉為一個列表。返回的變量類型為列表。a = range(0,10) #下限為0,上限為9a = list(a)

print(a)

#xrange函數的循環行為類似于range函數,區別在于range函數一次創建整個序列而xrange一次只創建一個數。當需要迭代一個巨大的序列時,xrange會更高效,一般情況下不需要關注它。

【結果】

this

is

an

ex

parrot

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

5.5.3 循環遍歷字典元素

【代碼】

#一個簡單的for語句就能循環字典的所有鍵,就像處理序列一樣d = {'x':1,'y':2,'z':3}

for key in d:


print(key,'correspends to',d[key])

#for循環的一大好處就是可以使用序列解包

#

字典嚴肅的順序通常是沒有意義的。迭代的時候,字典中的鍵和值都能保證被處理,但是處理順序不確定。如果處理順序很重要的話,可以把鍵保存在列表中。

【結果】

y correspends to 2

z correspends to 3

x correspends to 1

5.5.4 一些迭代工具

在Python中迭代序列時,一些函數非常有用。有些函數位于itertools模塊中,還有一些內建函數也十分有用。

[if !supportLists]1.? [endif]并行迭代

【代碼】

#程序可以同時迭代兩個序列。比如:names = ['anne','beth','george','damon']

ages = [

12,45,32,102]

#如果想要打印名字和對應的年齡,可以像下面這樣做:for i in range(len(names)):


print(names[i],'is',ages[i],'years

old')

print('---------------------------------------------')

z =

zip(names,ages) #python3中,使用zip后,得到的是一個可迭代對象print(z)

for i in z:


print(i) #可以看出,打印出來的是一個元組print('---------------------------------------------')

#注:要重新獲得迭代對象,不能再使用上面一個z了,以為上面的z已經迭代到末尾了z = zip(names,ages) #python3中,使用zip后,得到的是一個可迭代對象for i in z: #這里不嫩解包了?

??? print(i[0],'is',i[1],'years old')

【結果】

anne is 12 years old

beth is 45 years old

george is 32 years old

damon is 102 years old

---------------------------------------------

('anne', 12)

('beth', 45)

('george', 32)

('damon', 102)

---------------------------------------------

anne is 12 years old

beth is 45 years old

george is 32 years old

damon is 102 years old

【代碼】

#zip函數也可以用于任意多的序列。關于它很重要的一點就是zip可以應付不等長序列:當最短的序列用完時停止z = zip(range(5),range(100000))

for i in z:


print(i)


#print(i[0],i[1])

【結果】

(0, 0)

(1, 1)

(2, 2)

(3, 3)

(4, 4)

[if !supportLists]2.? [endif]編號迭代

【代碼】

#有時候想要迭代序列中的對象,還想要獲取當前對象的索引。例如,在一個字符串列表中替換所有包含'xxx'的子字符串。strings = ['hello,world!,xxx,yyy,zzz','dkdskldsklkl']

#strings 村莊

#string?

人家

#xxx????

要找的人for string in strings:#大框

??? if 'xxx' instring: #小框

??????? index = strings.index(string) #index相當于門牌號

??????? strings[index] = '[censored]'

print(strings)

【結果】

['[censored]', 'dkdskldsklkl']

【同上,代碼】

#沒問題,但是在替換之前搜索給定的字符串似乎沒必要。如果不替換的話,搜索還會返回錯誤的索引,較好的版本如下index = 0

for string in strings:


if 'xxx' in string:

??????? strings[index] =

'[censored]'

??? index += 1

print(strings)

【結果】

['[censored]', 'dkdskldsklkl']

【同上,代碼】

#方法有點笨,不過可以接受。另一種方法是使用內建的enumerate函數:for index,string in enumerate(strings): #這樣的話,一戶人家和一戶人家的門牌號都有了

??? if 'xxx' instring:

??????? strings[index] =

'[censored]'

print(strings)

【結果】

['[censored]', 'dkdskldsklkl']

[if !supportLists]3.? [endif]翻轉和排序迭代

【代碼】

s = sorted([4,3,6,8,3])

print(s)

s2 =

sorted('Hello,

world!')

print(s2)

s3 =

list(reversed('Hello,

world!'))

print(s3)

#reversed函數和sorted函數作用相反,但是reversed函數還需要使用list函數把得到的對象解包,sorted函數自帶解包功能。

【結果】

[3, 3, 4, 6, 8]

[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']

['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']

5.5.5 跳出循環

一般來說,循環會一直執行直到條件為假,或者到序列元素用完時。但是有些時候可能會提前中斷一個循環,進行新的迭代。

[if !supportLists]1.? [endif]break

【代碼】

#結束(跳出)循環可以使用break語句。假設需要尋找100以內的最大平方數,那么程序可以從100往下迭代到0,當找到一個平方數時就不需要繼續循環了。from math import sqrt

for n in range(99,0,-1): #range增加了第三個參數:步長

??? root = sqrt(n)


if root

== int(root):


print(n)


break

【結果】

81

[if !supportLists]2.? [endif]continue

for x in seq:

??? if condition1:continue

??? if condition2:continue

??? if condition3:continue


??? do_something()

??? do_something_else()

??? do_another_thing()

??? etc()

很多時候,只要使用if語句就可以了;

for x in seq:

??? if not (condition1 orcondition2 or condition3):

?????? do_something()

?????? do_something_else()

?????? do_another_thing()

?????? etc()

盡管continue語句非常有用,它卻不是最本質的。應該習慣使用break語句,因為在while True語句中會經常用到它。

[if !supportLists]3.? [endif]while True/break習語

【代碼1】

#Python中while和for循環非常靈活,但一旦使用while語句就會遇到一個需要更多功能的問題。word = 'dummy' #代碼有些丑,在進入循環體之前需要給word賦一個啞值。使用啞值就是工作沒有盡善盡美的標志while word:

??? word =

input('please enter a word:')


#處理word

??? print('The word was',word)

【代碼2】

#解決啞值問題while True:

??? word =

input('please enter a word:')


if not word: break

??? #處理word

??? print('The word was',word)

#while True部分實現了一個永遠不會自己停止的循環。但在循環內部的if語句中加入條件是可以的,在條件滿足時調用break語句,這樣就可以在循環體內部的任何地方結束循環,而不僅僅在凱循環的開頭部分。if/break語句自然地將循環分為兩部分:第1部分負責初始化,第2部分則在循環條件為真的情況下使用第1部分內部初始化好的數據。

5.5.6 循環中的else子句

broke_out = False

for x in seq:

??? do_something(x)

??? if condition(x):

?????? broke_out = True

?????? break

??? do_something_else(x)

if not broke_out:

??? print(“I didn’t break out!”)

更簡單的方式是在循環中增加一個else子句—它僅在沒有調用break時執行。

【代碼】

from math import sqrt

for n in range(99,81,-1):

??? root = sqrt(n)


if root

== int(root):


print(n)


break

else: #for循環中的所有語句都執行過的時候(沒有跳出循環),從這個地方開始執行

??? print("Didn't find it!")

#跳出for循環的話,從這個地方開始執行

#

注:for和while循環中都可以使用continue、break語句和else子句

【結果】

Didn't find it!

5.6 列表推導式----輕量級循環

【代碼】

#列表推導式是利用其他列表創建新列表(類似于數學術語中的集合推導式)的一種方法。它的工作方式類似于for循環x = [x*x for x in range(10)]

x2 = [x*x

for x in range(10) if x % 3 == 0]

xy = [(x,y)

for x in range(3) for y in range(3)] #兩個for之間不需要加and

#

作為對比,下面的代碼使用兩個for語句創建了相同的列表:result = []

for x in range(3):


for y in range(3):

??????? result.append((x,y))

print(x)

print(x2)

print(xy)

print(result)

#也可以和if子句聯合使用,像以前一樣:girls = ['alice','bernice','clarice']

boys = [

'chris','arnold','bob']

print([b + '+' + g for g in girls for b in boys if b[0] == g[0]])

#注:使用普通的圓括號而不是方括號不會得到“元組推導式”。,在最近的版本中,則會得到一個生成器。

【結果】

2

[0, 9, 36, 81]

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

['arnold+alice', 'bob+bernice', 'chris+clarice']

使用字典進行效率更高的優化,只使用一層for循環:

【代碼】

girls = ['alice','bernice','clarice']

boys = [

'chris','arnold','bob']

letteGirls = {}

for girl in girls:

??? letteGirls.setdefault(girl[

0],[]).append(girl)

a = [b +

'+' + g for b in boys for g in letteGirls[b[0]]] #列表推導式不能使用元組"()",而應使用列表"[]"print(a)

【結果】

['chris+clarice', 'arnold+alice', 'bob+bernice']

5.7 三人行

5.7.1 什么都沒發生

有時,程序什么事情都不做,這種情況,應讓pass出馬

【代碼】

#Python中空代碼是非法的,解決的方法就是在語句塊中加上一個pass語句:name = 'Bill Gates'

if name == 'Ralph Auldus Melish':


print('Welcome')

elif name == 'Enid':


#還沒完。。。

??? pass

elifname == 'Bill Gates':


print('Access Denied')

#注釋pass語句聯合的替代方案時插入字符串

【結果】

Access Denied

5.7.2 使用del刪除

【代碼】

#一般來說,Python會刪除那些不再使用的對象scoundrel = {'age':42,'first

name':'Robin','last

name':'of Locksley'}

robin = scoundrel

print(scoundrel)

print(robin)

scoundrel =

None

print(robin)

#首先,robin和scoundrel都被綁定到同一個字典上。所以當設置scoundrel為None的時候,字典通過robin還是可用的。但是當把robin也設置為None的時候,字典就“漂”在內存里面了,沒有任何名字綁定到它上面。所以python解釋器直接刪除了那個字典(這種行為被稱為垃圾收集)。也可以使用None之外的其他值,字典同樣會“消失不見”

【結果】

{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}

{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}

{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}

【代碼】

#使用del語句,不僅會移動一個對象的引用,也會移除那個名字本身

# x = 1

# del x

# print(x)

x = ['Hello','world']

y = x

y[

1] = 'Pythoin'

print(x)

del x

print(y)

#原因是刪除的只是名稱,而不是列表本身。事實上,在Python中是沒有辦法刪除值的(也不需要過多考慮刪除值的問題,因為在某個值不再使用的時候,Python解釋器會負責內存的回收)

【結果】

['Hello', 'Pythoin']

['Hello', 'Pythoin']

5.7.3 使用exec和eval執行和求值字符串

有時可能會需要動態創造Python代碼,然后將其作為語句執行或作為表達式計算,---在此之前,一定要謹慎

[if !supportLists]1.? [endif]exec

【代碼】

#在Python3中,exec是一個函數而不是語句exec('print("hello,world")') #exec函數的形參是一個字符串,所以兩邊加引號from math import sqrt

exec("sqrt

= 1")

print(sqrt(4))

#為什么一開始要這樣做?exec函數最有用的地方在于可以動態創建代碼字符串。為了安全起見,可以增加一個字典,起到命名空間的作用。命名空間又稱為作用域,是個非常重要的只知識。

【結果】

hello,world

Traceback (most recent call last):

? File"D:/python_file/20180113/test.py", line 6, in

??? print(sqrt(4))

TypeError: 'int' object is not callable

【示例】

#可以通過增加in 來實現,其中的就是起到放置代碼字符串命名空間作用的字典。from math import sqrt

scope = {}

exec('sqrt =

1',scope) #python3中使用這種形式限定作用域print(sqrt)

print(len(scope))

print(scope['sqrt']) #可以看出,sqrt=1 中,sqrt是鍵,1是值print(scope.keys()) #因為內建的__builtins__字典自動包含所有的內建函數和值

【結果】

2

1

dict_keys(['sqrt', '__builtins__'])

[if !supportLists]2.? [endif]eval

【示例】

#eval(用于“求值”)是類似于exec的內建函數。exec語句會執行一些列Python語句,而eval會計算Python表達式(以字符串形式書寫),并且返回結果值,(exec語句并不返回任何對象,因為它本身就是語句)。e = eval(input("Enter

an arithmetic expression:"))

print(e)

#注:Python2中,表達式eval(raw_input(...))事實上等同于input(...).在Python3.0中,raw_input被重命名為input

#

可以給eval語句提供兩個命名空間,一個全局的一個局部的。全局的必須是字典,局部的可以是任何形式的映射。

#

目前Python內沒有任何執行不可信任代碼的安全方式,一個可選的方案是使用Pythono的實現,比如Jython,以及使用一些本地機制,比如Java的sandbox

【結果】

Enter an arithmetic expression:6 + 18 * 2

42

【示例】

#初探作用域

#

給exec或者eval語句提供命名空間時,還可以在真正使用命名空間前放置一些值進去scope = {}

scope[

'x'] = 2

scope['y'] = 3

print(eval('x*y',scope))

#同理,exec或者eval調用的作用域也能在另外一個上面使用:scope = {}

exec('x = 2',scope)

print(eval('x*x',scope))

【結果】

6

4

第六章 抽 象

這里,將會學習語句組織成函數,這樣,可以告訴計算機如何做事。有了函數之后,不必反反復復項計算機傳遞同樣的具體指令了。本章還會詳細介紹參數和作用域的概念。以及遞歸概念及其在程序中的用途。

6.1 懶惰即美德

【代碼】

fibs = [0,1]

for i in range(8):

??? fibs.append(fibs[-

2] +fibs[-1])

print(fibs)

#如果想要一次計算前10個數的話,沒有問題。甚至可以將用戶輸入的數字作為動態范圍的長度使用,從而改變for語句循環的次數fibs = [0,1]

num =

int(input('How

many Fibonacci number do you want?')) #使用for i in range(num-2):


print('i=',i,)

??? fibs.append(fibs[-

2] +fibs[-1]) #主要在于循環幾次(總共需要5個,原始序列中有兩個)print(fibs)

【結果】

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

How many Fibonacci number do you want?5

i= 0

i= 1

i= 2

[0, 1, 1, 2, 3]

#真正的程序員會讓自己的程序抽象一些,上面的程序可以改寫為較抽象的版本:

#num = int(input('How many numbers do you want?'))

#print(fibs(num))

如果函數要用很多次的話,這么做會節省很多精力

6.2 抽象和結構

抽象可以節省很多工作,實際上它的作用還要更大,它是計算機程序可以讓人讀懂的關鍵(這也是最基本的要求)。計算機非常樂于處理精確和具體的指令,但是人可就不同了。

計算機會:向前走10米,左轉90度,再走5步,右轉45度,走123步

人只需要知道:一直沿著街道走,過橋,電影院就在左手邊,這樣就明白了吧!關鍵大家都知道怎樣走路和過橋,不需要指令來知道這些事。

組織計算機程序也是類似的。程序應該是非常抽象的。事實上,我們把這段描述翻譯為Python程序。

page = download_page()

freqs = compute_frequencies(page)

for word,freq in freqs:

??? print word,freq

6.3 創建函數

函數可以調用(可能包含參數),它執行某種行為并且返回一個值。一般來說,內建的callable函數可以用來判斷函數是否可調用:

【示例】

import math

x =

1

y = math.sqrt

print(callable(x))

print(callable(y))

【結果】

False

True

【示例】

#那么,怎樣定義函數呢def hello(name):


return 'Hello,'+name+'!'

print(hello('world'))

print(hello('Gumby'))

【結果】

Hello,world!

Hello,Gumby!

【示例】

def fibs(num):

??? result = [

0,1]


for i in range(num-2):

??????? result.append(result[-

2]+result[-1])


return result

print(fibs(10))

print(fibs(15))

#注:result語句是用來從函數中返回值的

【結果】

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]

6.3.1 記錄函數

如果想要給函數寫文檔,讓后面使用該函數人能理解的haul,可以加入注釋(以#開頭)。另外一個方式就是直接寫上字符串。這類字符串在其他地方可能會非常有用。如果在函數的開頭寫下字符串,它就會作為函數的一部分進行存儲,這稱為文檔字符串。

【示例】

def square(x):


'Calculates the square of the number x.'

??? return x*x

print(square(2))

【結果】

4

【示例】

def square(x):


'Calculates the square of the number x.'

??? return x*x

print(square(2))

print(square.__doc__) #這樣對文檔字符串進行訪問,__doc__是函數屬性。第7章會介紹更多關于屬性的知識,雙下劃線表示它是個特殊屬性。這類特殊和"魔法"屬性在第9章中介紹

#

內建的help函數是非常有用的。在交互式解釋器中使用它,可以得到關于函數,包括它的文檔字符串的信息:print(help(square)) #第10章對help函數進行討論

【結果】

4

Calculates the square of the number x.

Help on function square in module __main__:


square(x)

??? Calculates the square of thenumber x.


None

6.3.3 并非真正函數的函數

數學意義上的函數,總在計算其參數后返回點什么。Python的有些函數并不返回任何東西。但是Python的函數就是函數,即便它從學術上講并不是函數。沒有return語句,或者return后沒有跟任何值的函數不返回值

【示例】

def test():


print('This is printed')


return

??? print('This is not')

x =test()

#可以看到,第2個return語句被跳過了(類似于循環中的break語句)

#

但是如果test不返回任何值,那么x又引用什么呢?print(x)

#好熟悉的None,所以所有的函數的確都返回了東西:當不需要它們返回值的時候,它們就返回None。看來,“有些函數并不是真正的函數”的說法有些不公平了。

#

不要被默認的行為所迷惑,當調用者期待一個序列的時候,就不會意外地返回None。

【結果】

This is printed

None

6.4 參數魔法

函數用起來很簡單,創建起來也不復雜。但函數參數的用法就有時有些不可思議了。

6.4.1 值從哪里來

寫在def語句中函數名后面的變量通常叫做函數的形式參數,而調用函數的時候提供的值是實際參數(值)。

6.4.2 能改變參數么

【示例】

#函數通過它的參數獲得一系列值。那參數只是變量而已,所以它們的行為和預想的一樣。在函數內為參數賦予新值不會改變外部任何變量的值:def try_to_change(n):


n = 'Mr. Gumby'

name = 'Mrs. Entity'

try_to_change(name)

print(name) #可以看出,該函數沒有改變變量name的值

#

在try_to_change函數的內部,參數n獲得了新值,但是它沒有影響到name變量。n實際上是個完全不同的變量,具體的工作方式類似于下面:name = "Mrs. Entity"

n = name #這等于傳遞參數n = 'Mr. Gumby'

print(name)

#結果是顯而易見的,當變量n改變的時候,變量name不變。同樣,當在函數內部把參數重綁定(賦值)的時候,函數外部的變量不會受到影響。

#

注:參數存儲在局部作用域內

【結果】

Mrs. Entity

Mrs. Entity

【示例】

#字符串(以及數字和元組)是不可變的,即無法修改(也就是說只能用新的值覆蓋)。但是:如果將可變的數據結構如列表用作參數的時候會發生什么:def change(n): #關鍵看實參是什么,實參是字符串,則不能改變,實參是列表,則能改變

??? n[0] = 'Mr. Gumby'

names = ['Mrs. Entity','Mrs.

Thing']

change(names)

print(names)

#本例中,參數被改變了。這就是本例和前面例子中至關重要的區別。前面的例子中,局部變量被賦予了新值,但是這個例子中變量names所綁定的列表的確改變了。有些奇怪了吧?

#

下面進行模仿names = ['Mrs. Entity','Mrs.

Thing']

n = names

#再來一次,模擬傳參行為n[0] = 'Mr. Gumby' #改變列表names

【結果】

['Mr. Gumby', 'Mrs. Thing']

【示例】

#這種情況在前面已經出現很多次了。當兩個變量同時引用一個列表的時候,它們的確是同時引用一個列表。如果想避免出現這種情況,可以復制一個列表的副本。在序列中做切片的時候,返回的切片總是一個副本。names =['Mrs. Entity','Mrs.

Thing']

n = names[:]

#使用切片進行復制(這個相當于字典中的深復制)

#

如果現在改變n,則不會影響到namesn[0] = 'Mr. Gumby'

print(n)

print(names)

【結果】

['Mr. Gumby', 'Mrs. Thing']

['Mrs. Entity', 'Mrs. Thing']

【示例】

#這種情況在前面已經出現很多次了。當兩個變量同時引用一個列表的時候,它們的確是同時引用一個列表。如果想避免出現這種情況,可以復制一個列表的副本。在序列中做切片的時候,返回的切片總是一個副本。names =['Mrs. Entity','Mrs.

Thing']

n = names[:]

#使用切片進行復制(這個相當于字典中的深復制)

#

如果現在改變n,則不會影響到namesn[0] = 'Mr. Gumby'

print(n) #n變了print(names) #names沒變

#

再用change試一下def change(n): #關鍵看實參是什么,實參是字符串,則不能改變,實參是列表,則能改變

??? n[0] = 'Mr. Gumby'

change(names[:]) #使用這種方式,傳遞進去的是一個串,如果形參是names,則傳遞進去的是一個列表print(names)

【結果】

['Mr. Gumby', 'Mrs. Thing']

['Mrs. Entity', 'Mrs. Thing']

['Mrs. Entity', 'Mrs. Thing']

[if !supportLists]1.? [endif]為什么我們要修改參數

使用函數改變數據結構(比如列表或字典)是將程序抽象化的好方法。假設需要編寫一個存儲名字并且能用名字、中間名或姓查找聯系人的程序,可以使用下面的數據結構:

【示例】

storage= {}

storage[

'first'] = {}

storage[

'middle'] = {}

storage[

'last'] = {}

#storage這個數據結構的存儲方式是帶有3個鍵“first”、“middle”和“last”的字典。每個鍵的下面都又存儲一個字典。子字典中,可以使用名字(名字、中間名或姓)作為鍵,插入聯系人列表作為值。me = 'Magnus Lie Hetland'

storage['first']['Magnus'] = [me] #Magnus是key,me是value(說明value是一個列表,可以使用append方法進行追加元素)storage['middle']['Lie'] =[me]

storage[

'last']['Hetland'] = [me]

#每個鍵下面都存儲了一個以人名組成的列表。本例表中,人名只有“我”。如果想得到所有注冊的中間名為Lie的人,可以像下面這樣做:print(storage['middle']['Lie'])

#將人名加入到列表中的步驟有些枯燥乏味,尤其是要加入很多姓名相同的人時,因為要擴展已經存儲了哪些名字的列表。例如,下面加入我姐姐的名字,而且假設不知道數據庫中已經存儲了什么:my_sister = 'Anne Lie Hetland'

storage['first'].setdefault('Anne',[]).append(my_sister)#Anne是key,my_sister是valuestorage['middle'].setdefault('Lie',[]).append(my_sister)

storage[

'last'].setdefault('Hetland',[]).append(my_sister)

print(storage['middle']['Lie'])

【結果】

['Magnus Lie Hetland']

['Magnus Lie Hetland', 'Anne Lie Hetland']

【代碼】

#如果要寫個大程序來這樣更新列表,那么很顯然程序很快就會變得臃腫且笨拙不堪了。抽象的要點就是隱藏更新時的繁瑣的細節,這個過程可以使用函數來實現。def init(data): #data本身是個字典

??? data['first'] = {}

??? data[

'middle'] = {}

??? data[

'last'] = {}

#使用的方法如下:storage = {}

init(storage)

#print(storage)

#可以看到,函數包辦了初始化工作,讓代碼更易讀

#

在編寫存儲名字的函數前,先寫個獲得名字的函數def lookup(data,label,name):


return data[label].get(name)

#標簽(比如“middle")以及名字(比如“Lie”)可以作為參數提供給lookup函數使用,這樣會獲得包含全名的列表

#

注:返回的列表和存儲在數據結構中的列表是相同的,所以如果列表修改了,那么也會影響數據結構def store(data,full_name):

??? names = full_name.split()

#得到的是一個列表['','','',...]

??? if len(names)

== 2:

??????? names.insert(

1,'') #在names列表的下標為1的位置處插入

??? labels = 'first','middle','last'#是一個元組

??? for label,name inzip(labels,names):

??????? people = lookup(data,label,name)


if people:

??????????? people.append(full_name)


else:

??????????? data[label][name] =[full_name]

#如果原先沒有值,則賦值

#

(1)通過參數data和full_name進入函數,這兩個參數被設置為函數在外部獲得的一些值。

#

(2)通過拆分full_name,得到一個叫做names的列表

#

(3)如果names的長度為2(只有首名和末名),那么插入一個空字符串作為中間名

#

(4)將字符串“first”、“middle”和“last”作為元組存儲在labels中(也可以使用列表,這里只是為了方便而去掉括號)

#

(5)使用zip函數聯合標簽和名字,對于每一個(label,name)對,進行以下處理:

??? #

①獲得屬于給定標簽和名字的列表

??? #

②將full_name添加到列表中,或者插入一個需要更新的新列表MyNames = {}

init(MyNames)

store(MyNames,

'Magnus Lie

Hetland')

print(lookup(MyNames,'middle','Lie'))

#好像可以工作,再試試store(MyNames,'Robin Hood')

store(MyNames,

'Robin Locksley')

print(lookup(MyNames,'first','Robin'))

store(MyNames,

'Mr. Gumby')

print(lookup(MyNames,'middle',''))

#可看到,如果某些人的名字、中間名或姓相同,那么結果中會包含所有這些人的信息。

#

這類程序很適合進行面向對象程序設計

【結果】

['Magnus Lie Hetland']

['Robin Hood', 'Robin Locksley']

['Robin Hood', 'Robin Locksley', 'Mr. Gumby']

[if !supportLists]2.? [endif]如果我的參數不可變呢

在某些語言(如C++,Pascal和Ada)中,重綁定參數并且使這些改變到函數外的變量是很平常的事情。但在Python中,這是不可能的:函數只能修改參數對象本身。但是如果你的參數不可變—比如是數字—又該怎么辦呢?

不好意思,沒有辦法。這個時候應該從函數中返回所有需要的值(如果值多余一個的話就以元組的形式返回)。

??1???N??

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。