天天看點

python筆記10 - 函數傳參,嵌套,作用域2017/10/14函數的傳參,嵌套,作用域變量

2017/10/14

函數的傳參,嵌套,作用域變量

1.可變/不可變類型 的實參的參數傳遞的差別

可變:

    清單list,集合set,字典dict

不可變:

    frozenset,tuple(隻可以進行索引查找,不可以進行索引指派),str,float,int...

思考:函數隻能通過傳回值傳回資料嗎?

執行個體1(可變類型資料):

def fn(x):

    x.append(10)

l = []

fn(l)

print(l)

>>>[10]    

#改變了 l 的清單,但實際上沒有return語句,def内部也沒有print語句!!l是如何在定義函數fn外拿到資料的呢?

解答:清單,集合,字典是可變的資料類型,可以直接從定義函數中傳回已經變化的語句,而不需要進行傳回return處理

執行個體2(可變類型資料):

def fn(x):

    x['name'] = 'tarena'

d = {}

fn(d)

print(d)

>>>{'name': 'tarena'}

#原理同上

執行個體3(不可變類型資料):

def fn(x):

    x[1] = 2.2

t = (1,2,3)

print(t)

fn(t)           #元組内部資料不可變,不可以進行索引指派,隻可以進行索引查找        

print(t)

>>>TypeError: 'tuple' object does not support item assignment

執行個體4:

def fn(x):

    x=(4,5,6)

t = (1,2,3)

fn(t)

print(t)

>>>(1, 2, 3)   #元組本身資料就更改不了,元組可查不可變

執行個體5:

def fn(x):           #剛開始,x的确綁定的是清單[1,2,3]

    x = [4,5,6]      #到這裡,x改變了綁定關系,這句話的意思的實質是建立清單[4,5,6]并進行綁定,而不是對原來的裂變進行變值處理

l = [1,2,3]         

fn(l)

print(l)

>>>[1, 2, 3]         #調用fn時,系統建立一個單獨的空間

執行個體6:

def fn(x):

    x.append(4)      #這裡的x依舊綁定的是清單[1,2,3],是以可以綁定原來的清單值

l = [1,2,3]

fn(l)

print(l)

>>>[1, 2, 3, 4]      

執行個體7:

def fn(x):

    x = [1,2,3]      #這裡x建立并綁定的清單[1,2,3]與l建立并綁定的清單[1,2,3]是兩個完全無關的清單,分别位于兩個不同的記憶體空間

    x.append(4)      #在x綁定的清單中改變資料,不會影響l綁定的清單資料

l = [1,2,3]

fn(l)

print(l)

>>>[1, 2, 3]         

總結:

對于以上定義函數,如果了解為将l替換x是錯誤的(記得新常提的綁定關系,而不是指派關系),實際上x 和 l綁定的是某個空間中的某個清單,可能是同空間的同一個清單,也可能是不同空間的清單,資料修改互不相關

差別:

    不可變類型的資料作為函數參數傳入時,函數内部不會改變變量的原資料值,是安全的;

    可變資料類型的資料作為參數傳入時,函數内部可以改變原資料,多用來傳回更多資料。

2.函數嵌套:

    一個函數語句裡用def語句來建立其他函數的情況

函數變量:

函數名是變量,它在建立函數時綁定一個函數

執行個體:

def fn():

    print('hello xiao')

f1 = fn

f1()                #等同于調用函數fn()

執行個體8-函數嵌套:

def fn_outer(): #外部函數

    print('外部函數被調用')

    def fn_inner():

        print('fn_inner被調用')

    fn_inner()

    fn_inner()

    print('外部函數調用結束')

fn_outer()

>>>

外部函數被調用

fn_inner被調用

fn_inner被調用

外部函數調用結束

執行個體9-命名空間的作用:

def fn_outer(): #外部函數

    print('外部函數被調用')

    def fn_inner():

        print('fn_inner被調用')

    fn_inner()

    fn_inner()

    print('外部函數調用結束')

fn_outer()

fn_inner()     #錯誤,内嵌函數隻存在于函數内部

>>>NameError: name 'fn_inner' is not defined

3.函數作為函數的傳回值:

執行個體10:

def getfn():

    def print_hello():

        print('hello')

    return print_hello    #函數的名稱不加括号,不加括号表示一個變量,不執行語句塊,把整個語句丢過去,但是不執行;如果加括号,表示執行語句塊,調用了函數,并傳回函數執行的結果

fn = getfn()              #加括号?????????????

fn()

print(getfn())            #列印函數

print(fn)

4.函數作為函數的參數傳遞:

執行個體11:

def tab(x,y):

    return '|' + x.center(13) + '|' + y.center(13)+ '|'

def string(x,y):

    return '姓名:' + x + '年齡:' + y

def myprint(fx,x,y):

    s = fx(x,y)

    print(s)

myprint(tab,'tarena','15')     #這裡的tab作為函數進行傳參,先調用myprint函數

myprint(string,'xiaoyu','23')

>>>

|    tarena   |      15     |

姓名:xiaoyu年齡:23

執行個體12:

def goodbye(L):

    for x in L:

        print("再見",x)

def hello(L):

    for x in L:

        print("你好",x)

def operator(fn,L):

    fn(L)

operator(goodbye,['xiaoyu','bantian'])

>>>

再見 xiaoyu

再見 bantian

你好 xiaoyu

你好 bantian

5.全局變量和局部變量

全局變量:定義在函數外部,子產品内部的變量

局部變量:定義在函數内部的變量(包括函數參數)

執行個體13:

v = 100            #此處為全局變量,變量全局作用域

def fn():

    v = 200        #此處為局部變量,變量林臨時使用,隻有在調用函數的時候才使用此變量

    print(v)

fn()               #200

print(v)           #100

>>>

200

100

6.python作用域

    作用域,也叫名字空間,是變量通路的時候查找變量名的範圍空間

執行個體14:

v = 100

def fn():

    print(v)

fn()

>>>100

總結:變量查找空間的順序是先局部作用域,後全局作用域

7.python四個作用域:

局部作用域(函數内) Local                       L

外部嵌套函數作用域   Enclosing function locals   E

函數定義在子產品(檔案)的作用域 Global(module)  G(全局作用域)

python内置子產品的作用域 Builtin(Python)           B

變量名的查找規則:

    在通路變量時,先查找本地變量,然後是包裹此函數的外部函數的函數内部的變量,之後是全局變量,最後是内置變量

    L -> E -> G -> B

在預設情況下,變量名指派會建立或者修改本地變量

執行個體15:

v = 100                          #全局作用域G

def fun1():

    v = 200                      #外部嵌套作用域E

    print('fun1.v=',v)

    def fun2():

        v = 300                  #局部作用域L

        print('fun2.v=',v)

    fun2()

fun1()

print('v=',v)

>>>

fun1.v= 200

fun2.v= 300

v= 100

曾經出現的bug:

v = 100                          #全局作用域G

def fun1():

    v = 200                      #外部嵌套作用域E

    print('fun1.v=',v)

    def fun2():

        v = 300                  #局部作用域L

        print('fun2.v=',v)

    fun2

fun1

print('v=',v)

>>>v=100

分析:fun2   fun1   應該寫為fun2() ,fun1(),沒有括号僅僅表示函數名變量,綁定了一批代碼,不做任何處理和運作

執行個體16:

v = 100                          #全局作用域G

def fun1():

    v = 200                      #外部嵌套作用域E

    print('fun1.v=',v)

    def fun2():

                                 #v = 300  #局部作用域L

        print('fun2.v=',v)

    fun2()

fun1()

print('v=',v)

>>>

fun1.v= 200

fun2.v= 200

v= 100

global語句:

作用:

  告訴解釋執行器,global語句聲明的一個或多個變量,這些變量的作用域為子產品級别的作用域,也稱作全局變量

  對全局聲明(global)的變量指派,将映射到子產品檔案的内部作用域

文法:

  global 變量名1,變量名2,...

執行個體17:

v = 100

def fn():

    global v   #聲明以下的變量是全局的

    v = 200    #建立了全局變量v = 200,解除以往的v綁定的值  

fn()

print(v)

執行個體18:

def fn():

    global v   #聲明以下的變量是全局的

    v = 200

fn()

print(v)

global說明:

1.全局變量如果要在函數内部被指派,則必須經過全局聲明,否則被認為是局部變量

2.全局變量在函數内部不經過聲明就可以直接通路(前提是變量已經存在)

3.不能先聲明局部變量,再用global聲明為全局變量,此做法不符合文法規則

4.global變量清單裡的變量不能出現在此作用域的參數清單裡,for循環控制目标,類定義,函數定義及import導入名字中

執行個體19-bug:

v = 100

def fn():

    v = 200     #建立了一個局部變量v,在聲明為全局是不可以的

    global v    #後聲明全局

fn()

print(v)

執行個體20-bug:

def fn():       #定義了一個局部變量v 後又聲明為全局,這是錯誤的

    global v

    v = 200

fn(111)        #原因在此,已經建立了局部變量

print(v)

nonlocal語句:

作用:

    1.告訴解釋執行器,nonlocal聲明的變量不是局部變量,也不是全局變量,而是外部嵌套函數内的變量

文法:

    nonlocal 變量名1,變量2,.....

執行個體21-bug:

var = 100                    #全局

def outter():               

    var = 200                #外層嵌套

    def inner():

        var += 1             #此行出錯

        print(var)

    inner()

    print('outter.var',var)

outter()

執行個體22-bug:

var = 100                    #全局

def outter():

    var = 200

    def inner():

        nonlocal var         #指定var為外層嵌套函數作用域,表示是inner()之外的,而且是嵌套的

        var += 1             #此行出錯

        print('inner.var=',var)

    inner()

    print('outter.var=',var)

outter()

執行個體23-bug:

var = 100                    #全局

def outter():               

    var = 200                #外

    def inner():

                             #var += 1             #此行出錯

        print(var)

    inner()

    print('outter.var',var)

outter()

說明:

    1.nonlocal語句隻能在被嵌套函數的内部進行使用

    2.通路nonlocal變量将對外部嵌套函數的作用域内的變量進行操作

    3.當有兩層或兩層以上函數嵌套時,通路nonloal變量隻對最近一層的變量進行操作

    4.nonlocal語句的變量清單裡的變量名,不能出現在此作用域的參數清單中

執行個體24-bug:

var = 100

def fn():

    nonlocal var    #不是外部嵌套函數

    var = 200

fn()

執行個體-bug:

var = 100

def fn():

    global var    #不是外部嵌套函數,層數不對(原因是嵌套層數太少)

    var = 200

fn()

執行個體25:

def f1():

    v = 200

    def f2():

        v = 300

        def f3():

            nonlocal v       #對上一層的變量進行重新處理

            v = 400

        f3()

        print('f2.v=',v)

    f2()

    print('f1.v=',v)

f1()

>>>

f2.v= 400

f1.v= 200

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

練習:

1.寫一個函數,在函數内部讀取學生姓名,并存入清單中,通過兩種方式傳回學生姓名資料并列印出來

方式1:通過傳回值傳回資料

方式2:通過參數傳回資料

解:

方式1(傳回值傳回資料):

def read_names():

    L = []

    while 1:

        n = input('please enter students\' names:')

        if n != '0':       

            L.append(n)

        else :

            break                             #逃離最近的循環

    return L

print(read_names())

方式2(參數傳回資料):

def read_names(l):

    while 1:

        n = input('please enter students\' names:')

        if n != '0':       

            l.append(n)

        else :

            break

L = []

read_names(L)

print(L)

新的解法:

def get_names(x=[]):

    L = []

    while True:

        name = input('please enter students\' names:')

        if not name:                #@@@@@@@@@@@@@@@@@@@@  很巧妙的結束循環的方法 ,判斷字元是否為空

            break                   #@@@@@@@@@@@@@@@@@@@@  跳出最近的循環體

        L.append(name)

    x[:] = L           #切片指派     #也可以用x.extend(L) 追加清單

    return L

#方式1

print(get_names())    #傳回值傳遞資料,定義函數中傳回一個值L,這個L清單儲存了所有輸入學生的姓名,實參省略的原因(形參賦予預設值,實參就可以不寫了)

#方式2

n = []

get_names(n)          #參數傳遞資料,n與x綁定了同一個空值清單

print(n)

2.寫一個函數,此函數有一個參數op,如下:

def get_op(op):

   ...

此函數在傳入字元串'加',傳回加操作的函數

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

此函數在傳入字元串'乘',傳回加操作的函數

def myatt(x,y):return x*y

在主程式中程式如下:

a = int(input('輸入第一個數:'))

b = int(input('輸入第一個數:'))

operator = input('請輸入操作方式:')

fn = get_op(operator)

print('結果是:'fn(a,b))

測試用例:

3

2

加<回車>

結果是:5

3

2

乘<回車>

結果是:6

解:

def get_op(op):   

    def myadd(x,y):

        return x+y

    def myatt(x,y):

        return x*y  

    if op == '加':

        return myadd  

    if op == '乘':

        return myatt

a = int(input('輸入第一個數:'))

b = int(input('輸入第一個數:'))

operator = input('請輸入操作方式:')

fn = get_op(operator)

print('結果是:',fn(a,b))

總結:

a = function()        #将函數function運作的結果傳回并指派給a

b = function(x,y)     #将函數function運作的結果傳回并指派給b,形參為a和b

c = function          #函數名function是個變量,它将函數體内的語句塊全部傳給c,此時c和function是綁定同一堆函數語句的函數名變量   

3.給出一個數n,寫一個函數來計算1+2+3+...+n的和,要求用函數來做。

如:print(mysum(100))    #5050

4.給出一個數n,寫一個函數來計算n!(n的階乘)

如:print(myfac(5))      #120

5.給出一個數n,寫一個函數來計算1+2**2+3**3+...n**n的和,注意n給個小點的數。

6.寫函數列印楊輝三角,用清單做,元素逐漸增多。(隻列印6層)

                                    行數n       元素個數

          1                           1            1

        1   1                         2            2

       1  2  1                        3            3

      1  3 3  1                       4            4

     1 4  6  4 1

    1 5 10 10 5 1

解第3題:

def mysum(n):

    s = 0

    for i in range(1,n+1):

        s = s + i

    return s    

a = int(input('please enter a num:'))

print(mysum(a))

解第4題:

def myfc(n):

    s = 1

    for i in range(1,n+1):

        s = s * i

    return s    

a = int(input('please enter a num:'))

print(myfc(a))

解第5題:

def mytt(n):

    s = 0

    for i in range(1,n+1):

        s = s + i**i

    return s    

a = int(input('please enter a num:'))

print(mytt(a))

解第6題:

def tri(n):

    L = []

    i = 1

    s = 0

    while i <= n:

        for a in range(1,n+1):

            s = s +s

            L.append(s)

    return L

a = int(input('please enter a num>=3:'))

print(tri(a))

新解法:

思路:定義函數,計算下一行,輸出本行的資料

def local_line(L,space = 0):

    print(' ' * space,end  ='')

    for x in L:

        print(x,end=' ')

    print()

def next_line(L):

    out_L = []

    if L:

        out_L.append(L[0])

    if len(L)>=2:

        for j in range(len(L)-1):

            out_L.append(L[j]+L[j+1])

    if L:

        out_L.append(L[-1])

    return out_L

L = [1]

for i in range(6):

    local_line(L,6-i-1)    #輸出目前行

    next_line(L)           #計算下一行