日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

python之抽象一

發(fā)布時間:2025/6/15 63 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python之抽象一 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

6.1 懶惰即美德

假設(shè)我們編寫了一小段代碼來計算斐波那契數(shù)列:

fibs = [0,1]

for i in range(8):

fibs.append(fibs[-2] + fibs[-1])


fibs = [0,1]

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

for i in range(num-2)

fibs.append(fibs[-2] + fibs[-1])

print fibs



抽象后

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

print fibs(num)


6.2 抽象和結(jié)構(gòu)

page = download_page()

freqs = computer_frequencies(page)

for word,freq in freqs:

print word,freq


6.3 創(chuàng)建函數(shù)

函數(shù)是可以調(diào)用,它執(zhí)行某種行為并且返回一個值。一般來說,內(nèi)建的callable函數(shù)可以用來判斷函數(shù)是否可調(diào)用:

>>> import math

>>> x = 1

>>> y = math.sqrt

>>> callable(x)

False

>>> callable(y)

True

>>>?


def hello(name):

return 'hello,' + name + '!'

創(chuàng)建一個名為hello的函數(shù),他可以返回一個將輸入的參數(shù)作為名字的問候語。可以像使用內(nèi)建函數(shù)一樣使用它:

>>>print hello(‘world’)

hello,world


斐波那契函數(shù)

def fibs(num)

resule = [0,1]

for i in range(num-2):

result.append(result[-2] + result[-1])

return result


6.3.1 記錄函數(shù)

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

def square(x):

'Calculates the square of the number x.'

return x*x

文檔字符串可以按如下方式訪問:

>>>square._doc_

'Calculates the square of the number x.'


_doc_是函數(shù)屬性

內(nèi)建的help函數(shù)是非常有用的。在交互解釋器中使用它,就可以得到關(guān)于函數(shù),包括它的文檔字符串的信息

>>>help(square)


6.3.2 并非真正函數(shù)的函數(shù)

數(shù)學(xué)意義上的函數(shù),總在計算其參數(shù)后返回點什么。python的有些函數(shù)卻并不返回任何東西。在其他語言中,這類函數(shù)可能有其他名字。但是python的函數(shù)就是函數(shù),即便它從學(xué)術(shù)上并不是函數(shù)。沒有return語句,或者雖有return語句但return后邊沒有跟任何值得函數(shù)不返回值:

def test():

print 'this is printed'

return ? ? ? ? ? 這里的return函數(shù)只起到結(jié)束函數(shù)的作用

print 'this is not'


>>>x = test()

This is printed


可以看到,第2個print語句被跳過了,但是如果test不返回任何值,那么x又引用什么呢?

>>>x

>>>

沒東西,在仔細(xì)看看

>>>print x

None

所以所有的函數(shù)都返回了東西,:當(dāng)不需要他們返回值得時候,它們就返回None。


千萬不要被默認(rèn)行為所迷惑。如果在if語句內(nèi)返回值,那么要確保其他分支也有返回值,這樣一來當(dāng)調(diào)用者期待一個序列的時候,就不會意外的返回None。


6.4 參數(shù)

6.4.1 值從哪里來

寫在def語句中函數(shù)名后面的變量通常叫做函數(shù)的形式參數(shù),而調(diào)用函數(shù)的時提供的值是實際參數(shù),或者成為參數(shù)。


6.4.2 我能改變參數(shù)么

在函數(shù)內(nèi)內(nèi)為參數(shù)賦予新值不會改變外部任何變量的值:

>>> def try_to_change(n): n = 'Mr,Gumby'

...?

>>> name = 'Mrs,Entity'

>>> try_to_change(name)

>>> name

'Mrs,Entity'

>>>?


在try_to_change內(nèi),參數(shù)n獲得了新值,但是它沒有影響到name變量。n實際上是個完全不同的變量,具體的工作方式類似于下面這樣:

>>> name = 'Mrs,Entity'

>>> n = name ? ? ? ?#這句的作用基本上等于傳參數(shù)

>>> n = 'Mr.Gumby' ? #在函數(shù)內(nèi)部完成的

>>> name

'Mrs,Entity'

>>>?


結(jié)果是顯而易見的,當(dāng)變量n改變的時候,變量name不變。同樣,當(dāng)在函數(shù)內(nèi)部把參數(shù)重綁定的時候,函數(shù)外的變量不會受到影響。


參數(shù)存儲在局部作用域。


字符串是不可變的,即無法被修改。

>>> def change(n): n[0] = 'Mr.Gumby'

...?

>>> names = ['Mrs.Entity','Mrs.Thing']

>>> change(names)

>>> names

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

>>>?


本例中,參數(shù)被改變了。這就是本例和前面例子中至關(guān)重要的區(qū)別。前面的例子中,局部變量被賦予了新值,但是這個例子中變量names所綁定的列表的確改變了。

>>> names = ['Mrs.Entity','Mrs.Thing']

>>> n = names

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

>>> names

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

>>>?


這類情況在前面已經(jīng)出現(xiàn)了多次。當(dāng)兩個變量同時引用一個列表的時候,他們的確是同時引用一個列表。如果想避免出現(xiàn)這種情況,可以復(fù)制一個列表的副本。當(dāng)在序列中做切片的時候,返回的切片總是一個副本。因此,如果你復(fù)制了整個列表的切片,將會得到一個副本:

>>> names = ['Mrs.Entity','Mrs.Thing']

>>> n = names[:]


現(xiàn)在n和name包含兩個獨立的列表,其值相等:

>>> n is names

False

>>> n == names

True

>>>?


如果現(xiàn)在改變n,則不會影響到names:

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

>>> n

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

>>> names

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

>>>?


再用change試一下:

>>> change(names[:])

>>> names

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

>>>?

現(xiàn)在參數(shù)n包含一個副本,而原始的列表是安全的。


函數(shù)的局部名稱-----包括參數(shù)在內(nèi)-----并不和外面的函數(shù)名稱沖突。


1.為什么我想要修改參數(shù)

使用函數(shù)改變數(shù)據(jù)結(jié)構(gòu)是將程序抽象化的好方法。假設(shè)需要編號一個存儲名字并且能用名字、中間名或姓查找聯(lián)系人的程序,可以使用下面的數(shù)據(jù)結(jié)構(gòu):

storage = {}

storage['first'] = {}

storage['middle'] = {}

storage['last'] = {}


storage這個數(shù)據(jù)結(jié)構(gòu)的存儲方式是帶有3個鍵“first”“middle”和“l(fā)ast”的字典。每個鍵下面都又存儲一個字典。子字典中,可以使用名字作為鍵,插入聯(lián)系人列表作為值。比如要把我自己的名字加入這個數(shù)據(jù)結(jié)構(gòu),可以像下面這么做:

>>> me = 'Magnus Lie Hetland'

>>> storage['first']['Magnus'] = [me]

>>> storage['middle']['Lie'] = [me]

>>> storage['last']['Hetland'] = [me]


每個鍵下面都存儲了一個以人名組成的列表。本例中,列表中只有我。

現(xiàn)在如果想得到所有注冊的中間名為Lie的人,可以像下面這么做:

>>> storage['middle']['Lie']

['Magnus Lie Hetland']

>>>?


將人名加到列表中的步驟有點枯燥乏味,尤其是要加入很多姓名相同的人時,因為需要擴(kuò)展已經(jīng)存儲了那些名字的列表。例如,下面加入我姐姐的名字,而且假設(shè)不知道數(shù)據(jù)庫中已經(jīng)存儲了什么:

>>> storage['first'].setdefault('Anne',[]).append(my_sister)

>>> storage['middle'].setdefault('Lie',[]).append(my_sister)

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

>>> storage['first']['Anne']

['Anne Lie Hetland']

>>> storage['middle']['Lie']

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

>>>?

如果要寫個大程序來這樣更新列表,那么很顯然程序很快就會變得臃腫且笨拙不堪了。

抽象的要點就是隱藏更新時的繁瑣的細(xì)節(jié),這個過程可以用函數(shù)實現(xiàn)。下面的例子就是初始化數(shù)據(jù)結(jié)構(gòu)的函數(shù):

def init(data):

data['first'] = {}

data['middle'] = {}

data['last'] = {}

上面的代碼只是把初始化語句放到了函數(shù)中:

>>> storage = {}

>>> init(storage)

>>> storage

{'middle': {}, 'last': {}, 'first': {}}

可以看到,函數(shù)包辦了初始化的工作。

字典的鍵并沒有具體的順序。


在編寫存儲名字的函數(shù)前,先寫個獲得名字的函數(shù):

def lookup(data,label,name):

return data[label].get(name)

標(biāo)簽(比如middle)以及名字(比如Lie)可以作為參數(shù)提供給lookup函數(shù)使用,這樣會獲得包含全名的列表。換句話說,如果我的名字已經(jīng)存儲了,可以像下面這樣做:

>>> lookup(storage,'middle','Lie')


注意,返回的列表和存儲在數(shù)據(jù)結(jié)構(gòu)中的列表是相同的,所以如果列表被修改了,那么也會影響數(shù)據(jù)結(jié)構(gòu)(沒有查詢到人的時候就問題不大了,因為函數(shù)返回的是None)

def store(data,full_name):

names = full_name.split()

if len(names) == 2: names.insert(1,'')

labels = 'first','middle','last'

for label,name in zip(labels,names):

people = lookup(data,label,name)

if people:

people.append(full_name)

else:

data[label][name] = [full_name]


store函數(shù)執(zhí)行以下步驟:

1.使用參數(shù)data和full_name進(jìn)入函數(shù),這兩個函數(shù)被設(shè)置為函數(shù)在外部獲得的一些值。

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

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

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

5.使用zip函數(shù)聯(lián)合標(biāo)簽和名字,對于每一個(label,name)對,進(jìn)行以下處理:

獲得屬于給定標(biāo)簽和名字的列表。

將full_name添加到列表中,或者插入一個需要的新列表。


來試用一下剛剛實現(xiàn)的程序:

>>> Mynames={}

>>> init(Mynames)

>>> store(Mynames,'Magnus Lie Hetland')

>>> lookup(Mynames,'middle','Lie')

[‘Magnus Lie Hetland’]


好像可以工作:

>>> store(Mynames,'Robin Hood')

>>> store(Mynames,'Robin Locksley')

>>> lookup(Mynames,'first','Robin')

[‘Robin Hood’,‘Robin Locksley’]

>>> store(Mynames,'Mr.Gumby')

>>> lookup(Mynames,'middle','')

[‘Robin Hood’,‘Robin Locksley’,‘Mr。Gumby’]

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


2.如果我的參數(shù)不可變

函數(shù)只能修改參數(shù)對象本身。但是如果你得參數(shù)不可變----比如數(shù)字---又該怎么辦? 這是沒有辦法的,這時候你應(yīng)該從函數(shù)中返回你需要的值(如果值多于一個話就以元組形式返回)。例如,將變量的數(shù)值增1的函數(shù)可以這樣寫:

>>> def inc(x):return x+1

...?

>>> foo=10

>>> foo = inc(foo)

>>> foo

11

>>>?


如果真的想改變參數(shù),那么可以使用一點小技巧,即將值放置在列表中:

>>> def inc(x):x[0] = x[0] + 1

...?

>>> foo = [10]

>>> inc(foo)

>>> foo

[11]

>>>?

這樣就只返回新值,代碼看起來也比較清晰。


6.4.3關(guān)鍵字參數(shù)和默認(rèn)值

目前為止我們所使用的參數(shù)都叫做位置參數(shù),因為它們的位置很重要----事實上比它們的名字更加重要。本節(jié)中引入的這個功能可以回避為止問題,當(dāng)你慢慢習(xí)慣使用這個功能以后,就會發(fā)現(xiàn)程序規(guī)模越大,它們的作用也就越大。

考慮下面的兩個函數(shù):

def hello_1(greeting,name):

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


def hello_2(greeting,name):

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


兩個代碼所實現(xiàn)的是完全一樣的功能,只是參數(shù)名字反過來了:

>>>hello_1('hello','world')

hello,world

>>>hello_2('hello','world')

hello,world


有些時候,參數(shù)的順序是很難記住的,為了讓事情簡單些,可以提供參數(shù)的名字:

>>>hello_1(greeting=‘hello’,name=‘world’)

hello,world!

這樣一來,順序就完全沒影響了:

>>>hello_1(name=‘world’,greeting=‘hello’)

hello,world!

但參數(shù)名和值一定要對應(yīng):

>>>hello_2(greeting='hello',name='world')

world,hello

這類使用參數(shù)提供的參數(shù)叫做關(guān)鍵字參數(shù)。它的作用在于可以明確每個參數(shù)的作用,也就避免了下面這樣的奇怪的函數(shù)調(diào)用

>>>storre('Mr. Brainsample',10,20,13,5)

可以使用

>>>store(patient='Mr. Brainsample',hour=10,minute=20,day=13,mouth=5)

盡管這么做打得字多了些,但是很顯然,每個參數(shù)的含義變得更加清晰,而且就算弄亂了參數(shù)的順序,對程序的功能也沒有任何影響。

關(guān)鍵字參數(shù)最厲害的地方在于可以在函數(shù)中給函數(shù)提供默認(rèn)值:

def hello_3(greeting='hello',name='world'):

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

當(dāng)參數(shù)具有默認(rèn)值的時候,調(diào)用的時候就不用提供參數(shù)了!可以不提供,提供一些或提供所有的參數(shù):

>>>hello_3()

hello,world

>>>hello_3('Greetings')

Greetings,world

>>>hello_3('Greeting','universe')

Greeting,universe

可以看到,位置參數(shù)這個方法不錯-----除了在提供名字的時候就要提供問候語。但是如果只想提供name參數(shù),而讓greeting使用默認(rèn)值該怎么辦呢?

>>>hello_3(name='Gumby')

hello,Gumby

位置和關(guān)鍵字參數(shù)是可以聯(lián)合使用的。把位置參數(shù)放置在前面就可以了,如果不這樣做,解釋器會不知道它們到底是誰(也就是它們應(yīng)該處的位置)。


除非完全清楚程序的功能和參數(shù)的意義,否則應(yīng)該避免混合使用位置參數(shù)和關(guān)鍵字參數(shù)。一般來說,只有在強(qiáng)制要求的參數(shù)個數(shù)比可修改的具有默認(rèn)值的參數(shù)個數(shù)少的時候,才使用上面提到的參數(shù)書寫方法。


例如,hello函數(shù)可能需要名字作為參數(shù),但是也允許用戶自定義名字,問候語和標(biāo)點:

def hello_4(name,greeting=‘hello’,punctuation=‘!’):

print ‘%s,%s%s’ % (greeting,name,punctuation)


函數(shù)的調(diào)用方式很多:

>>>hello_4('Mars')

hello,Mars!

>>>hello_4('Mars','Howdy')

Howdy,Mars!

>>>hello_4('Mars','Howdy','...')

Howdy,Mars...

>>>hello_4('Mars',punctuation='.')

hello,Mars.

>>>hello_4()

Traceback (most recent call last):

? File "<stdin>", line 1, in <module>

如果為name也賦予默認(rèn)值,那么最后一個語句就不會產(chǎn)生異常



6.4.4 收集參數(shù)

有些時候讓用戶提供任意數(shù)量的參數(shù)是很有用的,比如在名字存儲過程中,用戶每次只能存一個名字。如果能像下面這樣存儲多個名字就更好了:

>>>store(data,name1,name2,name3)

用戶可以給函數(shù)提供任意多的參數(shù),實現(xiàn)起來也不難。

試著像下面這樣定義函數(shù):

def print_params(*params):

print params

這里我只指定了一個參數(shù),但是前面加上了個星號。

>>>print_params('Testing')

('Testing',)

?可以看到,結(jié)果作為元組打印出來,因為里面有個逗號。所以在參數(shù)前使用星號就能打印出元組?那么Params中使用多個參數(shù)看看會發(fā)生什么:

>>>print_params(1,2,3)

(1,2,3)

參數(shù)前的星號將所有值放置在同一個元組中,可以說是將這些值收集起來,然后使用。

def print_params_2(title,*params):

print title

print params


>>>print_params_2('Params:',1,2,3)

Params:

(1,2,3)

沒問題,所以星號的意思是收集其余的位置參數(shù)。如果不提供任何供收集的元素,params就是個空元組:

>>>print_params_2('Nothing:')

Nothing:

()



>>>print_params_2('Hmm...',something=42)

會報錯


def print_params_3(**params):

print params

至少解釋器沒有發(fā)牢騷,調(diào)用下

>>>print_params_3(x=1,y=2,z=3)

{'z':3,'x':1,'y':2}


返回的是字典而不是元組。

def print_params_4(x,y,z=3,*pospar,**keypar):

print x,y,z

print pospar

print keypar

和我們期望的結(jié)果別無二致

>>>print_params_4(1,2,3,5,6,7,foo=1,bar=2)

123

(5,6,7)

{'foo':1,'bar':2}


>>>print_params_4(1,2)

1 2 3

()

{}


怎么實現(xiàn)多個名字同時存儲。

def store(data,*full_names):

for full_name in full_names:

names = full_name.split()

if len(names) == 2:names.insert(1,'')

labels = 'first','middle','last'

for label,name in zip(labels,names):

people = lookup(data,label,name)

if people:

people.append(full_name)

else:

data[label][name] = [full_name]



>>> d = {}

>>> init(d)

>>> store(d,'Han Solo')


但是現(xiàn)在可以這樣用

>>>store(d,'Luke Skywalker','Anakin Skywalker')

>>>lookup(d,'last','Skywalker')

['Luke Skywalker','Anakin Skywalker']


6.4.5 反轉(zhuǎn)過程

如何將參數(shù)收集為元組和字典,但是事實上,如果使用*和**的話,也可以執(zhí)行相反的操作。那么函數(shù)收集的逆過程是什么樣?

def add(x,y): return x+y


比如說有包含由兩個要想加的數(shù)字組成的元組:

params = (1,2)

這個過程或多或少有點像我們上一節(jié)中介紹的逆過程。不是要收集參數(shù),而是分配它們在“另一端”。使用*運(yùn)算符就簡單了------不過是在調(diào)用而不是在定義時使用:

>>>add(*params)

3

對于參數(shù)列表來說工作正常,只要擴(kuò)展的部分是最新的就可以。可以使用同樣的技術(shù)來處理字典-----使用雙星號運(yùn)算符。假設(shè)之前定義了hello_3,那么可以這樣使用:

>>>params = {'name':'Sir Robin','greeting':'well met'}

>>>hello_3(**params)

Well met,Sir Robin!


在定義或者調(diào)用函數(shù)時使用星號(或者雙星號)僅傳遞元組或字典,所以可能沒遇到什么麻煩:

>>>def with_stars(**kwds):

print kwds['name'],'is',kwds['age'],'years old'

>>>def without_stars(kwds):

print kwds['name'],'is',kwds['age'],'years old'

>>>args = {'name':'Mr. Gumby','age':42}

>>>with_stars(**args)

Mr. Gumby is 42 years old

>>>without_stars(args)

Mr. Gumby is 42 years old


可以看到,在with_stars中,我在定義和調(diào)用函數(shù)時都使用了星號。而在without_stars中兩處都沒用,但是得到了同樣的效果。所以星號只在定義函數(shù)(允許使用不定數(shù)目的參數(shù))或者調(diào)用(“分割”字典或者序列)時才有用。

使用拼接操作符“傳遞”參數(shù)很有用,因為這樣一來就不用關(guān)心參數(shù)的個數(shù)之類的問題,例如:

def foo(x,y,z,m=0,n=0):

print x,y,z,m,n

def call_foo(*args,**kwds):

print "Calling foo!"

foo(*args,**kwds)


在調(diào)用超類的構(gòu)造函數(shù)時這個方法尤其有用。



6.4.6 練習(xí)使用參數(shù)

有了這么多種提供和接受參數(shù)的方法,很容易犯暈吧!所以讓我們把這些方法放在一起舉個 例子。首先,我定義了一些函數(shù):

def story(**kwds):

return 'Once upon a time,there was a ' \ '%(job)s called %(name)s.' % kwds'

def power(x,y,*others):

if others:

print 'Received redundant parameters:',others

return pow(x,y)

def interval(start,stop=None,step=1):

'Imitates range() for step > 0'

if stop is None:

start,stop = 0,start

result = []

i = start

while i < stop:

result.append(i)

i += step

return result



>>>print story(job='king',name='Gumby')

Once upon a time,there was a king called Gumby.

>>>print story(name='Sir Robin',job='brave knight')

Once upon a time,there was a brave knight called Sir Robin.

>>>params = {'job': 'language','name':'Python'}

>>>print story(**params)

Once upon a time,there was a language called Python.

>>>del params['job']

>>>print story(job='stroke of genius',**params)

Once upon a time,there was a stroke of genius called Python.

>>>power(2,3)

8

>>>power(3,2)

9

>>>power(y=3,x=2)

8

>>>params = (5,) * 2

>>>power(*params)

3125

>>>power(3,3,'hello,world')

Received redundant parameters:('hello,world',)

27

>>>interval(10)

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

>>>interval(1,5)

[1,2,3,4]

>>>interval(3,12,4)

[3,7,11]

>>>power(*interval(3,7))

Received redundant parameters:(5,6)

81


?

6.5 作用域

內(nèi)建的vars函數(shù)可以返回這個字典:

>>>x = 1

>>>scope = vars()

>>>scope['x']

1

>>>scope['x'] += 1

>>>x

2


一般來說,vars所返回的字典是不能修改的,因為根據(jù)官方的Python文檔的說法,結(jié)果是未定義的,換句話說,可能得不到想要的結(jié)果。


這類“不可見字典”叫做命名空間或者作用域。那么到底有多少個命名空間?除了全局作用域外,每個函數(shù)調(diào)用都會創(chuàng)建一個新的作用域:

>>>def foo():x = 42

>>>x = 1

>>>foo()

>>>x

1


這里的foo函數(shù)改變了變量x,但是在最后的時候,x并沒有變。這是因為當(dāng)調(diào)用foo的時候,新的命名空間就被創(chuàng)建了,它作用于foo內(nèi)的代碼塊。賦值語句x=42只在內(nèi)部作用域(局部命名空間)起作用,所以它并不影響外部作用域的x。函數(shù)內(nèi)的變量被稱為局部變量。參數(shù)的工作元素類似于局部變量,所以用全局變量的名字作為參數(shù)名并沒有問題。


>>>def output(x):print x

>>> x = 1

>>>y = 2

>>>output(y)

2


但是如果需要在函數(shù)內(nèi)部訪問全局變量怎么辦?而且只想讀取變量的值(也就是說不想重綁定變量),一般來說是沒有問題的:

>>>def combine(parameter):print parameter + external

>>>external = 'berry'

>>>combine('Shrub')

Shrubberry


像這樣使用全局變量是誘發(fā)錯誤的引發(fā)原因。慎重使用全局變量。


屏蔽的問題

讀取全局變量一般來說并不是問題,但是還是有個會出問題的事情。如果局部變量或者參數(shù)的名字和想要訪問的全局變量名相同的話,就不能直接訪問了。全局變量會被局部變量屏蔽。

如果的確需要的話,可以使用globals函數(shù)獲取全局變量值,該函數(shù)的近親是vars,take返回全局變量的字典(locals返回局部變量的字典)。例如,如果前例中有個叫做parameter的全局變量,那么就不能再combine函數(shù)內(nèi)部訪問該變量,因為你有一個與之同名的參數(shù)。必要時,能使用globals()['parameter']獲取:

>>>def combine(parameter):

print parameter + globals()['parameter']

>>>parameter = 'berry'

>>>combine(Shrub)

Shrubberry


接下來討論重綁定全局變量(使變量引用其他新值)。如果在函數(shù)內(nèi)部將值賦予一個變量,它自動成為局部變量----除非告知Python將其聲明為全局變量。那么怎么才能告訴Python這是一個全局變量呢?

>>>x=1

>>>def change_global()

global x

x = x+1

>>>change_global()

>>>x

2


嵌套作用域

Python的函數(shù)是可以嵌套的,也就是說可以將一個函數(shù)放在另一個里面。下面是一個例子 :

def foo()

def bar():

print "hello,world"

bar()

嵌套一般來說并不是那么有用,但它又一個很突出的應(yīng)用,例如需要用一個函數(shù)“創(chuàng)建”另一個,也就意味著可以像下面這樣書寫函數(shù):

def multiplier(factor):

def multiplyByFactor(number):

return number*factor

return multiplyByFactor

一個函數(shù)位于另外一個里面,外層函數(shù)返回里層函數(shù)。也就是說函數(shù)本身被返回了----但并沒有被調(diào)用。重要的是返回的函數(shù)還可以訪問它的定義所在的作用域,換句話說,它帶著它的環(huán)境和相關(guān)的局部變量。

每次調(diào)用外層函數(shù),它內(nèi)部的函數(shù)都被重新綁定,factor變量每次都有一個新的值。由于Python的嵌套作用域,來自外部作用域的這個變量,稍后會被內(nèi)層函數(shù)訪問。例如:

>>>double = multiplier(2)

>>>double(5)

10

>>>triple = multiplier(3)

>>>triple(3)

9

>>>multiplier(5)(4)

20

類似multiplyByFactor函數(shù)存儲于封閉作用域的行為叫做閉包

外部作用域的變量一般來說是不能進(jìn)行重新綁定的。但是Python3.0中,nonlocal關(guān)鍵字被引入。它和global關(guān)鍵字的使用方式類似,可以讓用戶對外部作用域的變量進(jìn)行賦值。


6.6 遞歸

遞歸的定義包括它們自身定義內(nèi)容的引用。由于每個人對遞歸的掌握程度不同,它可能會讓人大傷腦筋。

def recursion():

return recursion()

上述遞歸叫做無窮遞歸。有用的遞歸函數(shù)包含以下幾部分:

當(dāng)函數(shù)直接返回值時有基本實例

遞歸實例,包括一個或者多個問題最小部分的遞歸調(diào)用

這里的關(guān)鍵就是將問題分解為小部分,遞歸不能永遠(yuǎn)繼續(xù)下去,因為它總是以最小可能性問題結(jié)束,而這些問題又存儲在基本實例中。

所以讓函數(shù)調(diào)用自身。但是怎么實現(xiàn)呢?


6.6.1 兩個經(jīng)典:階乘和冪

首先,假設(shè)想要計算數(shù)n的階乘。n的階乘定義為n*(n-1)*(n-2)*。。。*1.很多數(shù)學(xué)應(yīng)用中都會用到它。

def factorial(n):

result = n

for i in range(1,n):

result *= i

return result


這個方法可行而且很容易實現(xiàn)。它的過程主要是;首先將result賦到n上,然后result依次與1到n-1的數(shù)相乘,最后返回結(jié)果。 階乘數(shù)學(xué)定義:

1的階乘是1

大于1的數(shù)n的階乘是n乘n-1的階乘


可以看到,這個定義完全符合剛才所介紹的遞歸的兩個條件。

現(xiàn)在考慮如何定義實現(xiàn)為函數(shù)。理解定義本身后,實現(xiàn)其實很簡單:

def ?factorial(n):

if n==1:

return 1

else:

return n * factorial(n-1)

這就是定義的直接實現(xiàn)。只要記住函數(shù)調(diào)用factorial(n)是和調(diào)用factorial(n-1)不同的實體就行。


假設(shè)需要計算冪,就像內(nèi)建的pow函數(shù)或者**運(yùn)算符一樣。可以用很多種方法定義一個數(shù)的冪。先看一個簡單的例子:power(x,n) ?(x為n的冪次) 是x自乘n-1次的結(jié)果。所以power(2,3) 是2乘以自身3次:2*2*2=8

實現(xiàn)很簡單:

def power(x,n):

result = 1

for i in range(n):

result *= x

return result

接下來該變成遞歸版本:

對于任意數(shù)字x來說,power(x,0)是1

對于任何大于0的數(shù)來說,power(x,n)是x乘以(x,n-1)的結(jié)果。

def pwoer(x,n):

if n == 0:

return 1

else:

return x * power(x,n-1)



6.6.2 另外一個經(jīng)典:二元查找

def search(sequence,number,lower,upper):

if lower == upper:

assert number == sequence[upper]

return upper

else:

middle = (lower + upper) // 2

if number > sequence[middle]:

return search(sequence,number,middle+1,upper)

else:

return search(sequence,number,lower,middle)

完全符合定義。如果lower == upper,那么返回upper,也就是上限。注意,程序設(shè)計(斷言)所查找的數(shù)字一定會被找到(number == sequence[upper])。如果沒有到達(dá)基本實例的話,先找到middle,檢查數(shù)字是在左邊還是在右邊,然后使用新的上下限繼續(xù)調(diào)用遞歸過程。也可以將限制設(shè)為可選以方便用。只要在函數(shù)定義的開始部分加入下面的條件語句即可:

def search(sequence,number,lower=0,upper=None):

if upper is None:upper = len(sequence)-1


現(xiàn)在如果不提供限制,程序會自動設(shè)定查找范圍為整個序列,看看行不行:

>>>seq = [34,67,8,123,4,100,95]

>>>seq.sort()

>>>seq

[4,8,34,67,95,100,123]

>>>search(seq,34)

2

>>search(seq,100)

5


但不必這么麻煩,一則可以直接使用列表方法index,如果想要自己實現(xiàn)的話,只要從程序的開始處循環(huán)迭代直到找到數(shù)字就行了。

當(dāng)然可以,使用index沒問題了,但是只使用循環(huán)可能效率有點低,剛才說過查找100內(nèi)的一個數(shù),只需要7個問題即可。用循環(huán)的話,在最糟糕的情況下要問100個問題。

標(biāo)準(zhǔn)庫中的bisect模塊可以非常有效的實現(xiàn)二元查找。


可以使用map函數(shù)將序列中的元素全部傳遞給一個函數(shù):

>>>map(str,range(10))

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


filter函數(shù)可以基于一個返回布爾值的函數(shù)對元素進(jìn)行過濾。

>>>def func(x):

return x.isalnum()

>>>seq = ["foo","x41","?!","***"]

>>>filter(func,seq)

['foo','x41']


本例中,使用列表推導(dǎo)式可以不用專門定義一個函數(shù):

>>>[x for x in seq if x.isalnum()]

['foo','x41']


事實上,還有個叫l(wèi)ambda表達(dá)式的特性,可以創(chuàng)建短小的函數(shù)。

>>>filter(lambda x: x.isalnum(),seq)

['foo','x41']


reduce函數(shù)一般來說不能輕松被列表推導(dǎo)式替代,但是通常用不到這個功能。它會將序列的前兩個元素與給定的函數(shù)聯(lián)合使用,并且將它們的返回值和第三個元素繼續(xù)聯(lián)合使用,直到整個序列都處理完畢,并且得到一個最終結(jié)果。例如,需要計算一個序列的數(shù)字的和,可以使用reduce函數(shù)加上lambda x,y:x+y (繼續(xù)使用相同的數(shù)字):


>>>numbers = [72,101,108,108,111,44,32,119,111,114,108,100,33]

>>>reduce(lambda x,y: x+y,numbers)

1161


轉(zhuǎn)載于:https://blog.51cto.com/pankuo/1661440

總結(jié)

以上是生活随笔為你收集整理的python之抽象一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。