Python入門day04_函數與裝飾器

1、函數入門

什麼是函數:html

# 函數:函數是一系列代碼的集,用來完成特定功能的代碼塊,相似於工具,能夠重複不但的去使用

爲何要有函數:git

# 優勢:
#
1. 避免代碼的冗餘 # 2. 讓程序代碼結構更加清晰 # 3. 讓代碼具備複用性,便於維護

函數四部分:編程

1. 函數名:使用該函數的依據
2. 函數體:完成功能的代碼塊
3. 返回值:功能完成的反饋結果
4. 參數:完成功能須要的條件信息

定義函數的語法:api

# 一、定義
# def 是聲明函數的關鍵字,後面跟着函數名,括號內是函數的參數
def 函數名(參數1,參數2,參數3,...): 
    '''註釋'''
    函數體  # 函數體寫具體的邏輯代碼
    return 返回的值 # retrun後面是函數的返回值,是函數體代碼的運行成果

# 二、調用
函數名(參數1,參數2,參數3,...)

根據函數體劃分:閉包

根據函數體劃分:

# 空函數:用來羅列功能
# 空函數指的是函數體用pass佔位,pass表明什麼也不作
def func():           
    pass 
# func():調用後什麼是都不幹

# 非空函數:正常能夠完成某項功能
def func():           
    print('非空函數') 
# func():調用後執行函數體


根據參數列表劃分:
# 無參函數:不需外界資源
def start():
    print('系統啓動')
start()
# 有參函數:須要外界資源 def login(usr, pwd): if usr == 'simon' and pwd == '123': print('登陸經過') else: print('登陸失敗') # 根據返回值劃分:return是用來結束函數的: # 空返回:返回None #沒有返回值,默認返回None def demo(x, y): print( x + y )
#主動書寫return,返回None
def demo(x, y): print( x + y ) return # 一值返回 def demo(x, y): return x + y # 多值返回:結束函數且帶出多個值: 本質就是帶出一個元組 def demo(x, y): return x + y, x - y, x * y, x / y
demo(10,20)
# 結果:(30, -10, 200, 0.5)

 如何定義函數:ide

# 一、功能的單一化
# 二、參數須要外界提供、經過函數的參數來實現
# 三、函數執行後的結果,經過返回值告訴給外界

2、函數的形參與實參

形參與實參函數

def fn(形參們):
    pass
    
fn(實參們)

# 形參:在函數定義時()裏出現的參數
    -- 形參自己沒有實際值(意義),在函數調用時,傳入什麼實參,形參就裝有什麼值
# 實參:在函數調用時()裏出現的參數
    -- 實參有實際值(意義)
    
# 重點:函數調用傳參:將實參的值賦值給形參 | 形參要獲取外界的值只能經過實參進行獲取

實參分類工具

位置實參:
1.傳參兩種方式:實參名 | 實參具體值
2.必須按位置對形參進行傳值

關鍵字實參:
1.傳參兩種方式:形參名=實參名 | 形參名=實參值
2.能夠指名道姓對形參進行傳值,因此能夠不用按位置進行傳參

def func(a, b, c):
print(a, b, c)ui

# func(10, b=20, 200) 報錯:SyntaxError: positional argument follows keyword argument
# 重點:兩種實參在一塊兒進行傳參時:必須位置在前,關鍵字在後加密

兩大形參分類

形參種類:
1)位置形參
    -- 普通位置形參
    -- 默認值形參
    -- 可變長位置形參

2)關鍵字形參
    -- 有默認值關鍵字形參
    -- 無默認值關鍵字形參
    -- 可變長關鍵字形參
def fn(a, b, *, x, y): # 位置形參:a, b 關鍵字形參: x, y pass # 重點: 1.*爲分水嶺 2.位置實參只能給位置形參進行傳值 3.關鍵字實參能夠給位置形參與關鍵字形參進行傳值
# 兩個帶默認值的形參
def fn2(a=10, *, x=20):
    print(a, x)
fn2(100, x=200)
# 總結:
# 1.有默認值的參數能夠不用傳值
# 2.*前有默認值的叫默認值參數,屬於位置形參,能夠被位置及關鍵字實參進行傳值
# 3.*後有默認值的叫有默認值的關鍵字形參,屬於關鍵字形參,只能被關鍵字實參進行傳值
# 4.若是省略*, 有默認值的形參都是默認值參數

# 不帶默認值與帶默認值形參結合使用
def fn3(a, b=10, *, x, y=20, z):
    print(a, b, x, y, z)
fn3(100, x=200, z=300)

# 結果:
# 100 10 200 20 300

# 總結:
# 1.沒有默認值的必須傳參,有默認值的能夠傳也能夠不傳
# 2.位置有值的必須出如今無值以後,關鍵字順序不作要求

可變長形參與可變長關鍵字形參:

def fn4(a, b=10, *args, x, **kwargs):
    print(a, b, x)
    print(args)
    print(kwargs)
fn4(10, 20, 30, x=100, y=200, z=300)
# 結果:
# 10 20 100
# (30,)
# {'y': 200, 'z': 300}
# 總結: # 1.可變長是用來接收未接收完的值(接收0到n個): # -- *args用來接收全部沒有接收完的位置(只能接收位置實參) # -- **kwargs用來接收全部沒有接收完的關鍵字(只能接收關鍵字實參) # 2.*args必須出如今因此位置參數以後,**kwargs必須出如今因此參數以後

# 假設第一個位置永遠是參數name
def func4(*args, **kwargs):
name = args[0] # 將name抽出來

def func44(name, *args, **kwargs):
# name 能夠直接接收,省了抽出來的過程
pass

可變形參打散傳值:

def fn5(*args, **kwargs):
    print(args, kwargs)  # args=(10, 20)  kwargs={'x': 100, 'y': 200}
fn5(10, 20, x=100, y=200)

def fn55(*args, **kwargs):
    print(args, kwargs)  # args=(10, 20)  kwargs={'x': 100, 'y': 200}
    fn5(*args, **kwargs)  # *args就會把元組打散傳遞,**kwargs就會把字典打散傳遞
  #fn5(args, kwargs) # 結果:((10, 20), {'x': 100, 'y': 200}) {} # 不加*號,字典是空的,沒有打散
fn55(10, 20, x=100, y=200) # 結果:(10, 20) {'x': 100, 'y': 200}
# 容器類型能夠打散傳值 def temp(*args, **kwargs): print(args, kwargs) ls = [1, 2, 3, 4, 5] dic = {'a': 1, 'b': 2} temp(*ls, **dic) # 結果:(1, 2, 3, 4, 5) {'a': 1, 'b': 2}

# 注:字符串也能夠做爲單列集合進行打散傳遞
fn(*'abc') # => ('a', 'b', 'c') {}

總結:

# 1.位置實參只能給位置形參傳值
# 2.關鍵字實參能夠給位置及關鍵字形參傳值
# 3.有默認值的能夠不用傳參
# 4.可變長位置形參只能接受位置實參,接受位置形參沒有接收完的位置實參,存放到元組中
# 5.可變長關鍵字形參只能接受關鍵字實參,接受關鍵字形參沒有接收完的關鍵字實參,存放到字典中
# 6.*args必須出如今全部位置形參以後,**kwargs必須在全部形參以後

 

3、函數對象

什麼是函數對象:

# 函數名就是存放了函數的內存地址,存放了內存地址的變量都是對象,即 函數名 就是 函數對象
# 函數對象:存放函數地址的變量就叫函數對象,就是函數名

函數對象的應用場景:

# 應用場景:
# 1 能夠直接被引用
# 2 能夠看成函數參數傳遞
# 3 能夠做爲函數的返回值
# 4 能夠做爲容器類型的元素

舉例:

def fn():
    print(1)
    print(2)
    print(3)
    return 1
print(fn)
# 結果:<function fn at 0x0000000001D02EA0>
# 1.函數對象():拿到函數地址並執行 - 函數的調用
# 2.函數調用必定會獲得一個結果 - 函數的返回值 - 函數值: res=fn() == 函數調用後res=1

res = fn()
print(res) # 1 2 3 1
# res = 1
print(res) # 1 #調用後再打印的結果仍是1

# 能夠當作函數參數傳值
def fn2():
    print('fn2 run')
# fn2()
aaa = fn2  # 直接賦值
# aaa()

def fn22(fn):  # fn = aaa = fn2  # 做爲參數
    fn()
fn22(aaa) #fn2 run

print("-------------------------")
def fn222():
    # return fn2()
    return fn2  # 做爲返回值
res = fn222()  # res = fn2() = None  |  res = fn2 = 函數對象
print(res()) #fn2 run和None

#能夠做爲容器類型
ls = [fn2, 10, 20]  # 做爲容器對象的成員
print(ls[1]) # 10
print(ls[0]()) #fn2 run和None
# print(ls) #[<function fn2 at 0x00000000004C2EA0>, 10, 20]
# 功能體
def add(n1, n2):
    return n1 + n2

def low(n1, n2):
    return n1 - n2

def jump(n1, n2):
    return n1 * n2

# 完成功能
def computed(n1, n2, fn):  # fn = add|low|jump
    res = fn(n1, n2)  # 調用具體的功能
    return res

# 功能對應關係
method_map = {  # 指令與函數對象的對應關係
    '1': add,
    '2': low,
    '3': jump
}

# 獲取功能
def get_method(cmd):
    if cmd in method_map:
        return method_map[cmd]  # 返回 add|low|jump
    return add  # 當指令錯誤,add做爲默認功能

while True:
    cmd = input('cmd: ')
    res = get_method(cmd)(10, 20)  # 根據指令獲取功能並調用獲得結果
    print(res)

"""
#實驗的過程
while True:
    cmd = input('cmd: ')
    res = 0
    # if cmd in method_map:
    #     # res = method_map[cmd](10, 20)
    #     fn = get_method(cmd)
    #     res = fn(10, 20)
    res = get_method(cmd)(10, 20)
    print(res)
"""
加減乘除案例

函數的嵌套調用

定義:函數的嵌套調用:在一個函數內部調用另外一個函數

# 案例:
# 求兩個數最大值
def max_two(n1, n2):
    if n1 > n2:
        return n1
    return n2

# 求三個數最大值:兩兩相比
def max_three(n1, n2, n3):
    max = max_two(n1, n2)
    return max_two(max, n3)

# 求四個數最大值
def max_four(n1, n2, n3, n4):
    max = max_three(n1, n2, n3)
    return max_two(max, n4)
print(max_four(20, 50, 30, 50)) #50

名稱空間:

# 名稱空間:存放名字與內存空間地址對應關係的容器
# 做用:解決因爲名字有限,致使名字重複發送衝突的問題 - 內置全局局部能夠同時使用一個名字存放不一樣地址

# 三種名稱空間
# Built-in:內置名稱空間;系統級,一個;隨解釋器執行而產生,解釋器中止而銷燬
# Global:全局名稱空間;文件級,多個;隨所屬文件加載而產生,文件運行完畢而銷燬
# Local:局部名稱空間;函數級,多個;隨所屬函數執行而產生,函數執行完畢而銷燬

# 加載順序:Built-in > Global > Local
#     -- 採用堆棧存儲數據的方式(壓棧),致使內置最後被訪問

# 注:
# del 名字:能夠移除查找最近的名字與內存空間地址的對應關係
# 加載順序:Built-in > Global > Local

函數的嵌套定義:

# 函數的嵌套定義:在函數內部定義函數
# 誕生的理由:一個函數想使用另外一個函數內部的變量,能夠定義在其內部
def func():
    a = 10
    def fn():
        print(a)
    return fn

new_fn = func()
new_fn() # 10

兩個與函數有關的關鍵字:global nonlocal

# 做用域:變量的做用範圍
len = 10
def fn3():
    len = 100
    def fn33():
        len = 1000
        print('1:', len)
    fn33()
    print('2:', len)
fn3()
print('3:', len)
# 結果:
# 1: 1000
# 2: 100
# 3: 10

# nonlocal關鍵字:統一局部與嵌套局部的變量名
def fn2():
    num = 666
    def fn22():
        nonlocal num
        num = 888
    fn22()
    print(num)
fn2() # 888

# global:統一局部與全局的變量名
num = 10
def outer():
    def inner():
        global num  # 讓函數的局部變量num與全局變量num統一,二者是一個
        num = 1000
    #return inner()
inner()
print(num) # 10 outer() # 函數執行後num變量就編程了全局變量 print(num) # 1000

做用域:global nonlocal

# 做用域:名字起做用的範圍
# 做用:解決同名字能夠共存問題 - 不一樣做用域相同名字的值都能在其做用域範圍下進行使用

# 四種做用域
# Built-in:內置做用域
# Global:全局做用域
# Enclosing:嵌套做用域
# Local:局部做用域

# 注:
# 不一樣做用域之間名字不衝突,以達到名字的重用
# 查找順序:Local > Enclosing > Global > Built-in

  # 加載順序:Built-in > Global > Enclosing > Local
  # 做用範圍:Built-in > Global > Enclosing > Local

def add_b():
    global  b
    b = 42
    def do_global():
        #global  b
        b = b + 10
        print(b)
    do_global()
    print(b)
add_b()

# 報錯:UnboundLocalError: local variable 'b' referenced before assignment
# global 定義的 b ,只能引用,不能修改
 

函數的閉包:

# closure:閉包
# 閉包:定義在函數內部的函數,這個內部的函數就是閉包

# 完整的閉包結構:1.將函數進行閉包處理;2.提高函數名的做用域
# 應用場景: # 1.能夠去使用其餘函數的內部變量,且還能夠保證調用位置不變(閉包的函數對象做爲那個函數的返回值) def outer(): count = 3000 def fn(): print(count) # 能使用outer內部的變量count return fn # 仍是在外界調用 outer()() # outer()() => fn() => 調用fn
# 延遲執行(外層函數能夠爲內存函數傳遞參數)
import requests

# def show_html(url):
#     response = requests.get(url)
#     print(response.text)
#
# show_html('https://www.baidu.com')
# # show_html('https://www.baidu.com')
# # show_html('https://www.sina.com.cn')

def outer(url):
    def show_html():
        response = requests.get(url)
        print(response.text)
    return show_html
# 製做 爬百度與新浪的 函數對象
show_baidu = outer('https://www.baidu.com')
show_sina = outer('https://www.sina.com.cn')
# 延遲到需求來了,須要爬百度,就用百度函數對象,須要爬新浪,就用新浪函數對象
show_baidu()
show_sina()
show_baidu()

練習:

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

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

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

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

6、寫函數,檢查字典的每個value的長度,若是大於2,那麼僅保留前兩個長度的內容,並將新內容返回給調用者。
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
PS:字典中的value只能是字符串或列表
# 題一
import os
def move_file(old_file,new_file,old,new):
    with open(old_file,'r',encoding='utf-8') as r,open(new_file,'w',encoding='utf-8') as w:
        for line in r.readlines():
            if old in line:
                line = line.replace(old,new)
            w.write(line)
    os.remove(old_file)

move_file(r'F:\安裝包\JetBrains\JetBrains\Projects\04\代碼\work\file',r"D:\\a.py",'sd','SD')


# 題二
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


def NR():
    with open('file','r',encoding='utf-8') as r:
        data = r.read()
    return data

res=check_str(NR())
print(res)
# {'num': 4, 'string': 24, 'space': 2, 'other': 2}

#題目四
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')}))
結果

4、裝飾器

什麼是裝飾器:

# 裝飾器:就是閉包(閉包的一個應用場景)
    -- 把要被裝飾的函數做爲外層函數的參數經過閉包操做後返回一個替代版函數
    
# 優勢:
    -- 豐富了原有函數的功能
    -- 提升了程序的可拓展性

開放封閉原則:

# 開放封閉原則:
# 開放:拓展功能的點是開放的 - 能夠爲以前的函數添加新功能
# 封閉:1.不能改變原函數的源代碼  2.還有經過原函數的函數對象來調用函數
# 需求:如何拓展一個原有函數的功能
#       -- 修改源代碼
#       -- 建立一個包含該功能和其餘新功能的新函數
# 錯誤一:修改了源代碼
# def huaping():
#     print('插花功能')
#     print('觀賞功能')
# huaping()

# 錯誤二:改變了調用方式
# def huaping():
#     print('插花功能')
# def my_huaping(fn):
#     fn()
#     print('觀賞功能')
# my_huaping(huaping)
# 裝飾器:知足開放封閉原則還能拓展新功能
# 裝飾器:知足開放封閉原則還能拓展新功能,簡單
'''
def huaping():
    print('插花功能')

temp = huaping

def my_huaping():
    temp()
    print('觀賞功能')
huaping = my_huaping

huaping()
'''

# 裝飾器演變一:
'''
def huaping():
    print('插花功能')

def outer(fn):  # fn = huaping
    # temp = huaping  # 能夠提取到實參對形參傳遞
    def my_huaping():
        fn()
        print('觀賞功能')
    return my_huaping
huaping = outer(huaping)  # 要整合該條邏輯

huaping()

# 裝飾器演變二:
def outer(fn):  # fn = 原功能的huaping
    def my_huaping():  # my_huaping => 新功能的huaping
        fn()
        print('觀賞功能')
    return my_huaping

@outer  # huaping = outer(huaping)  被裝飾的函數對象 = 裝飾器外層函數對象(被裝飾的函數對象)
def huaping():
    print('插花功能')

huaping()  # 被裝飾後的my_huaping

語法:
# 被裝飾的函數可能有參有返:裝飾器模板,能夠知足全部參數,且能裝飾原函數返回值
def outer(func):  # temp = huaping
    def inner(*args, **kwargs):
        pass
        res = func(*args, **kwargs)
           pass
        return res
    return inner

@outer
def any_method():
    pass

裝飾器語法糖:

def outer(func):
    def inner():
        print("新增功能1")
        func()
        print("新增功能2")
    return inner

@outer
def func():
    print("原有功能")

裝飾器有參有返的函數
def outer(func):
    def inner(*args, **kwargs):
        print("新增功能1")
        result = func(*args, **kwargs)
        print("新增功能2")
        return result
    return inner

@outer
def func(*args, **kwargs):
    print("原有功能")
    return "原有結果"

有參裝飾器

def wrap(arg):
    def outer(func):
        def inner(*args, **kwargs):
            print("新增功能1")
            result = func(*args, **kwargs)
            print("新增功能2")
            return result
        return inner

@wrap("裝飾器參數")
def func(*args, **kwargs):
    print("原有功能")
    return "原有結果"

舉例:
# 爲何要出現帶參裝飾器
def outer(func):
    # outer與inner之間要使用外部數據
    # 能夠解決的方案路徑,給outer添加參數,可是outer的參數是固定一個,就是被裝飾的函數
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    return inner

# 因此只能使用函數的閉包,經過外層函數給內存函數傳遞參數的方式
def wrap(*arg, **kwargs):
    def outer(func):
        # 就可使用wrap中的*arg, **kwargs,就是要使用的外部數據
     print(*arg, **kwargs)
def inner(*args, **kwargs): res = func(*args, **kwargs) return res return inner return outer a = 10 b = 20 @wrap(a, b) # @wrap(10, 20) => @outer => fn = outer(fn) => fn = inner def fn(): pass
 
 

wraps修改函數文檔註釋

from functools import wraps
def outer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        '''裝飾器文檔註釋'''
        func(*args, **kwargs)
    return inner

@outer
def func(*args, **kwargs):
    '''原有文檔註釋'''
    print("原有功能")
from functools import wraps
def outer(func):
    # @wraps(temp)  # 將inner.__doc__指向temp的__doc__
    @wraps(func)  # 將inner.__doc__指向被裝飾的func的__doc__
    def inner(*args, **kwargs):
        '''
        :param args: 被裝飾函數的位置形參
        :param kwargs: 被裝飾函數的關鍵字形參
        :return:
        '''
        res = func(*args, **kwargs)
        return res
    return inner

@outer
def fn3(arg):
    '''
    :param arg: fn3的參數
    :return:
    '''
print(fn3)  # fn3 == inner
# fn3本質是inner,可是我打印文檔註釋,能不能造成一個打印假象,
# 打印的是fn3本身文檔註釋 - 爲了統一查看原碼和打印文檔註釋,顯示的是一樣內容
print(fn3.__doc__)
View Code
fn.__doc__就是打印該函數的文檔註釋(解釋該函數功能的)

 

#需求:驗證帳號密碼登陸;登陸時檢驗帳號密碼是否合格並輸出對應的提示


# 爲登陸功能添加帳號檢驗功能:必須是3個及以上英文字母組成
def check_user(func):
    def inner(user, pwd):
        if not (user.isalpha() and len(user) >= 3):
            return '帳號不合法'
        res = func(user, pwd)
        return res
    return inner

# 爲登陸功能添加密碼檢驗功能:必須是3個及以上英文字母或數字組成
def check_pwd(func):
    def inner(*args, **kwargs):
        pwd = args[1]
        if not (pwd.isalnum() and len(pwd) >= 3):
            return '密碼不合法'
        res = func(*args, **kwargs)
        return res
    return inner

# 對登陸結果的修飾裝飾器:True=>登陸成功 False=>登陸失敗
def change_res(func):
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        if res == True:
            return '登陸成功'
        return '登陸失敗'
    return inner

# 裝飾器被執行的過程是從上至下
@check_user  # login = check_user(func=login) = inner
@check_pwd
@change_res
def login(user, pwd):  # 被裝飾的函數對象
    if user == 'owen' and pwd == '123':
        return True
    return False

user = input('user: ')
pwd = input('pwd: ')
res = login(user, pwd)

print(res)
裝飾器複雜函數
# 裝飾器:固定寫法
def outer(func):
    def inner(*args, **kwargs):
        pass
        res = func(*args, **kwargs)
        pass
        return res
    return inner

@outer
def f1():  # 任意函數
    pass
f1()
相關文章
相關標籤/搜索