函式
引言:比如植物大戰殭屍,這個遊戲本身也是由程式碼編寫,現在假設有一種豌豆射手,每發射一次炮彈會執行100行邏輯程式碼
如果我在程式,每當需要發射炮彈的時候,都要編寫100行邏輯程式碼,就會覺得該程式過於冗餘,程式碼重複度較高。
解決方案:
如果我將這100行程式碼放到一個區域中,然後給這個區域起一個名字,今後在需要發射炮彈的程式碼邏輯中,透過這個名字就可以呼叫起這100行程式碼。這個區域【程式碼段】在python中稱之為函式,先將函式定義出來,並對該函式起一個名字,將來在合適的地方透過函式名呼叫該函式,執行該函式的內部邏輯。
函式的定義
- 語句定義格式:
# 使用python中的關鍵字 def
def 函式名(...):
函式程式碼邏輯
函式使用特點
- 函式不呼叫不執行
- 定義函式必須在呼叫之前出現
函式的引數
-
引數種類
- 形式引數:指的是函式定義時,小括號中定義的引數
- 實際引數:指的是將來呼叫函式時,實際傳入進去的具體的值
def fun1(hhs, zcy): # hhs zcy是形式引數,名字自定義 print(hhs + zcy) a1 = int(input("請輸入第一個數值:")) b1 = int(input("請輸入第二個數值:")) fun1(a1,b1) # a1 b1 是實際引數,可以是變數,也可以是具體的值本身
-
引數的傳值方式
- 位置傳參
def show1(a, b, c): print(f"a:{a},b:{b},c:{c}") # a:11,b:22,c:33 show1(11, 22, 33)
- 關鍵字傳參 【透過形參的名字傳參】
def show1(a, b, c): print(f"a:{a},b:{b},c:{c}") show1(b=100, c=200, a=300)
- 混合傳參
def show1(a, b, c): print(f"a:{a},b:{b},c:{c}") show1(100, c=200, b=300)
注意: 混合傳參的時候,前面沒有關鍵字的實參是會按照形參的位置來的,後面關鍵字傳參可以順序不一樣。
函式傳參的場景【擴充套件知識】
# 未使用函式 ''' 編寫登入時的邏輯程式碼 ''' 編寫傳送郵件的程式碼 50行 ''' 編寫註冊時的邏輯程式碼 ''' 編寫傳送郵件的程式碼 50行
# 使用函式 def send_email(xxx,xxx): 編寫傳送郵件的程式碼 ''' 編寫登入時的邏輯程式碼 ''' send_email(xxx,xxx) ''' 編寫註冊時的邏輯程式碼 ''' send_email(xxx,xxx)
def send_email(msg_to, send_info): import smtplib from email.mime.text import MIMEText from email.header import Header msg_from = '1165872335@qq.com' # 傳送方郵箱 passwd = 'owbciardnivafija' # 填入傳送方郵箱的授權碼(填入自己的授權碼,相當於郵箱密碼) # msg_to = ['','',''] subject = "郵件資訊" # 主題 # 生成一個MIMEText物件(還有一些其它引數) msg = MIMEText(send_info) # 放入郵件主題 msg['Subject'] = subject # 也可以這樣傳參 # msg['Subject'] = Header(subject, 'utf-8') # 放入發件人 msg['From'] = msg_from # 放入收件人 # 透過ssl方式傳送,伺服器地址,埠 s = smtplib.SMTP_SSL("smtp.qq.com", 465) # 登入到郵箱 s.login(msg_from, passwd) # 傳送郵件:傳送方,收件方,要傳送的訊息 s.sendmail(msg_from, msg_to, msg.as_string()) print('成功') p = input("請輸入要接收郵件的qq郵箱地址:") info = input("請輸入要傳送的內容:") send_email(p, info)
- 預設值傳參
需求:呼叫一個函式,傳入一個大字串和一個小字串,查詢小字串在大字串中出現的次數,呼叫函式的時候,可以不傳小字串,預設查詢字元'a'在大字串中的出現次數。
def str_number(big_str, small_str='a'): # 定義函式時,可以設定形式引數的值,作為預設值 # input輸入dadqwwfwqfjwqaoiaiosijpoaospjfqwasaapjosaja list1 = list(big_str) counts = list1.count(small_str) print(f"{small_str}在大字串中總共出現了{counts}次。。。") str_number('dadqwwfwqfjwqaoiaiosijpoaospjfqwasaapjosaja') # 呼叫時若不傳入第二個引數,使用的就是定義時的預設值 str_number('dadqwwfwqfjwqaoiaiosijpoaospjfqwasaapjosaja','f') # 若傳入第二個引數,使用的就是實際傳入的值
- 動態傳參
未使用動態引數時,解決需求,比較麻煩,引數需要另外定義一個函式
# 需求1:定義一個函式,將來傳入兩個int型別的值求和
def sum1(a, b):
print(a + b)
sum1(10, 20)
def sum2(a, b, c):
print(a + b + c)
# 需求2:定義一個函式,將來傳入三個int型別的值求和
sum2(10, 20, 30)
使用動態引數,只需要定義一個函式就可以了
def sum1(*num):
# 這裡的num 是一個元組,接收若干個將來呼叫時傳入的實參
n = 0
for i in num:
n = n + i
print(f"總和為:{n}")
sum1(10, 20) # (10, 20)
sum1(10, 20, 30) # (10, 20, 30)
sum1(10, 20, 30, 40) # (10, 20, 30, 40)
使用動態引數時的注意事項:
傳參的內容,多個引數的型別可以是不一樣的
def sum1(*num): # 這裡的num 是一個元組,接收若干個將來呼叫時傳入的實參 # n = 0 # for i in num: # n = n + i # print(f"總和為:{n}") print(num, type(num)) # sum1(10, 20) # (10, 20) # sum1(10, 20, 30) # (10, 20, 30) # sum1(10, 20, 30, 40) # (10, 20, 30, 40) # sum1(11) # (11,) # sum1(11, '小虎', [11, 22, 33]) # (11, '小虎', [11, 22, 33]) sum1((11,22,33)) # ((11, 22, 33),)
傳入兩個**的動態引數
def sum1(**num): print(num, type(num)) sum1(name='小虎', age=18)
結論:
* : 表示傳入的每一個單獨的元素都被封裝成一個元組 ** : 表示傳入的是一個一個的鍵值對,所有的鍵值對會被封裝成一個字典 我們今後開發的時候,定義動態引數時,起名字是固定的,若一個*的動態引數,名字起為*args, 若**的動態引數,名字起為**kwargs def show1(a, b, *args, **kwargs): print(args, type(args)) print(kwargs, type(kwargs)) # show1(11,22,33,44,name='小虎',address='合肥') show1(11, 22, 33, 44, name='小虎', address='合肥')
函式的返回值
有些函式,我們呼叫完之後,是能夠得到結果的,理論上來說,python中所有的函式都有返回值
python中提供了一個關鍵字給我們在函式中使用,表示呼叫完後返回的值,這個關鍵字叫做return
- 例子
def sum1(a, b):
c = a + b
return c
res1 = sum1(10, 20)
print(res1)
print(res1+20)
-
函式返回值的特點
- 一個函式中如果沒有寫return, 預設情況下,這個函式最後一句話會有一個return None
- return 和print的區別?
- return是呼叫完函式時,可以返回一個值給呼叫者
- print就直接輸出了,沒有返回值
- 一個函式中,如果遇到了return,那麼這個函式就結束了,函式中的後續程式碼不執行
def fun1(a, b): print("今天是星期二") c = a + b return c print("明天自習") # 不執行 res1 = fun1(10, 20) print(res1)
- 一個函式中只能有一個return
def fun1(a, b): print("今天是星期二") c = a + b return c print("明天自習") # 不執行 return 100 # 無效程式碼 res1 = fun1(10, 20) print(res1) def fun1(): for i in range(1,11): return i res1 = fun1() print(res1) # 1
- 函式返回值return後面,要返回的型別可以是任意的型別
-
函式引數和返回值的練習
- 定義 兩個函式,第一個函式使用者迴圈輸入要累加的數值,函式內部將使用者輸入的值封裝成一個列表返回;第二個函式將第一個函式返回的列表當作引數傳入,返回列表中所有資料之和的結果。
def get_list(): list1 = [] while True: n = int(input("請輸入一個數值:")) if n == -1: break list1.append(n) return list1 def sum_list(l2): n = 0 for i in l2: n += i return n l1 = get_list() print(l1) res2 = sum_list(l1) print(res2)
-
函式返回值的的一些進階用法
- 直接返回多個值,多個值之間使用英文逗號分隔,實際返回的內容是一個元組
def show1(): return 11, 22, 33 res1 = show1() print(res1, type(res1))
- 分別接收每個返回元素的值
def show1(): return 11, 22, 33 a1, a2, a3 = show1() print(f"a1:{a1}") print(f"a2:{a2}") print(f"a3:{a3}") def show1(): return 11, ['hello','world','python'], 33 a1, a2, a3 = show1() print(f"a1:{a1}") # a1:11 print(f"a2:{a2}") # a2:['hello', 'world', 'python'] print(f"a3:{a3}") # a3:33
函式的分類
- 無參無返回值
def login():
# 登入的操作邏輯
login()
- 無參有返回值
def get_number():
# 隨機生成一個數
return num
n = get_number()
print(n)
- 有參無返回值
def sum1(a,b):
print(a+b)
sum1(10,20)
- 有參有返回值
def fun1(s1):
return "shujia:" + s1
res1 = fun1('hello')
函式可以進行巢狀
- 巢狀呼叫
def fun1():
print("hello world 1")
def fun2():
return 100
def fun3(a1, b1):
fun1() # 呼叫fun1函式
res1 = fun2() # 呼叫fun2函式
return a1 + b1 + res1
res2 = fun3(11,22)
print(res2)
- 巢狀定義
def fun1():
a = 10
def fun2():
print("hello world")
print(a)
fun2() # 不呼叫 不執行
fun1()
fun2() # 報錯,呼叫不了函式內部定義的函式
- 函式練習2:定義一個函式,傳入一個文字路徑,和一個關鍵詞,將文字中包含關鍵詞的那一句話拿出來放在一個列表中返回該列表
import os
def get_word_list(file_os, word):
res_list = []
if os.path.exists(file_os):
f = open(file_os, 'r', encoding='UTF-8')
line_list = f.readlines()
for line in line_list:
if word in line:
res_list.append(line.strip())
else:
print("該路徑不存在!!")
return res_list
list1 = get_word_list('data/words.txt', 'shujia')
print(list1)
def get_word_list(file_os, word):
res_list = []
if not os.path.exists(file_os):
print("該路徑不存在!!")
return res_list
f = open(file_os, 'r', encoding='UTF-8')
line_list = f.readlines()
for line in line_list:
if word in line:
res_list.append(line.strip())
return res_list
list1 = get_word_list('data/words.txt', 'shujia')
print(list1)
函式的傳值問題
在python中,呼叫函式時,傳入的是物件的引用。
- 不可變物件【str, int, float, bool】
def fun1(x, y): print(f"x:{x}, y:{y}") # x:hello, y:world x = y x = x + x print(f"x:{x}, y:{y}") # x:worldworld, y:world s1 = "hello" s2 = "world" print(f"s1:{s1}, s2:{s2}") # s1:hello, s2:world fun1(s1,s2) print(f"s1:{s1}, s2:{s2}") # s1:hello, s2:world
不可變物件:指的是函式內的操作不會影響到函式外的變數值
- 可變物件【list, tuple, dict, set, 類】
def fun1(x, y):
print(f"x:{x}, y:{y}") # x:['hello'], y:['world']
x = y
x.append('java')
print(f"x:{x}, y:{y}") # x:['world','java'], y:['world','java']
s1 = ['hello']
s2 = ['world']
print(f"s1:{s1}, s2:{s2}") # s1:['hello'] , s2:['world']
fun1(s1,s2)
print(f"s1:{s1}, s2:{s2}") # s1:['hello'], s2:['world', 'java']
可變物件函式內部可以進行操作,改變可變物件中儲存的值,間接影響到函式外變數的引用
作用域和變數
-
在python中,作用域分為兩個區域
- 函式外是一個作用域
- 函式內部是一個作用域
-
全域性變數:將變數定義在函式外
-
區域性變數:將變數定義在函式內部
-
全域性變數和區域性變數使用特點
- 區域性作用域中可以使用到全域性變數【可以使用函式外部定義的變數】
- 函式與函式內部的作用域是相互獨立的,不能互相呼叫函式內部建立的區域性變數
if 1==1: a=10 print(f"a:{a}") for i in range(11): pass print(f"i:{i}")
-
python中提供了一個關鍵字 global, 在函式內部定義一個全域性變數,出了函式也是可以被訪問到的。
def fun1():
global a
a = 100
fun1()
print(a)
注意:如果函式內部有變數被global修飾,需要先呼叫該函式,讓記憶體中出現這個變數,後續才能去使用。
函式名也可以當作一個變數使用
- 用法場景1:
def fun1():
print("好好學習,天天向上!")
fun2 = fun1
fun2()
賦值的時候,注意有沒有小括號,方法名直接賦值,相當於給函式另起了一個名字;如果加了小括號,相當於呼叫函式的結果賦值。
- 用法場景2:變數可以儲存在容易中,比如列表
def fun1():
print("鵝鵝鵝")
def fun2():
print("曲項向天歌")
def fun3():
print("白毛浮綠水")
def fun4():
print("紅掌撥清波")
fun_list = [fun1, fun2, fun3, fun4]
flag = input("請輸入開始:")
for i in fun_list:
i()
- 用法場景:將函式作為返回值使用
def fun1():
print("鵝鵝鵝")
def fun2():
print("曲項向天歌")
def fun3():
print("白毛浮綠水")
def fun4():
print("紅掌撥清波")
fun_list = [fun1, fun2, fun3, fun4]
def show1():
for i in fun_list:
i()
def function1():
return show1
res1 = function1()
res1()
- 用法場景4:將函式作為引數傳遞
def fun1():
print("鵝鵝鵝")
def fun2():
print("曲項向天歌")
def fun3():
print("白毛浮綠水")
def fun4():
print("紅掌撥清波")
fun_list = [fun1, fun2, fun3, fun4]
def show1():
for i in fun_list:
i()
def function1(s):
s()
function1(show1)
作業:登入註冊的案例
- main函式要有,使用者自己選擇要做的功能,根據選擇呼叫不同的函式
- 使用者註冊的資訊需要使用一個檔案儲存,登入需要判斷使用者是否存在,密碼是否正確
- 註冊的時候,需要傳送郵件
函式和模組
在python開發中,我們需要利用python語言完成現實生活中的場景,python提供了許多內建的函式和模組給我們使用,完善我們的程式,比如time,random等等模組,如果我們還想要學習其他的模組,就需要使用第三方模組了。所以在python開發行業中,我們將python開發程式設計師稱之為調包俠。
內建函式
python的內建函式分為很多種
-
1.數學類函式
- abs() 求絕對值
n = -12 print(abs(n)) #12
- sum() 求和 (字串型別的元素不行)
list1 = [11,22,33,44,55] res1 = sum(list1) print(res1) #165
- divmod() 傳入兩個數值,前一個除以後一個,得到兩個值:一個商,一個是餘數
s, y = divmod(16, 5) print(s) #3 print(y) #1
- round() 四捨五入
n = 12.765 print(round(n)) # 13 print(round(n,2)) # 四捨五入並保留兩位小數(12.77)
- pow 求冪次方
print(pow(2,3)) #輸出2的三次冪
-
聚合類函式
- max() 求最大值
list1 = [123,53,225,1123,52,5,3,14] res1 = max(list1) print(res1) #1123
- min 求最小值
list1 = [123,53,225,1123,52,5,3,14] res1 = min(list1) print(res1) #3
- all 判斷一個列表中是否出現一個False
# 只要存在一個元素轉bool型別結果是False,all()的結果就是False list1 = [12,45,124,'','hello',12.34] print(all(list1)) #由於列表中有一個元素為空,輸出False
- any 判斷一個列表中是否出現一個True
# 只要存在一個元素轉bool型別結果是True,all()的結果就是True list1 = [12,45,124,'','hello',12.34] print(any(list1)) #True
-
和進位制相關的函式
- 二進位制
bin() 將十進位制的值轉二進位制
print(bin(136)) # 0b10001000
int() 將某一種進位制轉10進位制
print(int('0b10001000',2))
- 八進位制
oct() 將十進位制轉八進位制
print(oct(136)) # 0o210
- 十進位制
整數預設都是十進位制
- 十六進位制
hex() 將十進位制轉16進位制
print(hex(136)) # 0x88
-
字元類函式
- ord() 將一個字元轉成ASCII碼數值
- 0' - 48
- 'A' - 65
- 'a' - 97
print(ord('0')) #48 print(ord('A')) #65 print(ord('a')) #97
- chr() 將數值轉成對應的ASCII碼字元
print(chr(97))
- ord() 將一個字元轉成ASCII碼數值
-
型別轉換相關函式
- int()
- str()
- bool()
- list()
- dict()
- tuple()
- set()
- bytes()
s1 = '中國' b1 = s1.encode('UTF-8') print(b1, type(b1)) #\xe4\xb8\xad\xe5\x9b\xbd' <class 'bytes'> b2 = bytes('中國','UTF-8') print(b2) #\xe4\xb8\xad\xe5\x9b\xbd'
-
獲取輸出類函式
- input()
- print()
- len()
- open()
獲取列表的索引和元素
list1 = [1,2,3,4] for i,j in enumerate(list1): print(i,j)
- id() 獲取物件的地址值
- callable() 判斷一個變數是否是一個函式
list1 = [1,2,3,4] def fun1(): pass print(callable(fun1)) #變數是函式,因此結果為True
- sorted() 排序 #預設為從小到大排序
list1 = [34,12,5,12,344,53] print(f"list1:{list1}") list2 = sorted(list1) print(f"list1:{list2}") # list1:[5, 12, 12, 34, 53, 344]
自定義排序依據:
list1 = ['小虎:1007', '小黃:1009', '小查:1001', '小濤:1004', '小方:1002'] def fun1(e): return int(e.split(':')[1]) #以“:”為分隔符,並取“:”後的字元 list2 = sorted(list1, key=fun1) #調取fun1函式並根據“:“後的數字進行排序 print(f"list2:{list2}")#list1:['小查:1001', '小方:1002', '小濤:1004', '小虎:1007', '小黃:1009']
- zip() 將兩個序列中的元素一一對應
list1 = [1001, 1002, 1003, 1004, 1005] list2 = ['小虎', '黃滬生', '查鎔賢', '黃濤', '方直'] for i,j in zip(list1,list2): print(f"學號:{i}, 姓名:{j}") #學號: 1002, 姓名: 黃滬生 #學號: 1003, 姓名: 查鎔賢 #學號: 1004, 姓名: 黃濤 #學號: 1005, 姓名: 方直
函式生成式
python中提供了一個關鍵字可以讓我們在函式中使用 yield
輸入一次 print(res1.next()) 便會執行一次,否則將會暫停
def fun1():
yield 1
print("hello world")
yield 2
print("hello world")
yield 3
print("hello world")
yield 4
print("hello world")
yield 5
res1 = fun1()
print(res1.__next__())
print(res1.__next__())
print(res1.__next__())
print(res1.__next__())
有yield關鍵字的函式,結果是可以使用for迴圈的
def fun1():
print("hello 1")
yield 1
print("hello 2")
yield 2
print("hello 3")
yield 3
print("hello 4")
yield 4
print("hello 5")
yield 5
res1 = fun1()
for i in res1:
print(i)
print("-----------")
def fun1():
for i in range(1,11):
yield i
res1 = fun1()
for i2 in res1:
print(i2)
print("-----------")