最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

系統(tǒng)講解Python函數(shù)中global與nonlocal關鍵字的使用

 更新時間:2026年07月22日 09:04:14   作者:星河耀銀海  
Python變量作用域總是搞混,這篇文章就用最通俗的語言為你拆解LEGB規(guī)則,從Local到Built-in逐層查找,還會詳細講解global和nonlocal關鍵字的用法、閉包中的變量綁定,以及實戰(zhàn)案例,讀完你就能徹底掌握Python的變量世界,寫出更安全的代碼

一、開篇:變量的"活動范圍"

想象每個變量都有一張"通行證",規(guī)定了它能在哪些代碼區(qū)域被訪問。有些變量是整個程序通用的(全局變量),有些只在某個函數(shù)內部有效(局部變量)。這種"通行范圍",就是作用域(Scope)。

先看一個經典的困惑:

# 這段代碼會輸出什么?
x = 10  # 全局變量

def my_function():
    print(x)   # 訪問全局變量x
    x = 20     # 嗯?這行在print后面,會影響前面的print嗎?
    print(x)

# my_function()  # 如果運行這行,會報錯還是正常輸出?
# 答案是:UnboundLocalError!
# 因為x=20讓Python認為x是局部變量,但print(x)時x還沒被賦值

這段代碼揭示了一個關鍵事實:Python在編譯函數(shù)時,會根據賦值語句來決定變量的作用域。如果函數(shù)內部有對某個變量的賦值,Python就認為它是局部變量——即使賦值語句在使用語句的后面。

這篇文章,我們就來徹底搞懂Python的變量作用域——LEGB規(guī)則、global關鍵字、nonlocal關鍵字,以及它們在實際開發(fā)中的應用。

二、LEGB規(guī)則:Python的變量查找法則

2.1 四層作用域

# Python變量查找遵循LEGB規(guī)則(由內向外):
# L - Local(局部作用域):函數(shù)內部
# E - Enclosing(嵌套作用域):外層函數(shù)的作用域
# G - Global(全局作用域):模塊級別
# B - Built-in(內置作用域):Python內置的名字

# ========== 內置作用域 ==========
# Python啟動時就存在,如print、len、range等

# ========== 全局作用域 ==========
# 模塊級別的變量和函數(shù)
global_var = "我是全局變量"

def outer_function():
    # ========== 嵌套作用域 ==========
    enclosing_var = "我是嵌套變量(屬于outer_function)"

    def inner_function():
        # ========== 局部作用域 ==========
        local_var = "我是局部變量(屬于inner_function)"

        print(f"局部: {local_var}")
        print(f"嵌套: {enclosing_var}")
        print(f"全局: {global_var}")
        print(f"內置: {len}")  # Python內置函數(shù)

    inner_function()

outer_function()
# 局部: 我是局部變量(屬于inner_function)
# 嵌套: 我是嵌套變量(屬于outer_function)
# 全局: 我是全局變量
# 內置: <built-in function len>

2.2 LEGB查找過程的直觀演示

# ?? 詳細演示:Python按順序查找變量
# 找到就停,不到最后一層不報錯

name = "Global: 張三"  # G層

def outer():
    name = "Enclosing: 李四"  # E層

    def inner():
        name = "Local: 王五"  # L層
        print(f"inner中的name: {name}")
        # 輸出 → Local: 王五 (找到了,停止)

    def inner_no_local():
        # 這層沒有name
        print(f"inner_no_local中的name: {name}")
        # 輸出 → Enclosing: 李四 (L層沒有,找到了E層)

    def inner_use_global():
        # L層和E層都沒有
        print(f"inner_use_global中的name: {name}")
        # 等等——E層有name="李四",所以輸出→Enclosing: 李四

    inner()
    inner_no_local()
    inner_use_global()

outer()

# 在全局作用域中訪問
print(f"全局中的name: {name}")
# 輸出 → Global: 張三

2.3 賦值修改了作用域

# ?? 核心規(guī)則:在函數(shù)內部,如果對變量進行了賦值
# Python就認為這個變量是該函數(shù)的局部變量
# 即使外面有同名的全局變量!

x = 100  # 全局變量

def read_only():
    """只讀取,不賦值——可以訪問全局變量"""
    print(f"read_only: x = {x}")  # 正常,訪問全局x

def has_assignment():
    """有賦值語句——x被視為局部變量"""
    # print(f"賦值前: x = {x}")  # UnboundLocalError!
    x = 200  # 這個賦值讓Python將x當作局部變量
    print(f"賦值后: x = {x}")

read_only()   # read_only: x = 100
has_assignment()
# 如果取消注釋print(f"賦值前: x = {x}"),會報錯:
# UnboundLocalError: local variable 'x' referenced before assignment

# 為什么?Python在編譯函數(shù)時掃描了整個函數(shù)體
# 發(fā)現(xiàn)了 x = 200 這行賦值語句
# 于是決定:x是這個函數(shù)的局部變量
# 所以,賦值之前的print(x)也在找局部變量x——但x還沒被賦值!

三、global關鍵字:在函數(shù)內部修改全局變量

3.1 global的基本用法

# global關鍵字:聲明一個變量是全局變量
# 這樣即使在函數(shù)內部對它賦值,也是修改全局的那個

count = 0  # 全局計數(shù)器

def increment():
    """使用global聲明,修改全局變量"""
    global count
    count += 1
    print(f"計數(shù)器: {count}")

def increment_bad():
    """沒有global——創(chuàng)建了局部變量"""
    count = 999  # 這是局部變量,不影響全局的count
    print(f"局部count: {count}")

increment()      # 計數(shù)器: 1
print(f"全局count: {count}")  # 全局count: 1

increment_bad()  # 局部count: 999
print(f"全局count: {count}")  # 全局count: 1  —— 沒變!

increment()      # 計數(shù)器: 2
print(f"全局count: {count}")  # 全局count: 2  —— 繼續(xù)累加

3.2 global的多種使用場景

# 場景一:同時聲明多個全局變量
a = 1
b = 2
c = 3

def modify_all():
    global a, b, c  # 一次聲明多個
    a = 10
    b = 20
    c = 30

modify_all()
print(a, b, c)  # 10 20 30

# 場景二:在嵌套函數(shù)中使用global
total = 0

def outer():
    def inner():
        global total    # 聲明使用的是全局的total
        total += 1      # 修改全局變量
    inner()

outer()
print(total)  # 1

# ?? 注意:global聲明的是全局變量,不是外層函數(shù)的變量
# 對于嵌套函數(shù)中外層函數(shù)的變量,需要用nonlocal(下一節(jié)講)

# 場景三:在函數(shù)內部創(chuàng)建全局變量
def create_global():
    global new_variable
    new_variable = "我在函數(shù)內部被創(chuàng)建為全局變量!"
    print("全局變量已創(chuàng)建")

create_global()
print(new_variable)  # 我在函數(shù)內部被創(chuàng)建為全局變量!
# ?? 雖然可以這樣做,但強烈不推薦——讓代碼難以追蹤

3.3 什么時候應該(和不應該)使用global

# ? 適用場景:全局狀態(tài)管理(但要謹慎)
# 例如:應用配置、全局計數(shù)器、緩存

app_config = {
    "debug": False,
    "log_level": "INFO",
    "max_connections": 100,
}

def set_debug_mode(enabled):
    """修改全局配置"""
    global app_config
    app_config["debug"] = enabled
    print(f"調試模式: {'開啟' if enabled else '關閉'}")

# ?? 但要注意:如果你只是修改字典的內容(而不是重新賦值字典變量)
# 可以不需要global!

def set_debug_mode_no_global(enabled):
    """不需要global——因為我們沒有給app_config重新賦值"""
    app_config["debug"] = enabled  # 修改的是字典的內容,不是變量本身
    print(f"調試模式: {'開啟' if enabled else '關閉'}")

# ?? 關鍵區(qū)別:
# app_config["key"] = value  → 不需要global(修改對象的內容)
# app_config = new_dict      → 需要global(給變量重新賦值)

# ? 不推薦:濫用global
# 這種代碼讓人抓狂——不知道變量從哪里冒出來的
def do_work():
    global result     # 突然出現(xiàn)一個全局變量
    result = 42       # 調用者完全不知道result被設置了
    global status
    status = "done"
    # ... 更多代碼 ...

# ? 更好的做法:用返回值
def do_work_better():
    result = 42
    status = "done"
    return result, status  # 明確地返回

# 或者用類封裝
class Worker:
    def __init__(self):
        self.result = None
        self.status = "idle"

    def do_work(self):
        self.result = 42
        self.status = "done"
        return self.result, self.status

四、nonlocal關鍵字:修改外層函數(shù)的變量

4.1 nonlocal的基本概念

# nonlocal:在嵌套函數(shù)中,聲明變量來自外層函數(shù)(Enclosing作用域)
# 它不是全局的(不是G層),也不是局部的(不是L層),而是中間的E層

def outer():
    message = "Hello"  # 外層函數(shù)的局部變量

    def inner():
        nonlocal message  # 聲明:我要用外層的message,不是局部的
        message = "你好"  # 修改的是外層的message
        print(f"inner中: {message}")

    print(f"調用inner前: {message}")
    inner()
    print(f"調用inner后: {message}")
    # 輸出:
    # 調用inner前: Hello
    # inner中: 你好
    # 調用inner后: 你好    ← message被inner修改了!

outer()

4.2 nonlocal vs global 的區(qū)別

# ?? 對比實驗:global vs nonlocal

x = "全局X"

def outer():
    x = "外層X"

    def inner_global():
        global x           # 聲明使用全局變量x
        x = "被global修改"
        print(f"inner_global中: x={x}")

    def inner_nonlocal():
        nonlocal x         # 聲明使用外層變量x
        x = "被nonlocal修改"
        print(f"inner_nonlocal中: x={x}")

    print(f"outer開始: x={x}")
    inner_global()
    print(f"inner_global后, outer中的x: {x}")   # 還是"外層X"——沒變!
    inner_nonlocal()
    print(f"inner_nonlocal后, outer中的x: {x}")  # 變成"被nonlocal修改"
    print(f"全局x: {x}")                          # 全局x變成了"被global修改"

outer()
print(f"最終全局x: {x}")
# 最終全局x: 被global修改

# ?? 總結:
# global → 跳轉到G層(全局作用域)
# nonlocal → 跳轉到E層(嵌套作用域,最近的外層函數(shù))

4.3 nonlocal的典型應用

# 場景一:計數(shù)器(閉包)
def make_counter(start=0):
    """創(chuàng)建一個計數(shù)器函數(shù)"""
    count = start  # 外層變量

    def counter():
        nonlocal count
        count += 1  # 修改外層變量
        return count

    return counter

counter1 = make_counter()
print(counter1())  # 1
print(counter1())  # 2
print(counter1())  # 3

counter2 = make_counter(100)
print(counter2())  # 101
print(counter2())  # 102
print(counter1())  # 4  —— counter1獨立運行

# 場景二:求平均值(不斷累加)
def make_averager():
    """創(chuàng)建一個求平均值的函數(shù),持續(xù)接收新數(shù)據"""
    total = 0
    count = 0

    def averager(new_value):
        nonlocal total, count
        total += new_value
        count += 1
        return total / count

    return averager

avg = make_averager()
print(avg(10))  # 10.0
print(avg(20))  # 15.0
print(avg(30))  # 20.0
print(avg(40))  # 25.0

# ?? 如果沒有nonlocal,這段代碼會直接報錯
# 因為 averager 中的 total += new_value 相當于 total = total + new_value
# Python會認為total是局部變量,但它還沒被賦值!

4.4 nonlocal的查找規(guī)則

# nonlocal查找最近的外層作用域(不包括全局作用域)

def level1():
    x = "level1"

    def level2():
        x = "level2"

        def level3():
            nonlocal x  # 找最近的包含x的外層——是level2的x
            x = "被level3修改"
            print(f"level3中: x={x}")

        print(f"level3調用前, level2的x: {x}")
        level3()
        print(f"level3調用后, level2的x: {x}")

    level2()

level1()
# level3調用前, level2的x: level2
# level3中: x=被level3修改
# level3調用后, level2的x: 被level3修改

# ?? 如果外層所有函數(shù)都沒有這個變量,nonlocal會報錯
def func():
    def inner():
        # nonlocal nonexistent_var  # SyntaxError! 外層沒有這個變量
        pass
    inner()

五、閉包中的變量綁定

5.1 Python的變量綁定是"引用"而非"拷貝"

# ?? 閉包中的變量綁定是"晚期綁定"
# 捕獲的是變量的引用,而不是變量的值

# ?? 經典陷阱:lambda在循環(huán)中
functions = []
for i in range(5):
    functions.append(lambda: i)  # 每個lambda都引用同一個i

# 循環(huán)結束后i=4,所有l(wèi)ambda看到的i都是4
for f in functions:
    print(f(), end=" ")  # 4 4 4 4 4  —— 不是0 1 2 3 4!
print()

# ? 解決方案:使用默認參數(shù)"凍結"當前值
functions_v2 = []
for i in range(5):
    functions_v2.append(lambda x=i: x)  # 默認參數(shù)在定義時求值

for f in functions_v2:
    print(f(), end=" ")  # 0 1 2 3 4
print()

# ? 或者用函數(shù)工廠
def make_printer(n):
    return lambda: n  # n是make_printer的參數(shù),每次調用都創(chuàng)建新的作用域

functions_v3 = [make_printer(i) for i in range(5)]
for f in functions_v3:
    print(f(), end=" ")  # 0 1 2 3 4

5.2 閉包中修改可變對象

# 如果閉包中的變量是可變對象(list, dict等)
# 修改其內容不需要nonlocal
# 只有給變量名重新賦值才需要nonlocal

def make_basket():
    """購物籃——存放可變列表對象"""
    items = []  # 可變列表

    def add_item(item):
        # 不需要nonlocal!因為我們在修改列表的內容,不是給items重新賦值
        items.append(item)
        return len(items)

    def get_items():
        return list(items)  # 返回副本

    return add_item, get_items

add, get = make_basket()
print(add("蘋果"))    # 1
print(add("香蕉"))    # 2
print(add("橙子"))    # 3
print(get())          # ['蘋果', '香蕉', '橙子']

# ?? 關鍵區(qū)別:
# items.append(x)  → 不需要nonlocal(修改對象內容,items指向同一個對象)
# items = []       → 需要nonlocal(給變量重新賦值,items指向新對象)

六、globals()和locals()查看作用域

# ?? 使用內置函數(shù)查看當前作用域中的所有變量

global_x = 10
global_y = 20

def demo():
    local_a = 100
    local_b = 200

    print("=== 局部作用域 (locals()) ===")
    for name, value in locals().items():
        print(f"  {name} = {value}")

    print("\n=== 全局作用域 (globals()) ===")
    # 只顯示我們定義的變量
    for name, value in globals().items():
        if not name.startswith("_"):
            print(f"  {name} = {value}")

demo()

# 在模塊級別,locals()和globals()返回同一個字典
print("\n=== 模塊級別 ===")
print(f"locals() is globals(): {locals() is globals()}")  # True

七、實戰(zhàn)案例

7.1 實現(xiàn)函數(shù)調用計數(shù)器

def make_tracked_function(func):
    """裝飾器:統(tǒng)計函數(shù)被調用的次數(shù)"""
    call_count = 0

    def wrapper(*args, **kwargs):
        nonlocal call_count
        call_count += 1
        print(f"→ {func.__name__} 第{call_count}次被調用")
        result = func(*args, **kwargs)
        return result

    def get_count():
        """查看調用次數(shù)"""
        return call_count

    def reset_count():
        """重置計數(shù)器"""
        nonlocal call_count
        call_count = 0

    wrapper.get_count = get_count
    wrapper.reset_count = reset_count
    return wrapper

@make_tracked_function
def calculate(a, b):
    return a + b

print(calculate(1, 2))  # → calculate 第1次被調用  → 3
print(calculate(3, 4))  # → calculate 第2次被調用  → 7
print(calculate(5, 6))  # → calculate 第3次被調用  → 11
print(f"總調用次數(shù): {calculate.get_count()}")  # 3
calculate.reset_count()
print(f"重置后: {calculate.get_count()}")  # 0

7.2 配置管理器

# 使用作用域管理配置——避免全局變量污染

def create_config_manager(initial_config=None):
    """創(chuàng)建配置管理器(使用閉包封裝配置數(shù)據)"""
    if initial_config is None:
        config = {}
    else:
        config = dict(initial_config)  # 復制一份,避免外部修改

    def get(key, default=None):
        """獲取配置項"""
        return config.get(key, default)

    def set(key, value):
        """設置配置項"""
        nonlocal config  # 如果set需要替換整個config,就需要nonlocal
        config[key] = value
        print(f"配置已更新: {key} = {value}")

    def update(new_config):
        """批量更新配置"""
        config.update(new_config)

    def get_all():
        """獲取所有配置"""
        return dict(config)  # 返回副本

    def reset(initial_config=None):
        """重置配置"""
        nonlocal config
        if initial_config is None:
            config = {}
        else:
            config = dict(initial_config)

    return get, set, update, get_all, reset

# 使用配置管理器
get_config, set_config, update_config, get_all_config, reset_config = create_config_manager({
    "debug": False,
    "host": "localhost",
    "port": 8080,
})

print(get_config("host"))          # localhost
set_config("debug", True)          # 配置已更新: debug = True
update_config({"timeout": 30, "retries": 3})
print(get_all_config())
# {'debug': True, 'host': 'localhost', 'port': 8080, 'timeout': 30, 'retries': 3}

八、最佳實踐

# ? 最佳實踐一:優(yōu)先使用局部變量
# 局部變量訪問最快,且不污染全局作用域

# ? 最佳實踐二:避免使用global
# 用函數(shù)參數(shù)和返回值來傳遞數(shù)據,而不是修改全局狀態(tài)
# ? 不推薦
total = 0
def add_to_total(x):
    global total
    total += x
# ? 推薦
def add(x, y):
    return x + y

# ? 最佳實踐三:模塊級別的常量用全大寫
# PEP 8規(guī)范:全局常量用大寫字母+下劃線
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
DATABASE_URL = "mysql://localhost/mydb"

# ? 最佳實踐四:用類代替閉包管理復雜狀態(tài)
# 當閉包變得復雜時(多個函數(shù)、多個狀態(tài)變量),用類更清晰
# 閉包適合簡單場景(1-2個狀態(tài))
# 類適合復雜場景(多個方法和屬性)

# ? 最佳實踐五:理解"修改"和"重新賦值"的區(qū)別
# my_list.append(x)   → 修改對象,不需要nonlocal/global
# my_list = [x]       → 重新賦值,需要nonlocal/global
# my_dict["key"] = v  → 修改對象,不需要nonlocal/global
# my_dict = {"k": v}  → 重新賦值,需要nonlocal/global

九、總結

Python的作用域規(guī)則是LEGB——從Local到Enclosing到Global到Built-in,逐層向外查找。理解這個規(guī)則是寫出正確Python代碼的基礎。

核心要點:

關鍵字作用跳轉層級使用場景
(無需聲明)讀取變量LEGB逐層查找只讀訪問外部變量
global修改全局變量跳到G層修改模塊級變量
nonlocal修改外層變量跳到最近的E層閉包中修改捕獲的變量

使用建議:

  1. 優(yōu)先使用局部變量——最快、最安全
  2. 謹慎使用global——能不用就不用,用參數(shù)和返回值代替
  3. nonlocal是閉包的好伙伴——但復雜場景用類替代
  4. 理解"修改對象"vs"重新賦值"——前者不需要關鍵字聲明

掌握了作用域規(guī)則,你就真正理解了Python的變量世界。下一篇文章,我們將深入探討全局變量與局部變量的優(yōu)先級規(guī)則——以及那些讓你意想不到的邊界情況。

以上就是系統(tǒng)講解Python函數(shù)中global與nonlocal關鍵字的使用的詳細內容,更多關于Python global與nonlocal關鍵字的資料請關注腳本之家其它相關文章!

相關文章

  • Python調用Java可執(zhí)行jar包問題

    Python調用Java可執(zhí)行jar包問題

    這篇文章主要介紹了Python調用Java可執(zhí)行jar包問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Python中斷多重循環(huán)的思路總結

    Python中斷多重循環(huán)的思路總結

    在本文里小編給大家整理的是關于Python中斷多重循環(huán)的思路以及相關知識點,有需要的朋友們可以學習下。
    2019-10-10
  • Python中Toml配置文件的操作詳解

    Python中Toml配置文件的操作詳解

    TOML是一種用于配置文件的輕量級、易讀的數(shù)據序列化格式,這篇文章主要為大家詳細介紹了python如何對Toml配置文件進行簡單操作,感興趣的小伙伴可以了解下
    2023-09-09
  • 淺談Python中用datetime包進行對時間的一些操作

    淺談Python中用datetime包進行對時間的一些操作

    下面小編就為大家?guī)硪黄獪\談Python中用datetime包進行對時間的一些操作。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • pytorch加載訓練好的模型用來測試或者處理方式

    pytorch加載訓練好的模型用來測試或者處理方式

    這篇文章主要介紹了pytorch加載訓練好的模型用來測試或者處理方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Python configparser模塊配置文件過程解析

    Python configparser模塊配置文件過程解析

    這篇文章主要介紹了Python configparser模塊配置文件過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • Python實現(xiàn)遍歷子文件夾并將文件復制到不同的目標文件夾

    Python實現(xiàn)遍歷子文件夾并將文件復制到不同的目標文件夾

    這篇文章主要介紹了如何基于Python語言實現(xiàn)遍歷多個子文件夾,將每一個子文件夾中大量的文件,按照每一個文件的文件名稱的特點復制到不同的目標文件夾中,感興趣的可以了解下
    2023-08-08
  • PyTorch的torch.cat用法

    PyTorch的torch.cat用法

    這篇文章主要介紹了PyTorch的torch.cat用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-06-06
  • 通過數(shù)據庫向Django模型添加字段的示例

    通過數(shù)據庫向Django模型添加字段的示例

    這篇文章主要介紹了通過數(shù)據庫向Django模型添加字段的示例,Django是人氣最高的Python web開發(fā)框架,需要的朋友可以參考下
    2015-07-07
  • python使用PySimpleGUI設置進度條及控件使用

    python使用PySimpleGUI設置進度條及控件使用

    PySimpleGUI是一個在tkinter基礎上的,足夠簡單,方便,pythonic的GUI庫.本文給大家介紹python使用PySimpleGUI設置進度條的方法及進度條控件使用代碼,感興趣的朋友跟隨小編一起看看吧
    2021-06-06

最新評論

务川| 无极县| 锡林浩特市| 伽师县| 尚志市| 黑山县| 长宁县| 平顶山市| 枞阳县| 阿拉善盟| 介休市| 竹北市| 区。| 布尔津县| 蓝田县| 贵阳市| 文水县| 阿图什市| 长兴县| 巴林左旗| 临夏市| 莱芜市| 康定县| 额尔古纳市| 车致| 黄山市| 五指山市| 杨浦区| 贵溪市| 云阳县| 桃园县| 思茅市| 北票市| 平潭县| 惠州市| 田林县| 定陶县| 钟祥市| 湖北省| 瑞昌市| 湖州市|