1. 程式人生 > >Python (六) 函式

Python (六) 函式

什麼時函式?

通俗的講,函式就是寫一段程式碼實現了某個小功能,然後把這些程式碼集中到一塊,起一個名字,下次可以根據這個名字再次使用。

函式有什麼作用?

  1. 方便程式碼的重用。
  2. 分解任務,簡化程式邏輯。
  3. 是程式碼更加模組化。

函式有哪幾類?

  1. 內建函式(Python自帶函式)
  2. 第三方函式(第三方公司或者第三方個人開發出來的函式)
  3. 自定義函式

引數傳輸的方式:

多個引數

使用位置實參:直接用形參和實參一一對應

使用關鍵字引數:引數名稱=引數(關鍵字引數)不需要嚴格按照順序

使用容器作為實參:可以將一個列表或者字典作為一串引數傳入函式。

不定長引數

1.在引數前面加一個*號代表不定長引數,函式中將會把這個變數作為元組來處理。(預設用*args)

之所以將這個變數當作元組處理而不作為列表處理,是因為在函式體中我們要儘可能直接改變引數。

2.在引數前面加倆個*號,以字典的方式來使用函式。這種方式定義的函式必須使用關鍵字引數進行傳遞。(預設使用**kwrgs)

這裡有一個裝包和拆包的概念,所謂裝包就是將單獨的個體整合成一個整體集合,例如使用*args和**kwrgs;而拆包則是將一個整體拆分為單獨的個體。讀取*args和**kwargs。

預設引數:

預設引數指的是引數在大多數情況下都是預設的情況,預設引數在定義的時候需要使用關鍵字引數的方式傳遞,下面的形參sombody就是一個預設引數。

def hit( sombody = "呼呼") :
    print("我想打",sombody)ha
hit("nim")

函式引數的一些注意點:

首先要明確在函式中什麼時值傳遞,什麼是引用傳遞。

值傳遞:僅僅是將資料的副本傳遞過去,傳遞過去的值的地址跟原資料不一致,修改副本不會引起原來資料的變化。

引用傳遞:所謂引用傳遞就是地址傳遞,通過相同的地址操控同一份地址,在函式中改變形參會引起實參的變化。

一定要注意在Python中函式一般 都是引用傳遞。(地址傳遞)

如果要使用原資料的副本進行函式操作,可以使用切片的方法進行拷貝,傳遞過去的就是一份原資料的副本。

如果是不可變的資料型別,在函式中進行修改一定會改變它的地址;如果是可變資料型別,採用簡單的新增刪除不會修改地址,而直接重新賦值會出現新的地址。

函式的返回值:

當我們需要接收函式的處理結果時,就必須使用return來接收這個結果。一定注意:return只會執行一次,後面的程式碼不會再被執行。

return的結果可以時元組,列表,字典。

函式的表述資訊:

當我們在寫一個函式時一定要寫一個函式的描述資訊,寫在開頭用三個引號括起來。

在使用中可以使用help(函式名)來檢視函式的功能。

函式描述資訊必須包含的內容:

  1. 說明當前函式的功能。
  2. 說明函式引數的資訊含義,資料型別,有沒有預設值,有的話說明預設值。
  3. 說明函式的返回結果。

函式的高階功能:

偏函式:

什麼時偏函式?當一個函式的預設引數大多數情況下時時不變的,但是有希望在指定的情況下改變其預設值,以此生成新的函式就是偏函式。我們可以通過functools模板的partial函式來改變函式的預設引數。

下面是一個使用偏函式的例子:

import functools


def test(a, b, c, d=2):
    print(a + b + c + d)


test(1, 2, 3)
newTest = functools.partial(test, c=2)
newTest(1,1)

高階函式:

當一個函式的引數接收的是另一個函式,那麼這個函式就是一個高階函式。

下面是一個高階函式的例子,其中的sorted()就是一個高階函式

l = [{"name":"s2","age":18} , {"name":"sz2","age":19} , {"name":"kc","age":20}]

def getKey(x):
    return x["age"]

result = sorted(l , key = getKey)
print(result)

注意點:在將函式作為引數時使用的只是函式的名稱,也就是說不用帶上函式引數列表(括號)

返回函式:

返回引數指的是函式內部返回的資料是另外一個函式,把這樣的操作稱之為“返回引數”。

def getFun(flag) :
    def sum( a, b , c) :
        return a + b + c
    def jianfa( a, b, c) :
        return a - b - c
    if flag == "+" :
        return sum
    elif flag == "-" :
        return jianfa

function = getFun("+")
print(function(1, 2, 3))

匿名函式:

匿名函式稱之為lambda函式,顧名思義就是指沒有名字的函式,匿名函式只適合用於簡單操作的函式。

使用的格式:lambda 引數1,引數2....:表示式(只能寫一個表示式)表示式的結果就是匿名函式的返回值。總之先使用lambda作為統一函式名,再加上一個表示式,表示式的結果就是返回值。

下面是一個使用匿名函式的例子

result = (lambda x, y: x + y)(1, 2)
print(result)

l = [{"name": "s2", "age": 18}, {"name": "sz2", "age": 19}, {"name" :"kc","age": 20}]
result = sorted(l, key = lambda x: x["age"])
print(result)

閉包函式:

在函式巢狀的前提下,內層函式引用了外層函式變數(包括引數),而外層函式又把n內層函式當作返回值進行返回。

這個內層函式加上所引用的外層變數稱之為閉包。

def test():
    a = 10

    def test2():
        print(a + 7)
    return test2


newFun = test()
newFun()


def line_config(content, length):
    def line():
        print("-"*(length//2) + content + "-"*(length//2))
    return line


line1 = line_config("還哦",40)
line1()

line2 = line_config("還有我",80)
line2()

注意點:

在使用閉包函式時,如果要修改外層函式,必須在變數前面加上nonlocal宣告,否則會被當作閉包內新定義的變數。

當函式被執行時裡面的程式碼才會執行,在定義時是不會被執行的。

函式裝飾器:

利用其它函式取裝飾原本的函式。

裝飾器適合做重複性,冗餘度非常大的程式碼段落。一些基礎業務邏輯程式碼的冗餘度非常大,導致程式碼的複用性比較差,程式碼的維護性比較差。當我們要修改程式碼時,儘量在重用性非常高的地方進行修改,這時候裝飾器的優勢非常大。

函式的使用要遵循單一職責,函式內容單一優先實現函式名的功能。

下面是一些使用函式裝飾器的案例:

def checklogin(func) :
    """
    這是一個要用來做裝飾器的函式,這個函式可以讓其它函式增加登陸驗證的操作
    """
    def inner() :
        """
        這是一個閉包函式,用來接收其它函式功能,使他們的功能和登陸驗證結合起來
        """
        print("正在登陸驗證....")
        func()
    return inner

@checklogin    # 裝飾器的寫法,相當於對下面的函式進行更新,是下面的發說說函式增加一個登陸驗證操作
def fss() :
    print("發說說!")
# fss = checklogin(fss)

@checklogin   # 裝飾器的寫法,相當於對下面的函式進行更新,是下面的發圖片函式增加一個登陸驗證操作
def ftp():
    print("發圖片!")
# ftp = checklogin(ftp)

fss()
ftp()

案例2:裝飾器疊加:從上到下進行疊加;從下到上進行疊加。

def print_line(funce2) :
    """
    這是一個用來做裝飾器的函式,給其它函式增加列印------的功能
    """
    def inner() :
        print("-" * 30)
        funce2()
    return inner

def print_star(funce2) :
    """
    這是一個用來做裝飾器的函式,給其它函式增加列印*****的功能
    """
    def inner() :
        print("*" * 30)
        funce2()
    return inner


@print_star    #使用上面的倆個裝飾器連續修飾,從下往上看。
@print_line
def print_name() :
    print("I am Linjunhan !")

案例3:對有引數的函式進行裝飾

def zsq(func) :
    """
    這是一個用來做修飾器函式,用來列印-------資訊
    """
    def inner(*args ,**kwargs) :
        print("-" * 30)
        func(*args ,**kwargs)
    return inner

@zsq 
def pnum(num) :
    print(num)

@zsq
def pnum2(num1,num2) :
    print(num1,num2)

案例4:對有返回值的函式進行修飾。

def zsq(func):
    def inner(*args, **kwargs):
        print("-" * 30)
        res = func(*args, **kwargs)
        return res
    return inner


@zsq  # pnum = zsq(pnum)
def pnum(num1, num2, num3):
    return num1 + num2 + num3


print(pnum(1, 2, 3))

案例5:帶有裝飾的修飾器 ,通過@修飾器(引數)的方式呼叫這個函式,並傳遞引數;並把返回值再次當作修飾器來使用。

def zsqs(char):
    def zsq(func):
        def inner():
            func()
            print(char * 30)

        return inner
    return zsq


@zsqs("*")  # 利用函式呼叫zsq修飾器。
def f1():
    print("666666")

f1()

生成器(特殊的迭代器):

生成器具有迭代器的特徵:

惰性計算資料,節省記憶體,一個一個拿出來用。

能夠記錄資料,並通過next()函式,訪問下一個函式。

具備可迭代性。

建立方式:把列表推導式[]改為()

生成器函式:

利用表示式建立:

l = (i for i in range(1, 100000) if i % 2 == 0)
print(next(l))
print(next(l))
print(next(l))

建立方式2:

def test():
    print("xxx")
    yield 1
    print("a")

    yield 2
    print("b")

    yield 3
    print("c")

    yield 4
    print("d")

    yield 5
    print("e")


g = test()
print(g)

超出範圍,迭代停止。

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

f = fib(10)
print('fib(10):', f)
for x in f:
print(x)

# 超出範圍,迭代停止
def test2() :
    print("xxxx")
    res1 = yield 1
    print(res1)

    res2 = yield 2
    print(res2)

    res3 = yield 3
    print(res3)


g = test2()

# print(g.__next__())   ##採用.__next__()的方法來訪問迭代器
#利用生成器的.send方法,這個方法有一個引數,時用來指定上一次被掛起的yield的返回值,如果第一次呼叫,引數必須為空。
# print(g.send())
print(g.__next__())
print(g.send("6666"))
g.close()    # 生成器的關閉方法

 遞迴函式(在函式內部再次呼叫函式)

def jiecheng(n):
    """
    這個遞迴函式用計算引數n的階乘
    :param n:
    :return:
    """
    if n == 1:
        return 1
    return n * (n - 1)
print(jiecheng(3))