1. 程式人生 > >Python函數講解

Python函數講解

必須 規範 bre 嵌套 增加 功能 修改 request baidu

目錄

  • python函數講解
    • 函數三問:
      • 什麽是函數?
      • 為什麽有函數?
      • 函數怎麽用?
      • 組成函數的四部分
    • 函數分類
      • 根據參數列表劃分
      • 根據返回值劃分
      • 函數的嵌套調用
    • 參數分類
      • 實參
      • 位置形參的範疇
      • 關鍵字形參範疇
      • 補充:args整體賦值
      • 參數總結
    • 函數對象
      • 名稱空間
      • global關鍵詞
      • 函數的嵌套定義
      • 作用域
      • 閉包
      • nonlocal關鍵字
      • 裝飾器
      • 運算
      • 遞歸
      • 匿名函數
    • 補充
      • 深拷貝 和 淺拷貝
      • 開放封閉原則:

python函數講解

函數三問:

什麽是函數?

函數就是可以完成特定功能的代碼塊,函數就是存放代碼的容器可以理解為一個裝有一些工具的箱子

為什麽有函數?

1.減少了代碼的冗余
2.讓程序增加了可擴展性
3.讓程序變得更容易維護

函數怎麽用?

# 函數必須先定義後引用!!!!

# 定義函數
def 函數名(參數們):
    函數體
    return '返回值','返回值2'
# 可以有多個返回值

"""
函數名:拿到函數的地址
函數名():拿到函數的地址,且執行函數中存放的代碼塊(函數體)
函數名(參數):執行函數且傳入參數

註:函數名加括號是調用,會得到函數的返回值,返回值和普通的變量一樣可以賦值,直接打印,使用運算,函數內沒有return默認返回None,return空也返回None
"""
def fu(num):
    print('傳入的num值:%s'% num)
    return '收到了'
  
res = fu(10)
# 這一個步驟調用了函數fu且傳入了參數10,並把返回值賦值給變量res
# 控制臺會打印:傳入的num值為:10
# res的值為:收到了

組成函數的四部分

"""
1.函數名:使用該函數的依據
2.函數體:完成特定功能的代碼塊
3.返回值:函數執行完成的返回結果
4.參數:執行程序所需要的條件信息
"""

函數分類

根據參數列表劃分

  • 無參函數:不需要外界傳入條件信息
def start():
    print('程序已啟動')
    return '關閉'
start()
  • 有參函數:需要外界資源
def login(usr, pwd):
    if usr == 'lmd' and pwd == '123':
        print('登錄成功')
    return '登錄失敗'

login('lmd', '123')

根據返回值劃分

  • 空返回:返回None
def demo(x, y):
    print(x + y)


def demo(x, y):
    print(x + y)
        return  # 用來強制結束函數類似於(break)
  • 一值返回
def demo(x, y):
    return x + y
  • 返回多值
def demo(x, y):
    return x, y # 返回裝有多個值的元祖
  
print(demo(10, 20))
(10, 20)

函數的嵌套調用

# 循環調用就是在一個函數的內部調用了另外一個函數

# 求倆個數的最大值
def max2(a1, a2):
    if a1 > a2:
        return a1
    return a2

# 三個
def max3(a1, a2, a3):
    m2 = max2(a1, a2)
    return max2(a3, m2)

# 四個
def max4(a1, a2, a3, a4):
    m3 = max3(a1, a2, a3)
    return max2(m3, a4)


print(max4(1, 2, 3, 4))

參數分類

先拋出兩個概念:

  • 實參:調用函數,在括號內傳入的實際值,值可以為常量,變量,表達式,函數等
  • 形參:定義函數,在括號內聲明的變量名,用來接受外界傳來值
  • 註:形參隨著函數的調用而產生,只在函數內部有效,隨著程序的調用結束而銷毀

實參

  • 位置實參:按照順序給形參傳值,順序亂了,接受的結果也亂的

  • 關鍵字實參:明確形參名和值給形參傳值,告訴系統你傳的值是給誰用的,可以不按照順序!但是一定要再位置實參之後!

    """
    混用:
    1.關鍵字實參出現必須在位置實參後
    2.多個位置實參還是按照位置傳參
    3.關鍵字傳參可以不按照順序給形參傳值
    """
    def fn(n1, n2, n3, n4, n5):
        return n1, n2, n3, n4, n5
    
    print(fn(30, 10, 20, n5=100, n4=99))
    (30, 10, 20, 99, 100)

位置形參的範疇

  • 位置形參
def fn(a, b, c):
    print(a, b, c)
    
# 位置形參可以由 位置實參 與 關鍵字實參 來傳值
fn(10, 20, 30)
fn(a=10, b=20, c=30)
  • 默認形參
def fu(a=10, b=20):
    return a, b

# 默認形參可以由 位置實參 與 關鍵字實參 來傳值,還可以不用傳值(采用自身默認值)
print(fu())
# (10, 20) ,不傳默認自己本身
print(fu(20, 10))
# (20, 10),可以位置實參傳值
print(fu(b=1, a=2))
# (2, 1)
print(fu(a=1000))
# (1000, 20),可以只傳一個
print(fu(a=1000 * 100, b=10 * 20))
# (100000, 200),可以運算傳值


# 混用
# 位置形參與默認形參同時存在,默認形參必須在後
def fn1(a,b,c=10,d=20):
    print(a, b, c, d)

# 位置形參必須傳值,默認形參分情況傳值
fn1(100, 200, d=1000)
  • 可變長形參
# 可變長形參會以 元組 形式接受 位置形參與默認形參未接受完的 所有傳入的位置實參,用索引來取第幾個
# 可變長形參只能由 位置實參 來傳值 
# *代表接收所有沒有被接收的位置實參,可以不用args來接收寫什麽都一樣前提是遵循命名規範,約定俗稱用*args

# 案例1:
def fu(n1, n2, *args):
    return n1, n2, args

print(fu(10, 20, 30, 40, 50))
# (10, 20, (30, 40, 50))

# 案例2:
def fu(n1, n2, *args):
    return n1, n2, args[0]

print(fu(10, 20, 30, 40, 50))
# (10, 20, 30)


# 小細節:可變長形參只能接受位置實參的值,位置實參還必須在關鍵字實參前,
#       導致默認形參只能由位置實參來傳值
fn(1, 20, 100, 200) # 1給a  20給b,不能用b=20傳  100,200給args

關鍵字形參範疇

# 前提:出現在*之後的形參稱之為關鍵字形參

#案例1:
def fu(n1, n2, *args, a, b=100):
    return n1, n2, args, a, b
  
# a,b都是出現在*之後的,都是關鍵字形參,必須通過關鍵字實參來傳值,順序無所謂,沒有被接收的位置實參都是被args接收

print(fu(10, 20, 30, 40, 50, b=11, a=9))
# (10, 20, (30, 40, 50), 9, 11)


# 可變長關鍵字形參:用來接收沒有被關鍵字形參接收完的關鍵字形參,也只能由關鍵字實參來傳值
# 用字典來存放數據
# 案例2:
def fu(**kwargs):
    return kwargs

print(fu(b=11, a=9, q=100, w=11, e=222))
# {'b': 11, 'a': 9, 'q': 100, 'w': 11, 'e': 222}

# 案例3:
def fn(**kwargs):
    print(kwargs)
fn(a=10,b=20)  # {'a': 10, 'b': 20}

dic = {'x': 100, 'y': 200}
fn(**dic)  # {'x': 100, 'y': 200}

# 拓展:
dic = {'b': 11, 'a': 9, 'q': 100, 'w': 11, 'e': 222}
print(*dic)
# b a q w e  得到的是key的值

補充:args整體賦值

技術分享圖片

參數總結

def fn(a, b, c=10, *args, d, e=20, f, **kwargs):
    pass
# 位置形參:a、b
# 默認形參:c
# 可變長位置形參:args
# 無初值關鍵字形參:d、f
# 有初值關鍵字形參:e
# 可變長關鍵字參數:kwargs


# 1.位置形參與默認形參: 能用 位置實參 關鍵字實參 傳值
# 2.可變長位置形參:只能 位置實參 傳值
# 3.所以關鍵字形參:只能 關鍵字實參 傳值

函數對象

# 函數名就是存放了函數的內存地址,存放了內存地址的變量都是對象,即 函數名 就是 函數對象
# 函數對象的應用
# 1 可以直接被引用  fn = cp_fn
# 2 可以當作函數參數傳遞 computed(cp_fn, 100, 20)
# 3 可以作為函數的返回值  get_cp_fn(cmd): return add
# 4 可以作為容器類型的元素  method_map: 對應關系中的值
def add(a, b):
    return a + b
def low(a, b):
    return a - b
def jump(a, b):
    return a * b
def full(a, b):
    return a / b
def quyu(a, b):
    return a % b
def computed(fn, n1, n2):
    res = fn(n1, n2) # 調用了函數且傳遞了參數n1,n2
    return res
method_map = {
    'add': add,
    'low': low,
    'jump': jump,
    'full': full,
    'quyu': quyu,
}
# 根據指令獲取計算方法
def get_cp_fn(cmd):
    if cmd in method_map:
        return method_map[cmd] # 取到功能字典中對應的value
    return add  # 輸入有誤用默認方法處理


while True:
    cmd = input('cmd: ')
    if cmd == 'quit':
        break
    cp_fn = get_cp_fn(cmd)
    result = computed(cp_fn, 100, 20) # 也就等於result = computed(加減乘除, 100, 20)
    print(result)

名稱空間

# 名稱空間:存放名字與內存空間地址對應關系的容器
# 作用:解決由於名字有限,導致名字重復發送沖突的問題

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

# 註:
# del 名字:可以移除查找的名字與內存空間地址的對應關系
# 加載順序:Built-in > Global > Local

global關鍵詞

num = 10

def fn():
    global num
    num = 20
    print(num)

print(num)
fn()  # 註:一定要調用函數,才能產生名字,並提升
print(num)
# global關鍵詞可以將Local的名字提升為Global的名字
# 一個文件中的Global名字就是一個,所以函數內部外部使用的名字都是一個
# 函數為執行的時候找的是全局的

函數的嵌套定義

def outer():
    num = 20
    def inner():
        print(num)  # inner就可以直接使用outer中的名字
    inner()
outer()

作用域

# 作用域:名字起作用的範圍
# 作用:解決同名字可以共存問題

# 四種作用域
# Built-in:內置作用域,所有文件所有函數
# Global:全局作用域,當前文件所有函數
# Enclosing:嵌套作用域,當前函數與當前函數的內部函數
# Local:局部作用域,當前函數

# 註:
# 不同作用域之間名字不沖突,以達到名字的重用
# 查找順序:Local > Enclosing > Global > Built-in
len = 10  #全局作用域
def outer():
    len = 20  # 外層函數的局部變量:Enclosing - 嵌套作用域
    def inner():
        len = 30
        print('1:', len)  # 30, inner -> outer -> global -> built-in 自己本身有找自己
    inner()
    print('2:', len)  # 20, outer -> global -> built-in 上一層outer
outer()
print('3:', len)  # 10, global -> built-in # 找全局

del len
print('4:', len)  # len地址, built-in ,全局沒有就找內置

閉包

# closure:被包裹的函數,稱之為閉包
# 完整的閉包結構:1.將函數進行閉包處理;2.提升函數名的作用域,將內部函數對象作為外部函數的返回值

def outer(url):
    def get_html():
        html = requests.get(url)
        print(html.text)
    return get_html
# 先預定義多個爬蟲方法,爬頁面操作並未執行
baidu = outer('https://www.baidu.com')
python = outer('https://www.python.org')
sina = outer('https://www.sina.com.cn')
# 什麽時候想爬什麽頁面就調用指定頁面的爬蟲方法
baidu()
sina()
baidu()

nonlocal關鍵字

# 作用:將 L 與 E(E中的名字需要提前定義) 的名字統一

# 應用場景:如果想在被嵌套的函數中修改外部函數變量(名字)的值

# 案例:

def outer():
    num = 10
    print(num)  # 10 
    def inner():
        nonlocal num
        num = 20
        p77rint(num)  # 20
    inner()
    print(num)  # 20

裝飾器

# 把要被裝飾的函數作為外層函數的參數通過閉包操作後返回一個替代版函數
# 被裝飾的函數:fn
# 外層函數:outer(func)  outer(fn) => func = fn
# 替代版函數: return inner: 原功能+新功能

def fn():
    print("原有功能")

# 裝飾器
def outer(tag):
    def inner():
        tag()
        print(新增功能")
    return inner
fn = outer(fn)              
              
fn()
  • @語法糖: @外層函數
def outer(f):
    def inner():
        f()
        print("新增功能1")
    return inner
              
def wrap(f):
    def inner():
        f()
        print("新增功能2")
    return inner              

@wrap  # 被裝飾的順序決定了新增功能的執行順序
@outer  # <==> fn = outer(fn): inner      
def fn():
    print("原有功能")
  • 有參有返的函數被裝飾
def check_usr(fn):  # fn, login, inner:不同狀態下的login,所以參數是統一的
    def inner(usr, pwd):
        # 在原功能上添加新功能
        if not (len(usr) >= 3 and usr.isalpha()):
            print('賬號驗證失敗')
            return False
        
        # 原有功能
        result = fn(usr, pwd)
        
        # 在原功能下添加新功能
        # ...
        
        return result
    return inner


@check_usr
def login(usr, pwd):
    if usr == 'abc' and pwd =='123qwe':
        print('登錄成功')
        return True
    print('登錄失敗')
    return False

# 總結:
# 1.login有參數,所以inner與fn都有相同參數
# 2.login有返回值,所以inner與fn都有返回值

"""
inner(usr, pwd):
    res = fn(usr, pwd)  # 原login的返回值
    return res


login = check_usr(login) = inner

res = login('abc', '123qwe')  # inner的返回值
"""
  • 裝飾器的最終寫法
def wrap(fn):
    def inner(*args, **kwargs):
        print('前增功能')
        result = fn(*args, **kwargs)
        print('後增功能')
        return result
    return inner

@wrap
def fn1():
    print('fn1的原有功能')
@wrap
def fn2(a, b):
    print('fn2的原有功能')
@wrap   
def fn3():
    print('fn3的原有功能')
    return True
@wrap
def fn4(a, *, x):
    print('fn4的原有功能')
    return True

fn1()
fn2(10, 20)
fn3()
fn4(10, x=20)
  • 帶參裝飾器
# 了解
def outer(input_color):
    def wrap(fn):
        if input_color == 'red':
            info = '\033[36;41mnew action\33[0m'
        else:
            info = 'yellow:new action'

        def inner(*args, **kwargs):
            pass
            result = fn(*args, **kwargs)
            print(info)
            return result
        return inner
    return wrap  # outer(color) => wrap


color = input('color: ')
@outer(color)  # @outer(color) ==> @wrap  # func => inner
def func():
    print('func run')

func()

運算

  • 三目運算
# 三目(元)運算符:就是 if...else...語法糖
# 前提:簡化if...else...結構,且兩個分支有且只有一條語句
# 註:三元運算符的結果不一定要與條件直接性關系

cmd = input('cmd: ')
print('可以轉化為數字') if cmd.isdigit() else print('不可以轉化為數字')


a = 20
b = 30
res = a if a > b else b  # 求大值,如果a大於b返回a否則返回b
print(res)


res = 'b為小值' if a > b else 'a為小值'  # 求小值
print(res)
  • 推導式
# 列表推導式:[v for v in iterable]
dic = {'a': 1, 'b': 2, 'c': 3}  # => [('a', 1), ('b', 2), ('c', 3)]
res = [(k, v) for k, v in dic.items()]

# 字典推導式:{k: v fro k, v in iterable}
ls = [('a', 1), ('b', 2), ('c', 3)]  # => {'a': 1, 'b': 2, 'c': 3}
res = {k: v for k, v in ls}

遞歸

# ***
# 遞歸:回溯與遞推 
# 回溯:詢問答案的過程
# 遞推:推出答案的過程

# 本質:函數的自我調用
# 直接:自己調自己
# 間接:自己調別人,別人最終由調回自己

匿名函數

# 匿名函數:沒有名字的函數
# 語法:lambda 參數列表: 一個返回值表達式

# 重點:
# 1.匿名函數:沒有函數名,沒有函數體,只有一個返回值
# 2.關鍵字:lambda  |  參數列表省略()  |  返回值return關鍵字也被省略

# 應用場景:
# 1.匿名函數函數地址可以被一個變量接受,該變量就可以作為函數名來使用,但就違背了匿名初衷
# 2.結合內置函數來使用: 內置函數某些參數需要一個函數地址,
#       -- 可以賦值一個有名函數名,也可以直接賦值匿名函數

補充

深拷貝 和 淺拷貝

淺拷貝:指向的都是同一內存地址

深拷貝:開辟新的內存空間來存放

技術分享圖片

開放封閉原則:

不改變調用方式與源代碼上增加功能
1.不能修改被裝飾對象(函數)的源代碼(封閉)
2.不能更改被修飾對象(函數)的調用方式,且能達到增加功能的效果(開放)

Python函數講解