天天看點

Python之Function

函數是組織好的,可重複使用的,用來實作單一,或相關聯功能的代碼段。

函數能提高應用的子產品性,和代碼的重複使用率。你已經知道Python提供了許多内置函數,比如print()。但你也可以自己建立函數,這被叫做使用者自定義函數。

函數代碼塊以 def 關鍵詞開頭,後接函數辨別符名稱和圓括号()。

任何傳入參數和自變量必須放在圓括号中間。圓括号之間可以用于定義參數。

函數的第一行語句可以選擇性地使用文檔字元串—用于存放函數說明。

函數内容以冒号起始,并且縮進。

return [表達式] 結束函數,選擇性地傳回一個值給調用方。不帶表達式的return相當于傳回 None。

def fun(形參):

    "内容"

    return True

fun(實參)

定義一個函數隻給了函數一個名稱,指定了函數裡包含的參數,和代碼塊結構。

這個函數的基本結構完成以後,你可以通過另一個函數調用執行,也可以直接從Python提示符執行。

最簡單的例子

def fun():

    print "OK"

fun()

定義函數時,申明的是形式參數簡稱形參,調用函數時,傳遞的參數是實際參數簡稱實參。而形參又可以分為四種

必備參數

關鍵字參數

預設參數

不定長參數

必備參數須以正确的順序傳入函數。調用時的數量必須和聲明時的一樣

def f1(a):

f1()

def f2(a,b)

    print(a+b)

f2(3+4)

傳回結果:OK和7

注:調用必備參數時,必須實參和形參位置對應,否則會報錯。

關鍵字參數和函數調用關系緊密,函數調用使用關鍵字參數來确定傳入的參數值。

使用關鍵字參數允許函數調用時參數的順序與聲明時不一緻,因為 Python 解釋器能夠用參數名比對參數值。

def info(name,age):

    print "Name %s"%name

    print"Age %s"%age

info("flash",18)

info("aa",19)

輸出結果

Name flash

Age 18

Name aa

Age 19

調用函數時,預設參數的值如果沒有傳入,則被認為是預設值。

def info(name,age,sex="male"):

    print"Name %s"%name

    print "Age %s"%age

    print "Sex %s"%sex

info("aa",19,sex="female")

輸出結果對比

Sex male

Sex female

你可能需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數(又稱動态參數),和上述2種參數不同,聲明時不會命名。一種是無名名字參數,另一種是有命名參數。

無命名參數(輸出的是元組)

def add(*args):

    sum = 0

    for i in args:

        sum += i

    print(sum)

add(1,2,3,4,5)

有命名參數(輸出的是字典)

def fun(**kwargs):

    for i in kwargs:

        print "%s:%s"%(i,kwargs[i])

fun(name="flash",age=22)

lambda函數也叫匿名函數,即,函數沒有具體的名稱。

f=lambda 賦的參數:參數要做什麼

然後調用:f(2)

    def f(x):

        return x**2

    print f(4) 

 把函數改寫成:g=lambda x:x**2

print g(4)

若LAMBDA要跟多個參數,要做什麼的參數也要對應

a=range(10)

b=range(10)

g=lambda x,y:x*y,a,b

sorted排序:可以給字典排序

a={'b':4,'a':2,'c':3}

sorted(a.items(),key=lambda x:x[1]) ###items()把字典轉換成tuple

map函數:

  map(func, seq1[, seq2,…]) 

    第一個參數接受一個函數名,後面的參數接受一個或多個可疊代的序列,傳回的是一個集合。 

        Python函數程式設計中的map()函數是将func作用于seq中的每一個元素,并将所有的調用的結果作為一個list傳回。如果func為None,作用同zip()。

map(lambda x:x**2,a)#求a的平方

print map(lambda x: x % 2, range(7))

#使用清單解析>>> print [x % 2for x in range(7)]

[0, 1, 0, 1, 0, 1, 0]

***将元組轉換成list***>>> map(int, (1,2,3))

[1, 2, 3]

***将字元串轉換成list***>>> map(int, '1234')

[1, 2, 3, 4]

***提取字典的key,并将結果存放在一個list中***>>> map(int, {1:2,2:3,3:4})

***字元串轉換成元組,并将結果以清單的形式傳回***>>> map(tuple, 'agdf')

[('a',), ('g',), ('d',), ('f',)]#将小寫轉成大寫def u_to_l (s):return s.upper()print map(u_to_l,'asdfd')    

return語句[表達式]退出函數,選擇性地向調用方傳回一個表達式。不帶參數值的return語句傳回None。

函數在執行過程中隻要遇到return語句,就會可以停止執行并傳回結果,so 也可以了解為 return 語句代表着函數的結束,return 後面的都不會執行的。

如果未在函數中指定return,那這個函數的傳回值為None  

return多個對象,解釋器會把這多個對象組裝成一個元組作為一個一個整體結果輸出

>>> def sayHi2(**kargs):

...     if kargs.has_key('name'):

...         print kargs['name']

...         print kargs

        return kargs['name']

...

>>> result=sayHi2(name='aa',age=29,phone=222222)

aa

{'phone': 222222, 'age': 29, 'name': 'aa'}

>>> print result

None

若在def 中加return kargs['name'],print result就不會是None

一個程式的所有的變量并不是在哪個位置都可以通路的。通路權限決定于這個變量是在哪裡指派的。變量的作用域決定了在哪一部分程式你可以通路哪個特定的變量名稱。

大緻可以分為四種情況

L:local,局部作用域,即函數中定義的變量;

E:enclosing,嵌套的父級函數的局部作用域,即包含此函數的上級函數的局部作用域,但不是全局的;

G:globa,全局變量,就是子產品級别定義的變量;

B:built-in,系統固定子產品裡面的變量,比如int, bytearray等。 搜尋變量的優先級順序依次是:作用域局部>外層作用域>目前子產品中的全局>python

total = 0; # 這是一個全局變量

# 可寫函數說明

def sum( arg1, arg2 ):

   #傳回2個參數的和."

   total = arg1 + arg2; # total在這裡是局部變量.

   print "函數内是局部變量 : ", total

   return total;

#調用sum函數

sum( 10, 20 );

print "函數外是全局變量 : ", total

特性

調用自身函數

有一個結束條件

遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。

使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種資料結構實作的,每當進入一個函數調用,棧就會加一層棧幀,每當函數傳回,棧就會減一層棧幀。由于棧的大小不是無限的,是以,遞歸調用的次數過多,會導緻棧溢出

舉個簡單例子,求5的階乘

def f(i):

    if i == 1:

        return 1

    return i * f(i-1)

print f(5)

小結

使用遞歸函數的優點是邏輯簡單清晰,缺點是過深的調用會導緻棧溢出。

針對尾遞歸優化的語言可以通過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的程式設計語言隻能通過尾遞歸實作循環。

Python标準的解釋器沒有針對尾遞歸做優化,任何遞歸函數都存在棧溢出的問題。

高階函數英文叫Higher-order function。

變量可以指向函數,函數的參數能接收變量,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。

函數名可以進行指派

函數名可以作為函數參數,還可以作為函數的傳回值

舉個例子如

def f(n):

    return n *n

def f1(a,b,f):

    set = f(a) + f(b)

    return set

print f1(1,2,f)<br>傳回值為 5

什麼是閉包函數,看一個簡單的例子

def foo():

    x = 5

    def func():

        print(x)

    return func

foo()()<br>#傳回值是5

我們會想,x = 5是屬于foo函數的局部變量,為什麼可以調用?這就是閉包的閉包的特性。

定義:如果在一個内部函數裡,對在外部作用域(但不是在全局作用域)的變量進行引用,那麼内部函數就被認為是閉包(closure)。

是以對上述例子中,func()就是閉包函數。

通過清單生成式,我們可以直接建立一個清單。但是,受到記憶體限制,清單容量肯定是有限的。而且,建立一個包含100萬個元素的清單,不僅占用很大的存儲空間,如果我們僅僅需要通路前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。

是以,如果清單元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,進而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。

要建立一個generator,有很多種方法。第一種方法很簡單,隻要把一個清單生成式的[]改成(),就建立了一個generator:

L = [x * x for x in range(10)]#list

g = (x * x for x in range(10))#生成器

生成器儲存的是算法,如果調用元素中的隻,最好的辦法就是使用for循環,因為生成器也是一個可疊代的對象。

g = (x * x for x in range(10))

for n in g:

    print(n)

如果算法過去複雜龐大,我們可以使用函數調用來實作。比如斐波那契數列

def fib(max):

    n, a, b = 0, 0, 1

    while n < max-1:

        #print(b)<br>     yield b

        a, b = b, a + b

        n = n + 1

    return

fib(6)<br>#這就是定義generator的另一種方法。如果一個函數定義中包含<code>yield</code>關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator:

兩種方法

next(f()) ----計算出一個值注意:生成器在建立的時候已經決定出計算出值的個數,超出next的次數就會報StopIteration

send("參數") 傳參給yield的變量。注意:第一次不能傳參數f().send(None) ==next(f())

我們已經知道,可以直接作用于for循環的資料類型有以下幾種:

一類是集合資料類型,如list、tuple、dict、set、str等。

一類是generator,包括生成器和帶yield的generator function。

def  sayHi2(**kargs):

      for i in range(10):

            time.sleep(1)

            yield 'Loop',i

result=sayHi2(name='ma',age=19,phone=222222)

print result.next()

......

内部有next方法

内部有iter()方法

這些可以直接作用于for循環的對象統稱為可疊代對象:Iterable。

如何去判斷一個對象是不是一個可疊代的對象,可以使用内置函數進行判斷

>>>from collections import Iterable

>>> isinstance([], Iterable)

True

#如果是傳回True,否傳回False

注:

凡是可作用于for循環的對象都是Iterable類型;

凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;

集合資料類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。

這裡我們也許會問,都已經是可疊代的了,為什麼還不是Iterator?這是因為Python的Iterator對象表示的是一個資料流,Iterator對象可以被next()函數調用并不斷傳回下一個資料,直到沒有資料時抛出StopIteration錯誤。可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,隻能不斷通過next()函數實作按需計算下一個資料,是以Iterator的計算是惰性的,隻有在需要傳回下一個資料時它才會計算。 

Iterator甚至可以表示一個無限大的資料流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。

什麼是裝飾器?

裝飾器本質上是一個函數,該函數用來處理其他函數,它可以讓其他函數在不需要修改代碼的前提下增加額外的功能,裝飾器的傳回值也是一個函數對象。本質就是将原函數封裝到另外一個函數裡面,讓其等于一個新函數,在執行新函數的内容。

首先舉個簡單的例子,在我們事先寫好的許多函數中,如果我們想要添加一個新的功能,不可能往每個函數寫一遍,這是一個不明智的做法。這裡我們就會用到裝飾器。例如

這是一個最簡單的裝飾器。其中@符号是python中表示裝飾器的文法。

當然裝飾器還可以指定參數去更友善的執行例如

import time

def logger(flag = "True"):

    def show_time(f):

        def func():

            start = time.time()

            f()

            end = time.time()

            if flag =="True":

                print("列印日志")

            print(end - start)

        return func

    return show_time

@logger("true")

def foo1():

    print("OK!!!!!!!!!!!!!")

    time.sleep(1)

foo1()

def foo2():

    time.sleep(2)

foo2()

也可以傳入不定長參數做簡單的計算

def show_time(f):

    def func(*args,**kwargs):

        start = time.time()

        f(*args,**kwargs)

        end = time.time()

        print(end - start)

@show_time

def foo1(*args,**kwargs):

    Sum = 0

        Sum += i

    print(Sum)

foo1(1,2,3)

内置函數:

    abs(n)

    divmod(a,b)##趣商的餘數 divmod(8,2)-->(4,0)

    pow(a,b)幂

    round(a)#4舍5入

    sum(list)#LIST的值相加

小結:

在面向對象(OOP)的設計模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承群組合來實作,而Python除了能支援OOP的decorator外,直接從文法層次支援decorator。Python的decorator可以用函數實作,也可以用類實作。

本文轉自 DBAspace 51CTO部落格,原文連結:http://blog.51cto.com/dbaspace/1872828