1. 程式人生 > 實用技巧 >2020-8-15 Python語言基礎3:函式、面向物件、異常處理

2020-8-15 Python語言基礎3:函式、面向物件、異常處理

函式

函式的簡介


# - 函式也是一個物件
# - 物件是記憶體中專門用來儲存資料的一塊區域
# - 函式可以用來儲存一些可執行的程式碼,並且可以在需要時,對這些語句進行多次的呼叫
# - 建立函式:
# 
# def 函式名([形參1, 形參2, ...形參n]):
#     程式碼塊
# 
# 
# - 函式名必須要符號識別符號的規範
# (可以包含字母、數字、下劃線、但是不能以數字開頭)
# - 函式中儲存的程式碼不會立即執行,需要呼叫函式程式碼才會執行
# - 呼叫函式:
# 函式物件()
# - 定義函式一般都是要實現某種功能的  

# 比如有如下三行程式碼,這三行程式碼是一個完整的功能
# print('Hello')
# print('你好')
# print('再見')

# 定義一個函式
def fn() :
    print('這是我的第一個函式!')
    print('hello')
    print('今天天氣真不錯!')

# 列印fn
# print(fn) <function fn at 0x03D2B618>
# print(type(fn)) <class 'function'>

# fn是函式物件  fn()呼叫函式
# print是函式物件 print()呼叫函式
# fn()

# 定義一個函式,可以用來求任意兩個數的和
# def sum() :
#     a = 123
#     b = 456
#     print(a + b)

# sum()

# 定義函式時指定形參
def fn2(a , b) :
    # print('a =',a)
    # print('b =',b)
    print(a,"+",b,"=",a + b)

# 呼叫函式時,來傳遞實參
fn2(10,20)
fn2(123,456)

函式的引數


# - 在定義函式時,可以在函式名後的()
# 中定義數量不等的形參,
# 多個形參之間使用, 隔開
# - 形參(形式引數),定義形參就相當於在函式內部聲明瞭變數,但是並不賦值
# - 實參(實際引數)
# - 如果函式定義時,指定了形參,那麼在呼叫函式時也必須傳遞實參,
# 實參將會賦值給對應的形參,簡單來說,有幾個形參就得傳幾個實參
#
# - 練習1:
# 定義一個函式,可以用來求任意三個數的乘積
# 求任意三個數的乘積
def mul(a,b,c):
    print(a*b*c)

# - 練習2:
# 定義一個函式,可以根據不同的使用者名稱顯示不同的歡迎資訊
# 根據不同的使用者名稱顯示不同的歡迎資訊   
def welcome(username):
    print('歡迎',username,'光臨')

# mul(1,2,3)   
# welcome('孫悟空') 

# 定義一個函式
# 定義形參時,可以為形參指定預設值
# 指定了預設值以後,如果使用者傳遞了引數則預設值沒有任何作用
#   如果使用者沒有傳遞,則預設值就會生效
def fn(a = 5 , b = 10 , c = 20):
    print('a =',a)
    print('b =',b)
    print('c =',c)

# fn(1 , 2 , 3)
# fn(1 , 2)
# fn()

# 實參的傳遞方式
# 位置引數
# 位置引數就是將對應位置的實參複製給對應位置的形參
# 第一個實參賦值給第一個形參,第二個實參賦值給第二個形參 。。。
# fn(1 , 2 , 3)

# 關鍵字引數
# 關鍵字引數,可以不按照形參定義的順序去傳遞,而直接根據引數名去傳遞引數
# fn(b=1 , c=2 , a=3)
# print('hello' , end='')
# 位置引數和關鍵字引數可以混合使用
# 混合使用關鍵字和位置引數時,必須將位置引數寫到前面
# fn(1,c=30)

def fn2(a):
    print('a =',a)

# 函式在呼叫時,解析器不會檢查實參的型別
# 實參可以傳遞任意型別的物件
b = 123
b = True
b = 'hello'
b = None
b = [1,2,3]

# fn2(b)    
fn2(fn)

def fn3(a , b):
    print(a+b)

# fn3(123,"456")

def fn4(a):
    # 在函式中對形參進行重新賦值,不會影響其他的變數
    # a = 20
    # a是一個列表,嘗試修改列表中的元素
    # 如果形參執行的是一個物件,當我們通過形參去修改物件時
    #   會影響到所有指向該物件的變數
    a[0] = 30
    print('a =',a,id(a))

c = 10   
c = [1,2,3] 

# fn4(c)
# fn4(c.copy())
# fn4(c[:])

# print('c =',c,id(c))

不定長引數


# 不定長的引數
# 定義一個函式,可以求任意個數字的和
def sum(*nums):
    # 定義一個變數,來儲存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums :
        result += n
    print(result)


# sum(123,456,789,10,20,30,40)

# 在定義函式時,可以在形參前邊加上一個*,這樣這個形參將會獲取到所有的實參
# 它將會將所有的實參儲存到一個元組中
# a,b,*c = (1,2,3,4,5,6)

# *a會接受所有的位置實參,並且會將這些實參統一儲存到一個元組中(裝包)
def fn(*a):
    print("a =",a,type(a))

# fn(1,2,3,4,5)
# 帶星號的形參只能有一個
# 帶星號的引數,可以和其他引數配合使用
# 第一個引數給a,第二個引數給b,剩下的都儲存到c的元組中
# def fn2(a,b,*c):
#     print('a =',a)
#     print('b =',b)
#     print('c =',c)

# 可變引數不是必須寫在最後,但是注意,帶*的引數後的所有引數,必須以關鍵字引數的形式傳遞
# 第一個引數給a,剩下的位置引數給b的元組,c必須使用關鍵字引數
# def fn2(a,*b,c):
#     print('a =',a)
#     print('b =',b)
#     print('c =',c)

# 所有的位置引數都給a,b和c必須使用關鍵字引數
# def fn2(*a,b,c):
#     print('a =',a)
#     print('b =',b)
#     print('c =',c)

# 如果在形參的開頭直接寫一個*,則要求我們的所有的引數必須以關鍵字引數的形式傳遞
def fn2(*,a,b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)
# fn2(a=3,b=4,c=5)

# *形參只能接收位置引數,而不能接收關鍵字引數
# def fn3(*a) :
#     print('a =',a)

# **形參可以接收其他的關鍵字引數,它會將這些引數統一儲存到一個字典中
#   字典的key就是引數的名字,字典的value就是引數的值
# **形參只能有一個,並且必須寫在所有引數的最後
def fn3(b,c,**a) :
    print('a =',a,type(a))
    print('b =',b)
    print('c =',c)

# fn3(b=1,d=2,c=3,e=10,f=20)

引數解包


# 引數的解包(拆包)
def fn4(a,b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)

# 建立一個元組
t = (10,20,30)

# 傳遞實參時,也可以在序列型別的引數前新增星號,這樣他會自動將序列中的元素依次作為引數傳遞
# 這裡要求序列中元素的個數必須和形參的個數的一致
# fn4(*t)    

# 建立一個字典
d = {'a':100,'b':200,'c':300}
# 通過 **來對一個字典進行解包操作
fn4(**d)

返回值


# 返回值,返回值就是函式執行以後返回的結果
# 可以通過 return 來指定函式的返回值
# 可以之間使用函式的返回值,也可以通過一個變數來接收函式的返回值

def sum(*nums):
    # 定義一個變數,來儲存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums :
        result += n
    print(result)

# sum(123,456,789)
 

# return 後邊跟什麼值,函式就會返回什麼值
# return 後邊可以跟任意的物件,返回值甚至可以是一個函式
def fn():
    # return 'Hello'
    # return [1,2,3]
    # return {'k':'v'}
    def fn2() :
        print('hello')

    return fn2 # 返回值也可以是一個函式

r = fn() # 這個函式的執行結果就是它的返回值
# r()
# print(fn())
# print(r)

# 如果僅僅寫一個return 或者 不寫return,則相當於return None 
def fn2() :
    a = 10
    return 

# 在函式中,return後的程式碼都不會執行,return 一旦執行函式自動結束
def fn3():
    print('hello')
    return
    print('abc')

# r = fn3()
# print(r)

def fn4() :
    for i in range(5):
        if i == 3 :
            # break 用來退出當前迴圈
            # continue 用來跳過當次迴圈
            return # return 用來結束函式
        print(i)
    print('迴圈執行完畢!')

# fn4()

def sum(*nums):
    # 定義一個變數,來儲存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums :
        result += n
    return result

r = sum(123,456,789)

# print(r + 778)

def fn5():
    return 10

# fn5 和 fn5()的區別
print(fn5) # fn5是函式物件,列印fn5實際是在列印函式物件 <function fn5 at 0x05771BB8>
print(fn5()) # fn5()是在呼叫函式,列印fn5()實際上是在列印fn5()函式的返回值 10

文件字串


# help()是Python中的內建函式
# 通過help()函式可以查詢python中的函式的用法
# 語法:help(函式物件)
# help(print) # 獲取print()函式的使用說明

# 文件字串(doc str)
# 在定義函式時,可以在函式內部編寫文件字串,文件字串就是函式的說明
#   當我們編寫了文件字串時,就可以通過help()函式來檢視函式的說明
#   文件字串非常簡單,其實直接在函式的第一行寫一個字串就是文件字串
def fn(a:int,b:bool,c:str='hello') -> int:
    '''
    這是一個文件字串的示例

    函式的作用:。。。。。
    函式的引數:
        a,作用,型別,預設值。。。。
        b,作用,型別,預設值。。。。
        c,作用,型別,預設值。。。。
    '''
    return 10

help(fn)

作用域與名稱空間


# 作用域(scope)
# 作用域指的是變數生效的區域
b = 20 # 全域性變數

def fn():
    a = 10 # a定義在了函式內部,所以他的作用域就是函式內部,函式外部無法訪問
    print('函式內部:','a =',a)
    print('函式內部:','b =',b)

# fn()    
  

# print('函式外部:','a =',a)
# print('函式外部:','b =',b)

# 在Python中一共有兩種作用域
#  全域性作用域
#   - 全域性作用域在程式執行時建立,在程式執行結束時銷燬
#   - 所有函式以外的區域都是全域性作用域
#   - 在全域性作用域中定義的變數,都屬於全域性變數,全域性變數可以在程式的任意位置被訪問
#   
#  函式作用域
#   - 函式作用域在函式呼叫時建立,在呼叫結束時銷燬
#   - 函式每呼叫一次就會產生一個新的函式作用域
#   - 在函式作用域中定義的變數,都是區域性變數,它只能在函式內部被訪問
#   
#  變數的查詢
#   - 當我們使用變數時,會優先在當前作用域中尋找該變數,如果有則使用,
#       如果沒有則繼續去上一級作用域中尋找,如果有則使用,
#       如果依然沒有則繼續去上一級作用域中尋找,以此類推
#       直到找到全域性作用域,依然沒有找到,則會丟擲異常
#           NameError: name 'a' is not defined

def fn2():
    def fn3():
        print('fn3中:','a =',a)
    fn3()

# fn2()    

a = 20

def fn3():
    # a = 10 # 在函式中為變數賦值時,預設都是為區域性變數賦值
    # 如果希望在函式內部修改全域性變數,則需要使用global關鍵字,來宣告變數
    global a # 宣告在函式內部的使用a是全域性變數,此時再去修改a時,就是在修改全域性的a
    a = 10 # 修改全域性變數
    print('函式內部:','a =',a)

# fn3()
# print('函式外部:','a =',a)


# 名稱空間(namespace)
# 名稱空間指的是變數儲存的位置,每一個變數都需要儲存到指定的名稱空間當中
# 每一個作用域都會有一個它對應的名稱空間
# 全域性名稱空間,用來儲存全域性變數。函式名稱空間用來儲存函式中的變數
# 名稱空間實際上就是一個字典,是一個專門用來儲存變數的字典

# locals()用來獲取當前作用域的名稱空間
# 如果在全域性作用域中呼叫locals()則獲取全域性名稱空間,如果在函式作用域中呼叫locals()則獲取函式名稱空間
# 返回的是一個字典
scope = locals() # 當前名稱空間
print(type(scope))
# print(a)
# print(scope['a'])
# 向scope中新增一個key-value
scope['c'] = 1000 # 向字典中新增key-value就相當於在全域性中建立了一個變數(一般不建議這麼做)
# print(c)

def fn4():
    a = 10
    # scope = locals() # 在函式內部呼叫locals()會獲取到函式的名稱空間
    # scope['b'] = 20 # 可以通過scope來操作函式的名稱空間,但是也是不建議這麼做

    # globals() 函式可以用來在任意位置獲取全域性名稱空間
    global_scope = globals()
    # print(global_scope['a'])
    global_scope['a'] = 30
    # print(scope)

fn4()