python函數作用域與閉包

函數的定義

在python中,是用def來創建一個函數,實際上def只是完成了一個類似與賦值的操作———把一個函數對象賦值給一個變量名,還記得我們之前說過在python中變量名只是一個標識符,相當於起到了一個指針的作用,它沒有類型(明確這一點是很重要的),又因爲python中的一切皆對象,函數當然也不例外,所以,函數被創建後就可以賦值給任意的變量名,也可以作爲參數傳遞給另外一個函數,也可以作爲函數的返回值。下面是相應的代碼演示
函數賦值給任意變量名:
這裏寫圖片描述
函數作爲參數傳遞:

def fun(test):
    print 'Test will execute'
    test()
def test():
    print 'hello,I am test'

fun(test)

這裏寫圖片描述
函數對象作爲返回值:

def test():
    def fun():
        print 'I am fun'
    return fun

a = test()
a()

這裏寫圖片描述

函數作用域

python中有三種(或四種)域作用域相關的作用域。本地變量、(外層函數的本地變量)、全局變量、內建變量
本地變量就是在一個函數內部的變量,全局變量就是不在特定的函數內的,內建變量比較特殊,它是python在開發時就被設計好的一些變量,我們可以通過builtins模塊查看。
這裏寫圖片描述
可以看到,其實這些內建的變量就是寫進了builtins這個文件裏而已,但是這個文件裏沒有寫builtins,所以我們需要導入builtins模塊,才能查看它。
這裏還有一個奇怪的變量,我把它單獨拿出來說————–外層函數的本地變量,它是伴隨着函數嵌套出現的。我們舉一個例子來說明:

def test():
    x = 1
    def fun():
        y = 2

上面的x就是所謂的外層函數的本地變量,當然它是相對於內層函數fun而言的,它也是本地變量(test的),但是它所處的作用域又不同於fun中的作用域,所以,如果現在fun中再創建一個x變量,他們是不衝突的。

LEGB規則

談完了函數的作用域,我們就來談一談python中變量名的解析規則。對於一個def語句:

  • 變量名分爲三個作用域查找:首先是本地(L),之後是函數內(E)(如果有的話),之後是全局(G),最後是內置(B)
  • 在默認情況下,變量名賦值會創建或改變本地變量

    LEGB圖示:
    這裏寫圖片描述
    因爲變量名賦值會創建本地變量,所以我們在函數內部想要改變全局變量的值的時候就不能直接給它賦值了(不考慮全局變量作爲參數傳遞進函數),必須要用到global語句來聲明這是一個全局變量:

#! /usr/bin/env python
#-*- coding:utf-8 -*-

x = 1
def test():
    global x #在函數內聲明x爲全局變量
    x = 2

test()
print 'x=%d'%x

#結果:x=2

global用於聲明一個變量是全局變量。但是有一點小細節需要注意,當全局變量是一個可變對象時,例如一個列表,我們可以直接在函數內部對它進行修改,而不是賦值

a = [1,2,3]
def test():
    a.append(4)
print a
test()
#結果:[1,2,3,4]

這裏時對可變對象在原處的修改,而不是賦值!!

閉包

首先還得從基本概念說起,什麼是閉包呢?來看下維基上的解釋:
在計算機科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認爲閉包是由函數和與其相關的引用環境組合而成的實體。閉包在運行時可以有多個實例,不同的引用環境和相同的函數組合可以產生不同的實例。


上面提到了兩個關鍵的地方: 自由變量 和 函數, 這兩個關鍵稍後再說。還是得在贅述下“閉包”的意思,望文知意,可以形象的把它理解爲一個封閉的包裹,這個包裹就是一個函數,當然還有函數內部對應的邏輯,包裹裏面的東西就是自由變量,自由變量可以在隨着包裹到處遊蕩。當然還得有個前提,這個包裹是被創建出來的。
在通過Python的語言介紹一下,一個閉包就是你調用了一個函數A,這個函數A返回了一個函數B給你。這個返回的函數B就叫做閉包。你在調用函數A的時候傳遞的參數就是自由變量。
舉個栗子:

def func(name):
    def inner_func(age):
        print 'name:', name, 'age:', age
    return inner_func

bb = func('the5fire')
bb(26)  # >>> name: the5fire age: 26

這裏面調用func的時候就產生了一個閉包——inner_func,並且該閉包持有自由變量——name,因此這也意味着,當函數func的生命週期結束之後,name這個變量依然存在,因爲它被閉包引用了,所以不會被回收。

另外再說一點,閉包並不是Python中特有的概念,所有把函數做爲一等公民的語言均有閉包的概念。不過像Java這樣以class爲一等公民的語言中也可以使用閉包,只是它得用類或接口來實現。

閉包與裝飾器

其實裝飾器就是閉包的一種應用,下面來引用廖老師教程中的一個例子:

def log(func):
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

上面的log函數就是一個裝飾器。它接受一個函數參數,我們使用python的@語法,把裝飾器放在函數的定義處,這樣當執行now函數的時候,就會自動執行log函數。

@log
def now():
    print '2013-12-25'
now()

以上程序的輸出結果爲:

call now():
2013-12-25

閉包的作用

閉包的最大特點是可以將父函數的變量與內部函數綁定,並返回綁定變量後的函數(也即閉包),此時即便生成閉包的環境(父函數)已經釋放,閉包仍然存在,這個過程很像類(父函數)生成實例(閉包),不同的是父函數只在調用時執行,執行完畢後其環境就會釋放,而類則在文件執行時創建,一般程序執行完畢後作用域才釋放,因此對一些需要重用的功能且不足以定義爲類的行爲,使用閉包會比使用類佔用更少的資源,且更輕巧靈活。


這裏寫圖片描述
關注web安全

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章