函數對象、函數嵌套、名稱空間與做用域、裝飾器

一 函數知識體系

什麼是函數?
爲何要用函數?
函數的分類:內置函數與自定義函數
如何自定義函數
  語法
  定義有參數函數,及有參函數的應用場景
  定義無參數函數,及無參函數的應用場景
  定義空函數,及空函數的應用場景
調用函數
    如何調用函數
    函數的返回值
    函數參數的應用:形參和實參,位置參數,關鍵字參數,默認參數,*args,**kwargs
高階函數(函數對象)
函數嵌套
做用域與名稱空間
裝飾器
迭代器與生成器及協程函數
三元運算,列表解析、生成器表達式
函數的遞歸調用
內置函數
面向過程編程與函數式編程

二 函數基礎

一 爲什麼要用函數之不用函數的問題python

#一、代碼的組織結構不清晰,可讀性差
#二、遇到重複的功能只能重複編寫實現代碼,代碼冗餘
#三、功能須要擴展時,須要找出全部實現該功能的地方修改之,沒法統一管理且維護難度極大 

 二 函數是什麼mysql

針對二中的問題,想象生活中的例子,修理工須要實現準備好工具箱裏面放好錘子,扳手,鉗子等工具,而後遇到錘釘子的場景,拿來錘子用就能夠,而無需臨時再製造一把錘子。

修理工===>程序員
具有某一功能的工具===>函數

要想使用工具,須要事先準備好,而後拿來就用且能夠重複使用
要想用函數,須要先定義,再使用

 三 函數分類git

#一、內置函數
爲了方便咱們的開發,針對一些簡單的功能,python解釋器已經爲咱們定義好了的函數即內置函數。對於內置函數,咱們能夠拿來就用而無需事先定義,如len(),sum(),max()
ps:咱們將會在最後詳細介紹經常使用的內置函數。

#二、自定義函數
很明顯內置函數所能提供的功能是有限的,這就須要咱們本身根據需求,事先定製好咱們本身的函數來實現某種功能,之後,在遇到應用場景時,調用自定義的函數便可。例如

 

二 定義函數

一 如何自定義函數?程序員

#語法
def 函數名(參數1,參數2,參數3,...):
    '''註釋'''
    函數體
    return 返回的值

#函數名要能反映其意義

 

def auth(user:str,password:str)->int:
    '''
    auth function
    :param user: 用戶名
    :param password: 密碼
    :return: 認證結果
    '''
    if user == 'egon' and password == '123':
        return 1
# print(auth.__annotations__) #{'user': <class 'str'>, 'password': <class 'str'>, 'return': <class 'int'>}

user=input('用戶名>>: ').strip()
pwd=input('密碼>>: ').strip()
res=auth(user,pwd)
print(res)
View Code

二 函數使用的原則:先定義,再調用redis

函數即「變量」,「變量」必須先定義後引用。未定義而直接引用函數,就至關於在引用一個不存在的變量名
#測試一
def foo():
    print('from foo')
    bar()
foo() #報錯

#測試二
def bar():
    print('from bar')
def foo():
    print('from foo')
    bar()
foo() #正常

#測試三
def foo():
    print('from foo')
    bar()
    
def bar():
    print('from bar')
foo() #會報錯嗎?


#結論:函數的使用,必須遵循原則:先定義,後調用
#咱們在使用函數時,必定要明確地區分定義階段和調用階段

#定義階段
def foo():
    print('from foo')
    bar()
def bar():
    print('from bar')
#調用階段
foo()
View Code

三 函數在定義階段都幹了哪些事?sql

#只檢測語法,不執行代碼
也就說,語法錯誤在函數定義階段就會檢測出來,而代碼的邏輯錯誤只有在執行時纔會知道

四 定義函數的三種形式編程

#一、無參:應用場景僅僅只是執行一些操做,好比與用戶交互,打印
#二、有參:須要根據外部傳進來的參數,才能執行相應的邏輯,好比統計長度,求最大值最小值
#三、空函數:設計代碼結構

 

#定義階段
def tell_tag(tag,n): #有參數
    print(tag*n)

def tell_msg(): #無參數
    print('hello world')

#調用階段
tell_tag('*',12)
tell_msg()
tell_tag('*',12)

'''
************
hello world
************
'''

#結論:
#一、定義時無參,意味着調用時也無需傳入參數
#二、定義時有參,意味着調用時則必須傳入參數

無參、有參
View Code
def auth(user,password):                             
    '''                                                           
    auth function                                                 
    :param user: 用戶名                                              
    :param password: 密碼                                           
    :return: 認證結果                                                 
    '''                                                           
    pass                                                          
                                                                  
def get(filename):                                                
    '''                                                           
    :param filename:                                              
    :return:                                                      
    '''                                                           
    pass                                                          
                                                                  
def put(filename):                                                
    '''                                                           
    :param filename:                                              
    :return:                                                      
    '''                                                           
def ls(dirname):                                                  
    '''                                                           
    :param dirname:                                               
    :return:                                                      
    '''                                                           
    pass                                                          

#程序的體系結構立見           

空函數
View Code

三 調用函數

一 調用函數緩存

函數的調用:函數名加括號
1 先找到名字
2 根據名字調用代碼

 二 函數返回值閉包

無return->None
return 1個值->返回1個值
return 逗號分隔多個值->元組

 

何時該有返回值?
    調用函數,通過一系列的操做,最後要拿到一個明確的結果,則必需要有返回值
    一般有參函數須要有返回值,輸入參數,通過計算,獲得一個最終的結果
何時不須要有返回值?
    調用函數,僅僅只是執行一系列的操做,最後不須要獲得什麼結果,則無需有返回值
    一般無參函數不須要有返回值

 三 函數調用的三種形式app

1 語句形式:foo()
2 表達式形式:3*len('hello')
3 當中另一個函數的參數:range(len('hello'))

 

四 函數的參數

一 形參與實參

#形參即變量名,實參即變量值,函數調用時,將值綁定到變量名上,函數調用結束,解除綁定

 二 具體應用

#一、位置參數:按照從左到右的順序定義的參數
        位置形參:必選參數
        位置實參:按照位置給形參傳值

#二、關鍵字參數:按照key=value的形式定義的實參
        無需按照位置爲形參傳值
        注意的問題:
                1. 關鍵字實參必須在位置實參右面
                2. 對同一個形參不能重複傳值

#三、默認參數:形參在定義時就已經爲其賦值
        能夠傳值也能夠不傳值,常常須要變得參數定義成位置形參,變化較小的參數定義成默認參數(形參)
        注意的問題:
                1. 只在定義時賦值一次
                2. 默認參數的定義應該在位置形參右面
                3. 默認參數一般應該定義成不可變類型


#四、可變長參數:
        可變長指的是實參值的個數不固定
        而實參有按位置和按關鍵字兩種形式定義,針對這兩種形式的可變長,形參對應有兩種解決方案來完整地存放它們,分別是*args,**kwargs

        ===========*args===========
        def foo(x,y,*args):
            print(x,y)
            print(args)
        foo(1,2,3,4,5)

        def foo(x,y,*args):
            print(x,y)
            print(args)
        foo(1,2,*[3,4,5])


        def foo(x,y,z):
            print(x,y,z)
        foo(*[1,2,3])

        ===========**kwargs===========
        def foo(x,y,**kwargs):
            print(x,y)
            print(kwargs)
        foo(1,y=2,a=1,b=2,c=3)

        def foo(x,y,**kwargs):
            print(x,y)
            print(kwargs)
        foo(1,y=2,**{'a':1,'b':2,'c':3})


        def foo(x,y,z):
            print(x,y,z)
        foo(**{'z':1,'x':2,'y':3})

        ===========*args+**kwargs===========

        def foo(x,y):
            print(x,y)

        def wrapper(*args,**kwargs):
            print('====>')
            foo(*args,**kwargs)

#五、命名關鍵字參數:*後定義的參數,必須被傳值(有默認值的除外),且必須按照關鍵字實參的形式傳遞
能夠保證,傳入的參數中必定包含某些關鍵字
        def foo(x,y,*args,a=1,b,**kwargs):
            print(x,y)
            print(args)
            print(a)
            print(b)
            print(kwargs)

        foo(1,2,3,4,5,b=3,c=4,d=5)
        結果:
            2
            (3, 4, 5)
            3
            {'c': 4, 'd': 5}

此乃重點知識!!!
View Code

五 練習題

一、寫函數,,用戶傳入修改的文件名,與要修改的內容,執行函數,完成批了修改操做
二、寫函數,計算傳入字符串中【數字】、【字母】、【空格] 以及 【其餘】的個數

三、寫函數,判斷用戶傳入的對象(字符串、列表、元組)長度是否大於5。

四、寫函數,檢查傳入列表的長度,若是大於2,那麼僅保留前兩個長度的內容,並將新內容返回給調用者。

五、寫函數,檢查獲取傳入列表或元組對象的全部奇數位索引對應的元素,並將其做爲新列表返回給調用者。

六、寫函數,檢查字典的每個value的長度,若是大於2,那麼僅保留前兩個長度的內容,並將新內容返回給調用者。
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
PS:字典中的value只能是字符串或列表

#題目一
def modify_file(filename,old,new):
    import os
    with open(filename,'r',encoding='utf-8') as read_f,\
        open('.bak.swap','w',encoding='utf-8') as write_f:
        for line in read_f:
            if old in line:
                line=line.replace(old,new)
            write_f.write(line)
    os.remove(filename)
    os.rename('.bak.swap',filename)

modify_file('/Users/jieli/PycharmProjects/爬蟲/a.txt','alex','SB')

#題目二
def check_str(msg):
    res={
        'num':0,
        'string':0,
        'space':0,
        'other':0,
    }
    for s in msg:
        if s.isdigit():
            res['num']+=1
        elif s.isalpha():
            res['string']+=1
        elif s.isspace():
            res['space']+=1
        else:
            res['other']+=1
    return res

res=check_str('hello name:aSB passowrd:alex3714')
print(res)


#題目三:略

#題目四
def func1(seq):
    if len(seq) > 2:
        seq=seq[0:2]
    return seq
print(func1([1,2,3,4]))


#題目五
def func2(seq):
    return seq[::2]
print(func2([1,2,3,4,5,6,7]))


#題目六
def func3(dic):
    d={}
    for k,v in dic.items():
        if len(v) > 2:
            d[k]=v[0:2]
    return d
print(func3({'k1':'abcdef','k2':[1,2,3,4],'k3':('a','b','c')}))
View Code

三 函數對象、函數嵌套、名稱空間與做用域、裝飾器

一 函數對象

一 函數是第一類對象,即函數能夠看成數據傳遞

#1 能夠被引用
#2 能夠看成參數傳遞
#3 返回值能夠是函數
#3 能夠看成容器類型的元素

 二 利用該特性,優雅的取代多分支的if

def foo():
    print('foo')

def bar():
    print('bar')

dic={
    'foo':foo,
    'bar':bar,
}
while True:
    choice=input('>>: ').strip()
    if choice in dic:
        dic[choice]()

 

二 函數嵌套

一 函數的嵌套調用

def max(x,y):
    return x if x > y else y

def max4(a,b,c,d):
    res1=max(a,b)
    res2=max(res1,c)
    res3=max(res2,d)
    return res3
print(max4(1,2,3,4))

 二 函數的嵌套定義

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()

f1()
f3() #報錯,爲什麼?請看下一小節

 

三 名稱空間與做用域

一 什麼是名稱空間?

#名稱空間:存放名字的地方,三種名稱空間,(以前遺留的問題x=1,1存放於內存中,那名字x存放在哪裏呢?名稱空間正是存放名字x與1綁定關係的地方)

 二 名稱空間的加載順序

python test.py
#一、python解釋器先啓動,於是首先加載的是:內置名稱空間
#二、執行test.py文件,而後以文件爲基礎,加載全局名稱空間
#三、在執行文件的過程當中若是調用函數,則臨時產生局部名稱空間

 三 名字的查找順序

 

局部名稱空間--->全局名稱空間--->內置名稱空間

#須要注意的是:在全局沒法查看局部的,在局部能夠查看全局的,以下示例

# max=1
def f1():
    # max=2
    def f2():
        # max=3
        print(max)
    f2()
f1()
print(max)

 四 做用域

#一、做用域即範圍
        - 全局範圍(內置名稱空間與全局名稱空間屬於該範圍):全局存活,全局有效
      - 局部範圍(局部名稱空間屬於該範圍):臨時存活,局部有效
#二、做用域關係是在函數定義階段就已經固定的,與函數的調用位置無關,以下
x=1
def f1():
    def f2():
        print(x)
    return f2
x=100
def f3(func):
    x=2
    func()
x=10000
f3(f1())

#三、查看做用域:globals(),locals()


LEGB 表明名字查找順序: locals -> enclosing function -> globals -> __builtins__
locals 是函數內的名字空間,包括局部變量和形參
enclosing 外部嵌套函數的名字空間(閉包中常見)
globals 全局變量,函數定義所在模塊的名字空間
builtins 內置模塊的名字空間
View Code

五 global與nonlocal關鍵字

一 什麼是閉包?

#內部函數包含對外部做用域而非全局做用域的引用

#提示:以前咱們都是經過參數將外部的值傳給函數,閉包提供了另一種思路,包起來嘍,包起呦,包起來哇

        def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看閉包的元素

 

二 閉包的意義與應用

#閉包的意義:返回的函數對象,不只僅是一個函數對象,在該函數外還包裹了一層做用域,這使得,該函數不管在何處調用,優先使用本身外層包裹的做用域
#應用領域:延遲計算(原來咱們是傳參,如今咱們是包起來)
    from urllib.request import urlopen

    def index(url):
        def get():
            return urlopen(url).read()
        return get

    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))

 

五 裝飾器

裝飾器就是閉包函數的一種應用場景

一 爲什麼要用裝飾器

#開放封閉原則:對修改封閉,對擴展開放

 二 什麼是裝飾器

裝飾器他人的器具,自己能夠是任意可調用對象,被裝飾者也能夠是任意可調用對象。
強調裝飾器的原則:1 不修改被裝飾對象的源代碼 2 不修改被裝飾對象的調用方式
裝飾器的目標:在遵循1和2的前提下,爲被裝飾對象添加上新功能

 三 裝飾器的使用

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def foo():
    time.sleep(3)
    print('from foo')
foo()

無參裝飾器
View Code
def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')

有參裝飾器
View Code

四 裝飾器語法

被裝飾函數的正上方,單獨一行
        @deco1
        @deco2
        @deco3
        def foo():
            pass

        foo=deco1(deco2(deco3(foo)))

 五 裝飾器補充:wraps

from functools import wraps

def deco(func):
    @wraps(func) #加在最內層函數正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
View Code

六 練習題

一:編寫函數,(函數執行的時間是隨機的)
二:編寫裝飾器,爲函數加上統計時間的功能
三:編寫裝飾器,爲函數加上認證的功能

四:編寫裝飾器,爲多個函數加上認證的功能(用戶的帳號密碼來源於文件),要求登陸成功一次,後續的函數都無需再輸入用戶名和密碼
注意:從文件中讀出字符串形式的字典,能夠用eval('{"name":"egon","password":"123"}')轉成字典格式

五:編寫裝飾器,爲多個函數加上認證功能,要求登陸成功一次,在超時時間內無需重複登陸,超過了超時時間,則必須從新登陸

六:編寫下載網頁內容的函數,要求功能是:用戶傳入一個url,函數返回下載頁面的結果

七:爲題目五編寫裝飾器,實現緩存網頁內容的功能:
具體:實現下載的頁面存放於文件中,若是文件內有值(文件大小不爲0),就優先從文件中讀取網頁內容,不然,就去下載,而後存到文件中

擴展功能:用戶能夠選擇緩存介質/緩存引擎,針對不一樣的url,緩存到不一樣的文件中

八:還記得咱們用函數對象的概念,製做一個函數字典的操做嗎,來來來,咱們有更高大上的作法,在文件開頭聲明一個空字典,而後在每一個函數前加上裝飾器,完成自動添加到字典的操做

九 編寫日誌裝飾器,實現功能如:一旦函數f1執行,則將消息2017-07-21 11:12:11 f1 run寫入到日誌文件中,日誌文件路徑能夠指定
注意:時間格式的獲取
import time
time.strftime('%Y-%m-%d %X')

#題目一:略
#題目二:略
#題目三:略
#題目四:
db='db.txt'
login_status={'user':None,'status':False}
def auth(auth_type='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            if login_status['user'] and login_status['status']:
                return func(*args,**kwargs)
            if auth_type == 'file':
                with open(db,encoding='utf-8') as f:
                    dic=eval(f.read())
                name=input('username: ').strip()
                password=input('password: ').strip()
                if name in dic and password == dic[name]:
                    login_status['user']=name
                    login_status['status']=True
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
            elif auth_type == 'sql':
                pass
            else:
                pass
        return wrapper
    return auth2

@auth()
def index():
    print('index')

@auth(auth_type='file')
def home(name):
    print('welcome %s to home' %name)


# index()
# home('egon')

#題目五
import time,random
user={'user':None,'login_time':None,'timeout':0.000003,}

def timmer(func):
    def wrapper(*args,**kwargs):
        s1=time.time()
        res=func(*args,**kwargs)
        s2=time.time()
        print('%s' %(s2-s1))
        return res
    return wrapper


def auth(func):
    def wrapper(*args,**kwargs):
        if user['user']:
            timeout=time.time()-user['login_time']
            if timeout < user['timeout']:
                return func(*args,**kwargs)
        name=input('name>>: ').strip()
        password=input('password>>: ').strip()
        if name == 'egon' and password == '123':
            user['user']=name
            user['login_time']=time.time()
            res=func(*args,**kwargs)
            return res
    return wrapper

@auth
def index():
    time.sleep(random.randrange(3))
    print('welcome to index')

@auth
def home(name):
    time.sleep(random.randrange(3))
    print('welcome %s to home ' %name)

index()
home('egon')

#題目六:略
#題目七:簡單版本
import requests
import os
cache_file='cache.txt'
def make_cache(func):
    def wrapper(*args,**kwargs):
        if not os.path.exists(cache_file):
            with open(cache_file,'w'):pass

        if os.path.getsize(cache_file):
            with open(cache_file,'r',encoding='utf-8') as f:
                res=f.read()
        else:
            res=func(*args,**kwargs)
            with open(cache_file,'w',encoding='utf-8') as f:
                f.write(res)
        return res
    return wrapper

@make_cache
def get(url):
    return requests.get(url).text


# res=get('https://www.python.org')

# print(res)

#題目七:擴展版本
import requests,os,hashlib
engine_settings={
    'file':{'dirname':'./db'},
    'mysql':{
        'host':'127.0.0.1',
        'port':3306,
        'user':'root',
        'password':'123'},
    'redis':{
        'host':'127.0.0.1',
        'port':6379,
        'user':'root',
        'password':'123'},
}

def make_cache(engine='file'):
    if engine not in engine_settings:
        raise TypeError('egine not valid')
    def deco(func):
        def wrapper(url):
            if engine == 'file':
                m=hashlib.md5(url.encode('utf-8'))
                cache_filename=m.hexdigest()
                cache_filepath=r'%s/%s' %(engine_settings['file']['dirname'],cache_filename)

                if os.path.exists(cache_filepath) and os.path.getsize(cache_filepath):
                    return open(cache_filepath,encoding='utf-8').read()

                res=func(url)
                with open(cache_filepath,'w',encoding='utf-8') as f:
                    f.write(res)
                return res
            elif engine == 'mysql':
                pass
            elif engine == 'redis':
                pass
            else:
                pass

        return wrapper
    return deco

@make_cache(engine='file')
def get(url):
    return requests.get(url).text

# print(get('https://www.python.org'))
print(get('https://www.baidu.com'))


#題目八
route_dic={}

def make_route(name):
    def deco(func):
        route_dic[name]=func
    return deco
@make_route('select')
def func1():
    print('select')

@make_route('insert')
def func2():
    print('insert')

@make_route('update')
def func3():
    print('update')

@make_route('delete')
def func4():
    print('delete')

print(route_dic)


#題目九
import time
import os

def logger(logfile):
    def deco(func):
        if not os.path.exists(logfile):
            with open(logfile,'w'):pass

        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            with open(logfile,'a',encoding='utf-8') as f:
                f.write('%s %s run\n' %(time.strftime('%Y-%m-%d %X'),func.__name__))
            return res
        return wrapper
    return deco

@logger(logfile='aaaaaaaaaaaaaaaaaaaaa.log')
def index():
    print('index')

index()
View Code

四 迭代器、生成器、面向過程編程

相關文章
相關標籤/搜索