文章目錄
- 函式
- 函式的定義
- 函式的引數
- 用模塊管理函式
- 高階函式
- Lambda函式
- 遞回呼叫
- 例題
- 例題1:漢諾塔問題
- 例題2:爬樓梯
- 例題3:冒泡排序
- 例題4:撰寫實作查找元素的函式
- 例題5:判斷快樂數
- 面向物件編程
- 面向物件編程的程序
- 定義類
- 創建物件
- 給物件發訊息
- 面向物件的支柱
- 靜態方法和類方法
- 繼承和多型
- 魔術方法
- 類和類之間的關系
函式
函式的定義
函式就是對實作某一特定功能的代碼的封裝,
在Python中可以使用def關鍵字來定義函式,函式的命名規則跟變數的命名規則是一致的,
# 定義函式:def是定義函式的關鍵字、fac是函式名,num是引數(自變數)
def fac(num):
"""求階乘"""
result = 1
for n in range(1, num + 1):
result *= n
# 回傳num的階乘(因變數)
return result
函式的引數
- 位置引數:傳入引數的時候對號入座即可;也可以通過
引數名=引數值的方式傳入函式所需的引數,因為指定了引數名,傳入引數的順序可以進行調整, - 命名關鍵字引數:在函式的引數串列中,寫在
*之后的引數,傳參時,只能以引數名=引數值的方式傳參, - 關鍵字引數:Python中可以通過
*kwargs運算式語法傳入0個或任意多個引數名=引數值形式的引數,關鍵字引數會將傳入的帶引數名的引陣列裝成一個字典,引數名就是字典中鍵值對的鍵,而引數值就是字典中鍵值對的值, - 可變引數:可以實作向一個函式中傳0個或任意多個引數,Python中可以通過
*args運算式語法來支持可變引數,(不能接收帶引數名的引數)
注意:不帶引數名的引數(位置引數)必須出現在帶引數名的引數(關鍵字引數)之前
def add(*args, **kwargs):
"""加法"""
total = 0
for arg in args:
if type(arg) in (int, float):
total += arg
for value in kwargs.values():
if type(value) in (int, float):
total += value
return total
# 傳入0個引數
print(add())
# 只傳入可變引數
print(add(1, 2, 3))
# 只傳入關鍵字引數
print(add(a=1, b=2, c=3))
# 傳入位置引數和關鍵字引數,關鍵字引數在*args的‘*’之后,因此也是命名關鍵字引數
print(add(1, 2, c=3, d=4))
用模塊管理函式
Python中每個檔案就代表了一個模塊(module),在不同模塊可以有同名的函式,在使用函式時,通過import關鍵字匯入指定的模塊,再使用完全限定名的呼叫方式就可以區分是哪個模塊中的函式,
高階函式
函式的引數和回傳值可以是任意型別的物件,這就意味著函式本身也可以作為函式的引數或回傳值,這就是所謂的高階函式,
# 中間兩個引數是命名關鍵字引數,必須帶上引數名
def calculate(*args, init_value, op, **kwargs):
"""可以做任意二元運算的函式
:param init_value:初始值
:param op: 一個實作二元運算(+、*)的函式
:param args: 位置引數
:param kwargs: 關鍵字引數
:return: 運算結果
"""
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
def add(a, b):
return a + b
def mul(a, b):
return a * b
print(calculate(1, 2, 30, init_value=0, op=add))
print(calculate(1, 2, 3, init_value=0, op=mul))
print(calculate(15, 20, init_value=100, op=add, c=5))
Lambda函式
在使用高階函式的時候,如果作為引數或者回傳值的函式本身非常簡單,一行代碼就能夠完成,那么我們可以使用Lambda函式來表示,如上面的add函式和mul函式
# 中間兩個引數是命名關鍵字引數,必須帶上引數名
def calculate(*args, init_value, op, **kwargs):
"""可以做任意二元運算的函式
:param init_value:初始值
:param op: 一個實作二元運算(+、-、*、/)的函式
:param args: 位置引數
:param kwargs: 關鍵字引數
:return: 運算結果
"""
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
# 呼叫calculate函式,通過lambda函式給op引數賦值
print(calculate(1, 2, 30, init_value=0, op=lambda x, y: x + y, a=5))
print(calculate(1, 2, 3, init_value=1, op=lambda x, y: x * y))
print(calculate(15, 20, init_value=100, op=lambda x, y: x - y))
遞回呼叫
Python中允許函式嵌套定義,也允許函式之間相互呼叫,而且一個函式還可以直接或間接的呼叫自身,
遞回函式的要點:
1.遞回公式:(第n次與第n-1次的關系)
2.收斂條件(什么時候停止遞回呼叫)
例題
例題1:漢諾塔問題
a柱上有n個盤子,要把所有盤子搬到b柱上,借用c柱幫助搬運,大盤不能壓在小盤上面,
def move(n, a, c, b):
if n == 1:
print(f'{a}->{b}')
else:
move(n - 1, a, b, c)
move(1, a, c, b)
move(n - 1, c, a, b)
if __name__ == '__main__':
n = int(input('請輸入盤子數:'))
move(n, 'a', 'c', 'b')
例題2:爬樓梯
有一個小孩爬樓梯,一次可以爬1個臺階、2個臺階或3個臺階,問爬完10個臺階,有多少種走法?
def step(n):
if n == 1:
return 1
elif n == 2:
return 2
elif n == 3:
return 4
else:
return step(n - 1) + step(n - 2) + step(n - 3)
if __name__ == '__main__':
print(step(10))
例題3:冒泡排序
def bubble_sort(nums, ascending=True, gt=lambda x, y: x > y):
"""冒泡排序
:param nums: 待排序的串列
:param ascending: 是否使用升序
:param gt: 比較兩個元素大小的函式
:return: 排序后的串列
"""
sort_nums = nums[:]
for i in range(1, len(sort_nums)):
swapped = False
for j in range(0, len(sort_nums) - i):
if gt(sort_nums[j], sort_nums[j + 1]):
sort_nums[j + 1], sort_nums[j] = sort_nums[j], sort_nums[j + 1]
swapped = True
if not swapped:
break
if not ascending:
return sort_nums[::-1]
return sort_nums
nums = [15, 20, 10, 15, 12, 30, 65]
# 使用默認的gt函式
print(bubble_sort(nums, False))
print(nums)
words = ['apple', 'watermelon', 'hello', 'zoo', 'internationalization']
# 修改gt函式根據字串長度排序
print(bubble_sort(words, gt=lambda x, y: len(x) > len(y), ascending=False))
例題4:撰寫實作查找元素的函式
若串列元素無序,使用順序查找;若串列元素有序,使用二分查找(折半查找),
def seq_search(items, key):
for index, i in enumerate(items):
if i == key:
return index
return -1
def bin_search(items, key, cmp=lambda x, y: x - y):
"""折半查找(漸進時間復雜度:(log2 n))
:param cmp: 比較兩個元素的大小
:param items: 一個有序串列
:param key: 查找的元素
:return: 找到了回傳元素下標,沒找到回傳-1
"""
high = len(items) - 1
low = 0
while low <= high:
mid = (high + low) // 2
if cmp(items[mid], key) > 0:
high = mid - 1
elif cmp(items[mid], key) < 0:
low = mid + 1
else:
return mid
return -1
if __name__ == '__main__':
nums1 = [78, 58, 93, 42, 45, 66, 12, 15]
nums2 = [1, 15, 25, 40, 80, 92, 98, 99]
print(seq_search(nums1, 58))
print(seq_search(nums1, 40))
print(bin_search(nums2, 99))
print(bin_search(nums2, 85))
例題5:判斷快樂數
對于一個正整數,每一次將該數替換為它每個位置上的數字的平方和,然后重復這個程序,如果數字變成了1,這個數就是快樂數,如果無限回圈始終變不到1,這個數就不是快樂數,
def is_happy_num(num):
temp = []
while num not in temp:
temp.append(num)
total = 0
while num != 0:
total += (num % 10) ** 2
num //= 10
if total == 1:
return True
num = total
return False
for i in range(100, 1001):
if is_happy_num(i):
print(i, end=' ')
面向物件編程
面向物件編程:把一組資料和處理資料的方法組成物件,把行為相同的物件歸納為類,通過封裝隱藏物件的內部細節,通過繼承實作類的特化與泛化,通過多型實作基于物件型別的動態分派,
面向物件編程的程序
-
定義類
—資料抽象:找到物件相關的靜態特征(屬性)—>找名詞
—行為抽象:找到和物件相關的動態特征(方法)—>找動詞
-
創建物件
-
給物件發訊息
定義類
在Python中,可以使用class關鍵字加上類名來定義類,通過縮進我們可以確定類的代碼塊,就如同定義函式那樣,
class Student:
def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age
def study(self, course_name):
print(f'{self.name}正在學習{course_name}.')
def play(self, game_name):
print(f'{self.name}正在玩{game_name}游戲.')
創建物件
stu1 = Student('小青', 20)
stu2 = Student('小白', 22)
給物件發訊息
# 通過“類.方法”呼叫方法,第一個引數是接收訊息的物件,第二個引數是學習的課程名稱
Student.study(stu1, 'Python程式設計')
# 通過“物件.方法”呼叫方法,點前面的物件就是接收訊息的物件,只需要傳入第二個引數
stu1.study('Python程式設計')
stu2.play('王者榮耀')
一般使用“物件.方法”呼叫方法
面向物件的支柱
面向物件編程的四大支柱:
- 抽象:提取共性(定義類就是一個抽象程序,需要做資料抽象和行為抽象),
- 封裝:把資料和操作資料的函式從邏輯上組成一個整體(物件),隱藏實作細節,暴露簡單的呼叫介面,
- 繼承:擴展已有的類創建新類,實作對已有類的代碼復用,
- 多型:給不同的物件發出同樣的訊息,不同的物件執行了不同的行為,
靜態方法和類方法
使用staticmethod裝飾器宣告某個方法是某個類的靜態方法,如果要宣告類方法,可以使用classmethod裝飾器,可以直接使用類名.方法名的方式來呼叫靜態方法和類方法,二者的區別在于,類方法的第一個引數是類物件本身,而靜態方法則沒有這個引數,簡單的總結一下,物件方法、類方法、靜態方法都可以通過類名.方法名的方式來呼叫,區別在于方法的第一個引數到底是普通物件還是類物件,還是沒有接受訊息的物件,
繼承和多型
繼承的語法是在定義類的時候,在類名后的圓括號中指定當前類的父類,在子類的初始化方法中,我們可以通過super().__init__()來呼叫父類初始化方法,super函式是Python內置函式中專門為獲取當前物件的父類物件而設計的,子類除了可以通過繼承得到父類提供的屬性和方法外,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力,
子類繼承父類的方法后,還可以對方法進行重寫(重新實作該方法),
多型就是呼叫相同的方法,不同的子類物件做不同的事情,
魔術方法
魔術方法(魔法方法):有特殊用途和意義的方法,
Python中有很多魔法方法,下面是幾個常用的:
__init__ —> 初始化方法,在呼叫構造器語法創建物件的時候會被自動呼叫
__str__ —> 獲得物件的字串表示,在呼叫print函式輸出物件時會被自動呼叫
__repr__ —> 獲得物件的字串表示,把物件放到容器中呼叫print輸出時會自動呼叫
__lt__ —> 在使用 < 運算子比較兩個物件大小時會自動呼叫
類和類之間的關系
類和類之間的關系可以粗略的分為is-a關系(繼承)、has-a關系(關聯)和use-a關系(依賴),
- is-a關系:繼承
- has-a關系:關聯 —>把一個類的物件作為另一個類的物件的屬性
—普通關聯
—強關聯:整體和部分的關聯,聚合和合成 - use-a關系:依賴 —> 一個類的物件作為另一個類的方法的引數或回傳值
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292502.html
標籤:python
