函數對象,命名空間,作用域


[TOC]

函數對象

一、函數名是可以被引用的

秉承着一切皆對象的理念,我們再次回頭來看函數(function)。

函數也是一個對象,具有屬性(可以使用dir()查詢)。作為對象,它還可以賦值給其它對象名,或者作為參數傳遞。

# 變量可以被傳遞
name = 'kai'
x = name
print(x)
# kai
print(id(name), id(x))
#2385705498608 2385705498608
# 嘗試函數像變量一樣被傳遞
def func():
    print('from func')
print(func)
# <function func at 0x0000016E5E062E18>

f = func  # 其實指向的也是函數func 指向的函數體代碼的內存地址
print(f)
# <function func at 0x000001B4D0D92E18>
f()
# from func
print(id(func), id(f))
# 3085463137816 3085463137816
def func():
    print("from func")

def index(args):
    print(args)
    args()  # 函數主要一定義(先定義)就可以在任意地方調用
    print("from index")

index(func)
# <function func at 0x000001B7429A2E18>
# from func
# from index

二、函數名可以被當做函數的返回值

def index():
    print('index')

def func():
    print('func')
    return index

res = func()  # 將返回的函數名index 賦值給變量res
# func
print(res)
# <function index at 0x000001EF64362E18>
res()
# index

三、函數名可以被當做作容器類型的參數

def func():
    print('func')

l = [1, 2, func, func()]  # 定義的時候默認執行了func(),所以下面先打印了 func
# func  # func函數沒有返回值,所以默認是None
print(l)
# [1, 2, <function func at 0x0000013931C92E18>, None]

函數對象小練習

def register():
    print("注冊了")
    pass


def login():
    print("登錄了")
    pass


def shopping():
    print("購物了")
    pass


def output_func_list():
    print("----- 請選擇功能!")
    for key in func_list:
        print(f"----  {key}.{func_list[key][1]}")


func_list = {
    0: [register, '注冊'],
    1: [login, '登錄'],
    2: [shopping, '購物'],
}


while True:
    output_func_list()
    chose_func = input("請輸入功能編號(q 退出系統):").strip()
    if chose_func.isdigit():
        # 執行相應功能
        chose_func = int(chose_func)
        # 判斷輸入的編號在不在功能列表里
        if chose_func in func_list:
            func_list[chose_func][0]()  # 取到功能函數名,加括號調用
        else:
            print("您輸入的功能編號不存在,請重新輸入!")
    elif chose_func.lower() in ['q', 'quit']:
        print("感謝您的使用,祝您生活愉快~")
        break
    else:
        print("請正確輸入數字!")


函數名可以作為容器對象的元素值、函數名(即函數內存地址)可以加括號直接調用


四、函數的嵌套

函數的嵌套調用: # 在函數內部調用其他函數

def index():
    print('index')

def func():
    index()  # 在定義 func 函數的時候不會直接調用 index 的方法 --> 函數定義的時候不執行代碼
    print('func')

func()
# index  # 通過 func()函數內部調用了index() 函數,打印出了 index
# func

函數的嵌套調用可以將復雜的邏輯簡單化

小練習:寫一個可以求四個數中的最大值

def my_max(x, y):
    if x > y:
        return x
    return y

def my_max4(a, b, c, d):
    res = my_max(a, b)
    res = my_max(res, c)
    res = my_max(res, d)
    return res

print(my_max4(1, 5, 7, 1))
# 7


嵌套定義
def outer():
    x = 1
    print("outer")
    def inner():
        print("inner")
    inner()

# inner()  # 會報錯,在外部無法訪問內部內容
outer()
# outer
# inner

實現在外部調用 outer函數的內部函數 inner

# 想在外部調用inner 可通過把內部的函數名當做外部函數的返回值來返回給外部
def outer():
    x = 1
    print("outer")
    def inner():
        print("inner")
    return inner  # 把 inner 函數當做函數的返回值返回給 outer函數的調用者

res = outer()
# outer
res()  # 變相調用inner
# inner

實現代碼

五、名稱空間

名稱空間: 存放的是變量名 與 變量值 的內存地址 綁定關系的地方 ,后文可能稱之為命名空間

訪問變量的值: 要想訪問一個變量的值,必須先去名稱空間拿到對應的名字,才能訪問變量的值

命名空間的分類:

命名空間分為:內置名稱空間,全局名稱空間,局部名稱空間 三大類

內置名稱空間: python 解釋器提前已經定義好了的名字(已經存放到了內置名稱空間中了)

print("hello world")
max(1, 44, 62, 15)
len('26515f1asfafqw')
sum([1, 2, 3, 4, 5])
# 像上面的print max len sum 並沒有定義就可以值使用,它們就是python解釋器提前定義好了的函數,屬於內置命名空間的

全局命名空間:文件級別的代碼

x = 1
if x == 1:
    y = 2
print(y)
# 2

for i in [1, 2]:
    print(i)
print(i)
# 1
# 2
# 2

# 上面的 x y z 都在全局名稱空間,不要以為縮進的就是局部的(if、 for、 while 無論嵌套,多少層,他們內部所創建的名字都是全局名稱空間的)

局部命名空間: (目前所學)函數體內創建的名字都屬於局部名稱空間(最外層的函數名是屬於全局名稱空間的)

def func():
    username = 'jason'
# print(username)  # 會報錯 NameError: name 'username' is not defined
func()

至於為什么上面的 print(username) 為什么會報錯,學完下面的知識你就知道啦。


六、命名空間的生命周期

名稱空間的生命周期 內置名稱空間:(最長)只要 python解釋器啟動,立馬創建 關閉 python解釋器時自動銷毀 全局名稱空間: 只要右鍵運行 py文件就會自動創建 py文件程序運行結束自動銷毀 局部名稱空間:(動態創建動態銷毀)函數被調用的時候自動創建 函數執行結束后立即銷毀

名稱空間生命周期結束 -- > 里面存的變量與指向值的內存地址解綁,內存中的值等待垃圾回收機制回收

def 刪除變量 -- > 里面存的變量與指向值的內存地址解綁,內存中的值等待垃圾回收機制回收 ---> 等同於名稱空間里刪除了一個變量(綁定關系)

垃圾回收機制:垃圾回收機制隔一段時間就會檢查一次,內存中的值如果沒有變量指向它(引用),那垃圾回收機制就會把它清除掉(釋放內存)

如果多次檢查都有變量等指向它,那就會把它等級提升,檢查頻率就會變低

命名空間的查找順序

驗證思路: # 找一個三個地方都有的東西來驗證(比如 len、max等,暫時忽略命名規范不能與關鍵字重復) , # 分別注釋來測試其查找順序(全局、局部 )

len = '我是全局名稱空間的len'
def func():
    len = '我是局部名稱空間的len'
    print(len)
print(len)  # 這里是全局的位置
# 我是全局名稱空間的len
'''
# 把全局的len 注釋掉,就去找了內置的len
print(len)  # 是全局的位置
# <built-in function len>
'''
func()
# 我是局部名稱空間的len

(******)名稱空間的查找順序 1.需要先確定當前的在哪(全局、局部),大前提 1.1 站在全局:全局 >>> 內置 1.2 站在局部:局部 >>> 全局 >>> 內置 1.2.2 站在局部的內部(多個局部嵌套):局部 >>> 上一級局部 >>> 上一級局部 >>> .... >>> 全局 >>> 內置 會在作用域同級的前后(這句代碼前后的同級語句)去找,然后再上一級

2.函數在定義階段查找名字的順序(范圍)就已經固定了, 不會因為函數的調用位置變化而變化(*******) 可以在函數定義的時候寫個注釋,指出他查找的位置,防止邏輯復雜了搞不清楚


七、作用域

python中的作用域有 全局作用域 與 局部作用域 , 全局作用域: # 全局有效: 內置名稱空間、全局名稱空間 都屬於全局作用域 , 局部作用域: # 局部有效:局部名稱空間

# 嘗試修改不可變類型的全局變量
x = 1
def func():
    x = 2  # 實質是又創建了一個局部變量 x
func()
print(x)  # 局部無法修改不可變類型的全局變量
# 1

# 嘗試修改可變類型的局部變量
x = []
def func():
    x.append('嘿嘿嘿')
func()
print(x)  # 修改成功,局部可以修改可變類型的全局變量
# ['嘿嘿嘿']

# 全局訪問不了局部的變量,所以不展開研究

小結論: # 局部無法修改不可變類型的全局變量 , # 局部可以修改可變類型的全局變量 (前提:在不使用 global 和 nonlocal 關鍵字的情況下)

通過 global 關鍵字在局部修改全局,修改多個用 , 隔開

x = 1  # 不可變類型
username = 'jason'
def func():
    global x,username
    x = 999  # 修改全局變量,而不是創建局部變量
    username = 'egon'
func()
print(x, username)
# 999 egon

通過 nonlocal 關鍵字在局部修改局部,修改多個用 , 隔開

def func():
    x = 1
    def index():
        x = 2
    index()
    print(x)
func()
# 1

# 想就在 index 里把 x 改了
def func():
    x = 1
    def index():
        nonlocal x
        x = 2
    index()
    print(x)
func()
# 2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM