day12- 函式

似小陈ya發表於2024-04-02
我們學習程式設計的時候,一學到函式就感覺很難,其實函式很簡單,聽我給你細細道來,在我們之前的學習中,我們最常用的一個操作,列印輸出print(),其實這就是我們最先接觸的函式,只不過這是由Python原始碼中編寫好的函式,那我們來看下print()函式到底是怎麼寫的?
從中我們可以看到用到def關鍵字,然後接一個print還有一個括號並且裡邊有內容,這樣我們就可以使用print列印輸出這個功能了,下邊我們詳細介紹函式

1、什麼是函式

1、為什麼有函式

函式是組織好的,可重複使用的,用來實現單一、或相關聯功能的程式碼段。這句話怎麼理解,比如我們要比較2和3的大小,我們可以直接寫
num1 = 2
num2 = 3
if num1 < num2:
    print("2小於3")
else:
    print("2大於3")
那如何我們下次又要比較5和6的大小呢,把程式碼可以在寫一遍,如果下次再比較10和11的大小呢,這時候就可以用到函式

2、函式的定義

我們一般使用 def 關鍵詞作為宣告,後面緊接著是函式識別符號名稱與圓括號 (),最後接一個冒號 :
def 函式名(引數列表):
    # 函式體
    return [返回值]
函式體 必須有縮排,在函式體我們編寫要實現的功能邏輯
  • 函式名:見名識意,通俗點,不建議使用單字母
  • 引數列表:設定該函式可以接收多少個引數,多個引數之間用逗號( , )分隔(也可不帶引數,但是括號不能省略)
  • return [返回值] :返回該函式的返回值。可以有返回值也可以沒有

小栗子

熟悉招式後,我們就可以練習,在我們上邊還有一個問題,比較兩個數的大小,可能有很多兩個數,我們用函式實現
# 定義比較兩個數字的函式
def compare_numbers(num1, num2):
    # 如果num1小於num2
    if num1 < num2:   
        # 列印num1小於num2
        print("{}小於{}".format(num1, num2))
    # 否則
    else:
        # 列印num1大於num2
        print("{}大於{}".format(num1, num2))
這樣我們就寫好了這個函式

3、函式的呼叫

呼叫函式是什麼意思,我們不是把函式都寫好了嗎,我們來執行上邊的程式碼,比較兩個數大小的函式
發現執行結果為空
現在懂了吧,函式相當於我們的工具,可以是扳手,也可以是螺絲刀,如果沒有手使用,就會毫無效果
所以我們得呼叫函式,才能使用函式的功能,來看下呼叫:
語法格式如下所示:
函式名(引數)    # 第一種呼叫方式,如果有引數就填寫對應引數,無引數可以不寫
返回值 = 函式名(引數)    # 第二種呼叫方式,如果函式有返回值,得變數來接收該值
括號裡傳的引數需要額外注意,定義函式的時候有幾個引數,呼叫的時候就傳幾個引數,並且要保持順序
在我們小栗子中,我們來呼叫一下
compare_numbers(2, 3)
compare_numbers(6, 5)
compare_numbers(10, 11)


# 執行結果
2小於3
6大於5
10小於11

看,這樣以後我們不管在比較多少次兩個數的大小,就不需要在編寫重複的程式碼了
在我們實際程式設計工作中,如果不是單一功能的程式碼,也可以不用使用函式,這個要看情況使用

2、函式的引數

1、函式的值傳遞和引用傳遞

函式中有形參和實參,我們拿一個具體例子,傳入一個數字,返回這個數字的2倍數字
def toal(num):
    return num * 2
在我們定義的函式中,num就是我們的形參,相當於放個位置,不做實際使用
我們呼叫上邊的函式
toal(5)
我們呼叫函式,括號中實際傳入5,這就是我們的實參,函式實際會拿實參來進行邏輯處理
好,我們理解了形參和實參後,就來看下我們的值傳遞和引用傳遞:
  1. 值傳遞:適用於實參型別為不可變型別(字串、數字、元組)
  2. 引用(地址)傳遞:適用於實參型別為可變型別(列表,字典)
那值傳遞和引用傳遞有什麼區別啊?
值傳遞,形參的值發生改變,不影響實參的值
引用傳遞,改變形參的值,實參的值也會一同改變
我們來做個例子參考下:
# 定義了一個函式
def change(a):
    a += 100  
    print("函式內:", a)
    
a = 100
change(a)
print("函式外:", a)
猜猜看,函式內的值是多少,函式外的值是多少
執行結果:
函式內: 200
函式外: 100
大家猜的準嗎
這個就屬於值傳遞,雖然形參裡的改變了,但是實際我們定義的實際引數值雖然在函式中進行了改變,但是實際的值是不會改變的
當然這種適用於數字,字串,元祖等不可變型別
那引用傳遞是什麼呢?
# 函式的引用傳遞
def f(a):
    a[0] = 100
    print('函式內:', a)

a = [1, 2, 3, 4]
f(a)
print('函式外:', a)
執行結果:
函式內: [100, 2, 3, 4]
函式外: [100, 2, 3, 4]
這下大家應該都猜對了吧,引用傳遞,傳遞給函式引數是實際引用地址,修改形參中的值後,引用地址就會改變,所以傳遞給實參的值也會進行改變

2、位置引數

位置引數,也叫必傳引數,顧名思義,引數是必須要傳入的,並且還要按照位置順序傳入,如果沒有按照上邊要求,會報錯或者得到結果不一致
直接看例子
def hello(name, message):
    print(f"{name}! {message}")
傳入1個引數
hello("小華")

# 執行結果
TypeError: hello() missing 1 required positional argument: 'message'
傳入3個引數
hello("小華", '你好', '哈哈')

# 執行結果
TypeError: hello() takes 2 positional arguments but 3 were given
傳入2個引數
hello("小華", '你好')
hello("你好", '小華')

# 執行結果
小華! 你好
你好! 小華
所以,在呼叫函式時,一定要確定好位置

3、預設引數

預設引數,函式定義時,如果給某個引數提供一個預設值,這個引數就變成了預設引數
直接看程式碼:
def hello(name='小華', message):
    pass
 
 def hello(message,name='小華'):
    pass
上述兩個程式碼有問題嗎,在編輯器中發現第一個函式報錯了
原因就在預設引數必須在位置引數後面!
那預設值引數有什麼用呢?
預設值參數列示這個引數就算不給傳參,也有一個預設值,不會報錯
def test1(a, b=1):
    print(a, b)
    

test1(0)
test1(0, 2)
執行結果
0 1
0 2
程式碼是沒問題的

實戰練習

Python程式設計題
# 預設引數傳空列表
def my_function(a=[]):
    a.append('A')
    print(a)


my_function()
my_function()
my_function()
不實際執行程式碼,答案是什麼?
那不很簡單嗎
['A']
['A']
['A']
如果是這個答案,肯定錯誤
真正的答案是:
['A']
['A', 'A']
['A', 'A', 'A']
預設引數傳入空列表,在我們函式的引用傳遞中我們知道,如果引數傳入的是列表,表示傳入的引數的引用地址,而後邊列表改變了,預設參數列示這個預設值也對應改變了,所以呼叫一次函式後續再次呼叫這個函式的引數的預設值就會改變

4、可變引數

可變引數也叫動態引數,為什麼有可變引數呢
比如我們有求2個數的求和函式
def add(a, b):
    return a + b
之後又需要3個數的求和函式
def add(a, b,c):
    return a + b + c
那之後,我們要求100個數的和,應該怎麼做呢
這時候就用到了我們的動態引數
Python的動態引數有兩種,分別是*args**kwargs,這裡面的關鍵是一個和兩個星號的區別
至於叫*a或者*as是沒有區別的,只不過Python官方預設讓我們使用這個*args**kwargs

*args

定義中使用星號 * 來表示。它允許函式接受任意數量的位置引數,並將它們作為一個元組傳遞給函式
# 函式可變引數
def my_func(*args):
    for arg in args:
        print(arg)


my_func('hello', 'world', 123)
列印結果
hello
world
123
如果傳入的是一個列表呢
def my_func(*args):
    for arg in args:
        print(arg)


my_func([1, 2, 3, 4])
猜猜列印的結果是什麼
正確答案:[1, 2, 3, 4]
為什麼不是1,2,3,4分別列印出來呢,因為當傳入的引數為列表,是作為一個整體傳入的,那接受會用一個元祖接受就是args = ([1, 2, 3, 4],),遍歷元祖整個列表表示為1個元素會列印輸出
那如果我們想要1,2,3,4作為單獨的元素一個個傳入給引數呢,我們可以用解包運算子 * 來將列表的元素作為獨立的引數傳遞給函式
my_func(*[1, 2, 3, 4])
再來看看列印結果
1
2
3
4
如果可變引數和位置引數和預設引數聯合使用呢,看下邊這個例子:
def myfun(a, *b, c=None):
    print(a)
    print(b)
    print(c)
myfun(1, 2, 3, 4)

猜猜a,b,c各自的值,執行結果:
1
(2, 3, 4)
None

a拿到了1,b作為可變引數,把後邊的值都接受了,所以c沒有拿到值
所以,我們如果想要給c賦值,我們就要指定引數值,這就用到了我們的關鍵字引數

5、關鍵字引數

我們在呼叫函式時,可以以 “引數名 = 引數值” 的形式傳遞引數,這種我們可以認為強行賦值,不需要傳遞,所以不受位置影響,還是上邊的例子,我們要給c賦值,這時候我們就用關鍵字引數
def myfun(a, *b, c=None):
    print(a)
    print(b)
    print(c)
myfun(1, 2, 3, c=4)

# 執行結果
1
(2, 3)
4

那如果我們把給c賦值放在最前面可以嗎?
myfun(c=4, 2, 3)

# 執行結果
 myfun(c=4, 2, 3)
               ^
SyntaxError: positional argument follows keyword argument

程式碼報錯了,因為如果有關鍵字引數,呼叫的時候順序必須放在最後面
myfun(2, 3,c=4)

6、可變關鍵字引數

**kwargs

定義中使用星號 ** 來表示,它允許函式接受任意數量的鍵值對也就是關鍵字引數,並將它們作為一個字典傳遞給函式
# 函式可變引數
def my_func(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")


my_func(name="小華", age=25, city="beijing")

列印結果
name: 小華
age: 25
city: beijing

這是第一種傳遞方式:key=value 的方式傳參
如果我們傳入整個字典,是否可以?
# 函式可變引數
def my_func(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

my_dict = {
    "name": "小華",
    "age": 25,
    "city": "beijing"
}
my_func(my_dict)

執行結果:報錯
這是因為,我們傳入整個字典,屬於位置引數傳遞方式,但是我們的函式沒有任何位置引數,所以型別就會報錯,如果我們想要以整個字典的方式傳入,我們可以使用字典解包運算子 **,這樣就可以將字典中的鍵值對分別作為關鍵字引數傳遞給函式。

7、引數的順序

一個函式中包含多種引數的組合,必須遵守這樣的順序:位置引數(必傳引數),預設引數,單星號引數,雙星號引數
定義引數時,位置引數都必須在關鍵字引數之前
def my_func(a=1, b):
    print(a, b)
    
 # 這種寫法就會報錯,關鍵字引數必須放在位置引數後邊

可變引數中,單星號要在雙星號之前
def my_func(a, b, c=0, *args, **kwargs):
    print(a, b, c, args, kwargs)
    

my_func(1, 2, 3, 4, 5, x=9, y=10)
my_func(1, 2, x=9, y=10)
my_func(1, 2, 3, (4, 5), a=9, b=10)
my_func(1, 2, 3, x=9, y=10, z=11)
my_func(1, 2, 3, x=9, y=10, z=11, a=12)

不執行程式碼,猜猜結果
還有一個小細節,單星號後面的都是關鍵字引數,無論是否帶=;
def my_func(a, b, c=0, *args, d , **kwargs):
    print(a, b, c, args,d, kwargs)

上述中的d在*args後面,因此屬於關鍵字引數,雖然沒有帶=

3、函式的返回

1、返回是什麼

Python函式,可以用 return 語句指定應該返回的值,該返回值可以是任意型別 語法格式:return [返回值]

2、返回有兩個作用

  • 提前退出函式
  • 返回具體的資料
def add(a, b):
    return a + b
    print(a + b)


add(3, 4)

上述一個求和的程式碼,我們執行後會返回什麼,試一試,發現返回的是啥也沒有,為什麼呢?我們的程式碼裡不是有列印a+b嗎,是的,但是執行到return的時候,就直接跳出函式了,所以return 語句會提前退出函式
那我們想看下我們返回的資料應該怎麼辦,有返回值的時候,我們可以將函式賦值給一個變數,變數儲存函式的返回值,然後列印輸出
num = add(3, 4)
print(num)

當然我們如果不需要返回,也可以不寫return,這樣就會預設返回None的
def add(a, b):
    a + b
num = add(3, 4)
print(num)

3、None是什麼?

None 是一個特殊的常量,表示空或缺失,和 False 不同,它不表示 0,也不表示空字串,而表示沒有值,也就是空值。
None 常用於 assert、判斷以及函式無返回值的情況

4、return多個值

我們可以使用return返回多個值
def add(a, b):
    return a + b, a - b

上述函式返回兩個數的和,和兩個數的差,我們列印看一下:
print(add(5, 3))

# 執行結果
(8, 2)
return 多個返回值,得到的是一個元組,後續我們可以根據元祖取值
這裡,我們也可以分別接受一下對應的值
he, cha = add(5, 3)
print('和:', he)
print('差:', cha)

day12練習

初級:
1.定義函式名add,接受兩個數字引數,求和返回值
2.定義函式名Area,引數接受r半徑的值,求圓的面積,(計算公式:π*r*r)
3.定義函式名season,接受一個月份引數,返回其對應的季節(春夏秋冬)
4.定義函式名reverse,接受一個字串引數,並返回逆序後的字串(比如傳入abcdef,返回fedcba)
中級:
1.編寫一個函式 find_max(numbers),接受一個整數列表 numbers,並返回列表中的最大值
2.編寫一個函式 is_prime(n),判斷一個正整數 n 是否為素數(質數)
3.編寫一個函式 remove_list(numbers),接受一個整數列表 numbers,並移除列表中的重複元素,返回去重後的列表
高階
1.請寫一個函式 equals ,該函式引數為任意數量的數字,請在函式中統計出這些引數數字中重複的數字有多少個
比如 :
equals(3, 4, 3, 4, 1, 6, 2) 

輸出為:
數字 3 出現了 2 次
數字 4 出現了 2 次
數字 1 出現了 1 次
數字 6 出現了 1 次
數字 2 出現了 1 次

相關文章