首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 檢查變數是否定義的四種方法:locals、globals、vars、try/except

     編輯:更多程式設計知識

在動態語言 Python 中,檢查變數是否已定義是常見需求(如配置檔載入、選參數處理、動態屬性存取)。本文系統整理四種核心方法、適用範圍、效能差異、最佳實務與陷阱避坑指南。

方法比較總覽

方法適用作用域回傳型別效能推薦場景
'var' in locals()區域bool快函式內檢查區域變數
'var' in globals()全域bool快模組層級檢查全域變數
'var' in vars(obj)物件屬性bool快檢查物件/類別屬性
try/except NameError任意值/例外較慢(例外開銷)不確定作用域、需取得值
hasattr(obj, 'attr')物件屬性bool快檢查物件屬性(含繼承)
getattr(obj, 'attr', default)物件屬性值/default快安全取得屬性值(首選)

核心方法詳解

1. locals() — 區域作用域字典


def demo():
    x = 10
    print('x' in locals())   # True
    print('y' in locals())   # False
    
    # 注意:locals() 在函式內回傳副本(CPython 實作細節),寫入不影響實際區域變數
    locals()['z'] = 20
    # print(z)  # NameError! 實際未定義
    
demo()

2. globals() — 全域作用域字典


GLOBAL_CONFIG = {"debug": True}

def check_config():
    if 'GLOBAL_CONFIG' in globals():
        print("全域配置存在:", globals()['GLOBAL_CONFIG'])
    
    # 可動態新增全域變數
    globals()['RUNTIME_FLAG'] = True

check_config()
print(RUNTIME_FLAG)  # True

3. vars() — 物件 __dict__ 存取


class Config:
    def __init__(self):
        self.host = "localhost"
        self.port = 8080

cfg = Config()
print('host' in vars(cfg))      # True
print('timeout' in vars(cfg))   # False

# vars() 無參數等同 locals()
def func():
    a = 1
    print(vars() == locals())   # True

4. try/except NameError — 通用捕捉


def get_var_safely(var_name, default=None):
    """通用安全取得變數值"""
    try:
        return eval(var_name)
    except NameError:
        return default

# 或直接在程式碼中
try:
    value = some_variable
    print(f"已定義: {value}")
except NameError:
    print("未定義")
    value = "default"

進階場景與最佳實務

場景一:配置檔案載入(預設值覆蓋)


# config_default.py
DEBUG = False
PORT = 8080
HOST = "0.0.0.0"

# config_local.py (可選,不提交版控)
# DEBUG = True
# PORT = 3000

# main.py
from config_default import *

# 嘗試載入本地覆蓋
try:
    from config_local import *
except ImportError:
    pass

print(DEBUG, PORT, HOST)  # 使用本地值或預設值

場景二:選參數/環境變數檢查


import os
import sys

# 環境變數檢查
database_url = os.getenv('DATABASE_URL')
if not database_url:
    # 回退預設
    database_url = "sqlite:///app.db"

# 命令列參數檢查
verbose = '--verbose' in sys.argv
config_file = 'config.yaml'
if '--config' in sys.argv:
    idx = sys.argv.index('--config')
    if idx + 1 < len(sys.argv):
        config_file = sys.argv[idx + 1]

場景三:動態屬性存取(物件導向)


class Settings:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)
    
    def get(self, key, default=None):
        """安全取得屬性"""
        return getattr(self, key, default)
    
    def has(self, key):
        """檢查屬性存在"""
        return hasattr(self, key)

settings = Settings(debug=True, port=8080)
print(settings.get('debug'))        # True
print(settings.get('timeout', 30))  # 30 (預設值)
print(settings.has('port'))         # True

場景四:模組層級條件定義


# 僅在特定條件定義變數
import sys

if sys.platform == 'win32':
    PLATFORM_CMD = 'cmd.exe'
else:
    PLATFORM_CMD = '/bin/bash'

# 後續代碼安全使用
print(PLATFORM_CMD)  # 保證已定義

效能測試與比較


import timeit

# 定義測試變數
test_var = 42

# 方法 1: in locals()
t1 = timeit.timeit("'test_var' in locals()", globals=globals(), number=100000)
# 方法 2: try/except
t2 = timeit.timeit("""
try:
    test_var
except NameError:
    pass
""", globals=globals(), number=100000)
# 方法 3: getattr (需物件)
obj = type('', (), {'test_var': 42})()
t3 = timeit.timeit("hasattr(obj, 'test_var')", globals=globals(), number=100000)
t4 = timeit.timeit("getattr(obj, 'test_var', None)", globals=globals(), number=100000)

print(f"in locals(): {t1:.4f}s")
print(f"try/except:  {t2:.4f}s")
print(f"hasattr:     {t3:.4f}s")
print(f"getattr:     {t4:.4f}s")

典型結果(相對比較)


in locals():  0.012s  (最快)
hasattr:      0.015s
getattr:      0.016s
try/except:   0.045s  (例外開銷大,但未定義時才拋出)

常見陷阱與避坑

陷阱錯誤代碼正確做法
locals() 寫入不生效locals()['x'] = 1直接賦值 x = 1 或用 globals()
檢查全域用 locals()'x' in locals() (模組層級)模組層級 locals()==globals(),但語意用 globals()
vars() 無參數在類別方法中class A: def f(self): print(vars())vars() 等同 locals(),用 vars(self) 取實例屬性
hasattr 捕捉所有例外hasattr 會吞沒 property/descriptor 內部錯誤Python 3.2+ 已修正,僅捕捉 AttributeError
eval 注入風險eval(user_input)絕不 eval 使用者輸入,用 getattr/字典查找

決策樹:選哪種方法?


需要檢查變數是否定義
    │
    ├─ 知道確切作用域?
    │   ├─ 區域變數 (函式內) → 'var' in locals()
    │   ├─ 全域變數 (模組層級) → 'var' in globals()
    │   └─ 物件屬性 → hasattr(obj, 'attr') / getattr(obj, 'attr', default)
    │
    └─ 不確定作用域 / 需同時取得值
        ├─ 簡單腳本、除錯 → try/except NameError
        └─ 庫代碼、效能敏感 → 明確作用域查找 (避免例外開銷)

總結

Python 檢查變數定義的核心方法:區域用 locals()、全域用 globals()、物件屬性用 hasattr()/getattr()、通用捕捉用 try/except NameError。最佳實務:優先用 getattr(obj, key, default) 安全取值,避免例外控制流;明確作用域查找效能最佳;vars() 僅用於物件 __dict__ 檢查。理解各方法作用域、效能、副作用差異,能寫出更健壯、高效的動態代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved