1. 程式人生 > >python函數(2):函數進階

python函數(2):函數進階

int splay 基本 源文件 tuple [0 執行 內容 理念

昨天說了函數的一些最基本的定義,今天我們繼續研究函數。今天主要研究的是函數的命名空間、作用域、函數名的本質、閉包等等

預習:

技術分享
1、寫函數,用戶傳入修改的文件名,與要修改的內容,執行函數,完成整個文件的批量修改操作
2、寫函數,檢查用戶傳入的對象(字符串、列表、元組)的每一個元素是否含有空內容。
3、寫函數,檢查傳入字典的每一個value的長度,如果大於2,那麽僅保留前兩個長度的內容,並將新內容返回給調用者。
    dic = {"k1": "v1v1", "k2": [11,22,33,44]}
    PS:字典中的value只能是字符串或列表
預習題

一、命名空間

命名空間的本質:存放名字與值的綁定關系

命名空間

局部命名空間

全局命名空間

內置命名空間

*內置命名空間中存放了python解釋器為我們提供的名字:input,print,str,list,tuple...它們都是我們熟悉的,拿過來就可以用的方法。

三種命名空間之間的加載與取值順序:

加載順序:內置命名空間(程序運行前加載)->全局命名空間(程序運行中:從上到下加載)->局部命名空間(程序運行中:調用時才加載)

取值:

在局部調用:局部命名空間->全局命名空間->內置命名空間

技術分享
x = 1
def f(x):
    print(x)

print(10)
在局部使用變量取值

在全局調用:全局命名空間->內置命名空間

技術分享
x = 1
def f(x):
    print(x)

f(10)
print(x)
在全局引用變量x 技術分享
print(max)
在全局引用內置max

二、作用域

為什麽要有作用域的概念:

為了函數內的變量不會影響到全局

作用域就是作用範圍,按照生效範圍可以分為全局作用域和局部作用域。

全局作用域:包含內置名稱空間、全局名稱空間,在整個文件的任意位置都能被引用、全局有效

局部作用域:局部名稱空間,只能在局部範圍內生效

在子程序中定義的變量稱為局部變量,在程序的一開始定義的變量稱為全局變量。

全局變量作用域是整個程序,局部變量作用域是定義該變量的子程序。

當全局變量與局部變量同名時:在定義局部變量的子程序內,局部變量起作用;在其它地方全局變量起作用。

作用域:

小範圍的可以用大範圍的但是大範圍的不能用小範圍的

範圍從大到小(圖)

技術分享

在小範圍內,如果要用一個變量,是當前這個小範圍有的,就用自己的如果在小範圍內沒有,就用上一級的,上一級沒有就用上上一級的,以此類推。如果都沒有,報錯

globals和locals方法

技術分享
a = 20
b = 50
def func():
    x = 1
    y = 2
    print(globals())  #全局作用域中的名字
    print(locals())   #局部作用域中的名字

func()
print(globals())  #全局作用域中的名字
print(locals())   #全局的局部還是全局
globals和locals

global關鍵字

技術分享
a = 10
def func():
    a = 20
def func1():
    global a  #改變全局變量
    a = 20

print(a)
func()
print(a)
func1()
print(a)
global關鍵字

三、函數的嵌套和作用域鏈

函數的嵌套調用

技術分享
def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

max4(23,-7,31,11)
函數的嵌套調用

函數的嵌套定義

技術分享
def f1():
    print("in f1")
    def f2():
        print("in f2")

    f2()
f1()
函數的嵌套定義(一) 技術分享
def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
    
f1()
函數的嵌套定義(二)

函數的作用域鏈

技術分享
def f1():
    a = 1
    def f2():
        print(a)
    f2()

f1()
作用域鏈(一) 技術分享
def f1():
    a = 1
    def f2():
        def f3():
            print(a)
        f3()
    f2()

f1()
作用域鏈(二) 技術分享
def f1():
    a = 1
    def f2():
        a = 2
    f2()
    print(a in f1 : ,a)

f1()
作用域鏈(三)

nolocal關鍵字

1、外部必須有這個變量

2、在內部函數聲明nonlocal變量之前不能再出現同名變量

3、內部修改這個變量如果想在外部有這個變量的第一層函數中生效

技術分享
def f():
    a = 3
    def f1():
        a = 1
        def f2():
            nonlocal a #僅改變外一層變量值
            a = 2
        f2()
        print(a in f1 : , a)
    f1()
    print(a in f : ,a)
f()
nolocal關鍵字

四、函數名的本質

函數名本質上就是函數的內存地址

1、可以被引用

技術分享
def func():
    print(in func)

f = func
print(f)
函數被引用

2、可以被當作容器類型的元素

技術分享
def f1():
    print(f1)


def f2():
    print(f2)


def f3():
    print(f3)

l = [f1,f2,f3]
d = {f1:f1,f2:f2,f3:f3}
#調用
l[0]()
d[f2]()
函數被當作容易類型的元素

3、可以當作函數的參數和返回值

技術分享
def func():
    print(func)
def func2(f):
     f()
 func2(func)
函數做參數 技術分享
def func():
    def func2():
        print(hello)
    return func2

f2 = func()
f2()
f = func
函數做返回值

第一類對象(first-class object)指

1、可在運行期創建

2、可用作函數參數或返回值

3、可存入變量的實體。

不明白就記住一句話,就當普通變量用


五、閉包

閉包函數:內部函數,包含了對外部作用域中變量的引用,內部函數包含對外部作用域而非全劇作用域名字的引用,該內部函數稱為閉包函數
#函數內部定義的函數稱為內部函數

閉包

1、閉 內部的函數

2、包 包含了對外部函數作用域中變量的引用

def func():
    name = eva
    def inner():
        print(name)

技術分享
def func():
    name = eva
    def inner():
        print(name)
    return inner

f = func()
f()
閉包函數常用方法

判斷閉包函數的方法__closure__

技術分享
#輸出的__closure__有cell元素 :是閉包函數
def func():
    name = eva
    def inner():
        print(name)
    print(inner.__closure__)
    return inner

f = func()
f()

#輸出的__closure__為None :不是閉包函數
name = egon
def func2():
    def inner():
        print(name)
    print(inner.__closure__)
    return inner

f2 = func2()
f2()
閉包函數的判斷 技術分享
def wrapper():
    money = 1000
    def func():
        name = eva
        def inner():
            print(name,money)
        return inner
    return func

f = wrapper()
i = f()
i()
閉包嵌套 技術分享
from urllib.request import urlopen

def index():
    url = "http://www.cnblogs.com/liluning/"
    def get():
        return urlopen(url).read()
    return get

cnblogs = index()
content = cnblogs()
print(content)
閉包函數獲取網絡應用

總結圖:

技術分享

預習答案

技術分享
import os
def file_l(demo,i,j) :
    ‘‘‘
    用戶傳入修改的文件名,與要修改的內容,執行函數,完成整個文件的批量修改操作
    :param demo:傳入的文件
    :param i:需要修改的字符串
    :param j:要改變後的字符串
    :return:返回源文件和改後文件的句柄
    ‘‘‘
    with open(demo, r, encoding=utf-8) as read_f,             open("demo1.py", w, encoding=utf-8) as write_f :
        for line in read_f :
            write_f.write(line.replace(i, j))
    return read_f,write_f
file_l1,file_l2 = file_l("demo.py","def","hahaha")
os.remove(file_l1.name)
os.rename(file_l2.name, file_l1.name)


def space_l(str) :
    ‘‘‘
    檢查用戶傳入的對象(字符串、列表、元組)的每一個元素是否含有空內容。
    :param str: 傳入數據
    :return: 布爾值
    ‘‘‘
    for i in str :
        if i.isspace() :
            return False
    return  True

msg = "hello world!"
print(space_l(msg))


def length(dic) :
    ‘‘‘
    檢查傳入字典的每一個value的長度,如果大於2,那麽僅保留前兩個長度的內容,
    並將新內容返回給調用者。
    :param dic:
    :return:
    ‘‘‘
    for i in dic :
        if len(dic[i]) > 2 :
            dic[i] = dic[i][0:2]
    return dic
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
length(dic)
print(dic)
預習答案

小知識點:

技術分享
#三元運算
# a = 20
# b = 10
# if a > b:
#     c = 5
# else:
#     c = 10

#if條件成立的結果 if 條件 else else條件成立的結果
# a = 20
# b = 10
# c = 5 if a>b else 10
# print(c)
三元運算

課外娛樂:

技術分享
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases arent special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless youre Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, its a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- lets do more of those!
python之禪

在python之禪中提到過:命名空間是一種絕妙的理念,讓我們盡情的使用發揮吧!

python函數(2):函數進階