您好,登錄后才能下訂單哦!
今天小編給大家分享一下Python命名空間、作用域和裝飾器怎么使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
命名空間是從名稱到對象的映射,大部分的命名空間都是通過 Python 字典來實現的。
命名空間提供了在項目中避免名字沖突的一種方法。各個命名空間是獨立的,沒有任何關系的,所以一個命名空間中不能有重名,但不同的命名空間是可以重名而沒有任何影響。
內置名稱空間(built-in names):存放內置的名字,如len/eval/enumerate/bytes/max/min/sorted/map/filter....
全局名稱空間(global names):模塊中定義的名稱,記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。
局部名稱空間(local names):函數內部的名字都是局部名稱空間,不同函數內部的名字互不干涉。
如果找不到變量 runoob,它將放棄查找并引發一個 NameError 異常:
NameError: name 'runoob' is not defined。
查找順序:假設我們要使用變量 runoob,則 Python 的查找順序為:局部的命名空間去 -> 全局命名空間 -> 內置命名空間。
執行順序:先內置(Python解釋器啟動的時候才會生成)-> 全局(文件執行的時候才會生成)-> 局部(函數調用的時候才會生成)
命名空間的生命周期取決于對象的作用域,如果對象執行完成,則該命名空間的生命周期就結束。
因此,我們無法從外部命名空間訪問內部命名空間的對象。
如下圖所示,相同的對象名稱可以存在于多個命名空間中。
作用域就是一個 Python 程序可以直接訪問命名空間的正文區域。
全局名稱空間和局部名稱空間中可能會存在名字相同的變量,但是這兩個變量互不影響。
Python 中,程序的變量并不是在哪個位置都可以訪問的,訪問權限決定于這個變量是在哪里賦值的。
變量的作用域決定了在哪一部分程序可以訪問哪個特定的變量名稱。
Python的作用域一共有4種,分別是:
L(Local):最內層,包含局部變量,比如一個函數/方法內部。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。比如兩個嵌套函數,一個函數(或類) A 里面又包含了一個函數 B ,那么對于 B 中的名稱來說 A 中的作用域就為 nonlocal。
G(Global):當前腳本的最外層,比如當前模塊的全局變量。
B(Built-in): 包含了內建的變量/關鍵字等。,最后被搜索
對于變量作用域,變量的訪問以: L –> E –> G –>B 的 規則查找。
在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內置中找。
舉例:
x = 1 def func(): print(x) #10 x = 10 func()
內置作用域是通過一個名為 builtin 的標準模塊來實現的,但是這個變量名自身并沒有放入內置作用域內,所以必須導入這個文件才能夠使用它。
在Python3.0中,可以使用以下的代碼來查看到底預定義了哪些變量:
import builtins print(dir(builtins))
Python 中只有模塊(module),類(class)以及函數(def、lambda)才會引入新的作用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)是不會引入新的作用域的,也就是說這些語句內定義的變量,外部也可以訪問,
如下代碼:實例中 msg 變量定義在 if 語句塊中,但外部還是可以訪問的。如果將 msg 定義在函數中,則它就是局部變量,外部不能訪問。
if True: msg = 'I am from Runoob' print(msg) # 'I am from Runoob'
定義在函數內部的變量擁有一個局部作用域,定義在函數外的擁有全局作用域。
局部變量只能在其被聲明的函數內部訪問,而全局變量可以在整個程序范圍內訪問。調用函數時,所有在函數內聲明的變量名稱都將被加入到作用域中。
# 作用域注意點 x = 1 def f1(): # 定義階段x=1 print(x) #1 def f2(): x = 2 #此x為f2函數的局部變量,f1無法直接訪問 f1() f2()
def f1(): def inner(): print('from inner') return inner f = f1() # from inner 。把局部定義的函數inner()放在全局之中 def bar(): f() bar()
函數內可以訪問全局變量,但不能直接更新(修改)其值,可以加上 global 引用以更新變量值 :
x = 1 def f1(): x = 2 def f2(): global x # 修改全局 x = 3 f2() f1() print(x) # 3
如果要修改嵌套作用域(enclosing 作用域,外層非全局作用域)中的變量則需要 nonlocal 關鍵字了
x = 1 def f1(): x = 2 def f2(): nonlocal x x = 3 f2() print(x) # 3 f1()
閉包:閉是封閉(函數內部函數),包是包含(該內部函數對外部作用域而非全局作用域的變量的引用)。
閉包指的是:函數內部函數對外部作用域而非全局作用域的引用。
def outter(x): x = 1 def inner(): print(x) return inner #返回的是函數名(函數對象) f = outter(2) f() # 1 f() # 1 f() # 1 # 查看閉包的元素 print(f.__closure__[0].cell_contents) # 1
閉包的意義:返回的函數對象,不僅僅是一個函數對象,在該函數外還包裹了一層作用域,這使得,該函數無論在何處調用,優先使用自己外層包裹的作用域。
延遲計算(原來我們是傳參,現在我們是包起來)、爬蟲領域。
import requests def outter(url): def get(): response = requests.get(url) print(f"done: {url}") return get baidu = outter('https://www.baidu.com') python = outter('https://www.python.org') baidu() baidu() python() python()
裝飾器指的是為被裝飾器對象添加額外功能。因此定義裝飾器就是定義一個函數,只不過該函數的功能是用來為其他函數添加額外的功能。裝飾器的實現必須遵循兩大原則:
不修改被裝飾對象的源代碼
不修改被裝飾對象的調用方式
裝飾器其實就是在遵循以上兩個原則的前提下為被裝飾對象添加新功能。
不改變函數體代碼,并且不改變函數調用方式,它本質就是一個閉包函數。
def f1(x): def f2(): print(x) # 10 return f2 f2 = f1() f2() # f2()
在不改變當前函數的情況下, 給其增加新的功能:
def log(pr): # 將被裝飾函數傳入 def wrapper(): print("**********") return pr() # 執行被裝飾的函數 return wrapper # 將裝飾完之后的函數返回(返回的是函數名) @log def pr(): print("我是小小洋") pr() # ********** # 我是小小洋
回調函數和返回函數的實例就是裝飾器。
舉例:
import time def index(): print('welcome to index') time.sleep(1) def time_count(func): # func = 最原始的index def wrapper(): start = time.time() func() end = time.time() print(f"{func} time is {start - end}") # time is -1.0038220882415771 return wrapper index = time_count(index) # index為被裝飾函數index的內存地址,即index = wrapper index() # wrapper()
如果原始的被裝飾函數index()有返回值的時候,wrapper()函數的返回值應該和index()的返回值相同,也就是說,我們需要同步原始的index()和wrapper()方法的返回值。
import time def index(): print('welcome to index') time.sleep(1) return 123 def time_count(func): # func = 最原始的index def wrapper(): start = time.time() res1 = func() end = time.time() print(f"{func} time is {start - end}") # time is -1.0050289630889893 return res1 return wrapper index = time_count(index) res = index() print(f"res: {res}") # res: 123
如果原始的被裝飾函數index()方法需要傳參,那么我們之前的裝飾器是無法實現該功能的,由于有wrapper()=index(),所以給wrapper()方法傳參即可。
import time def index(): print('welcome to index') time.sleep(1) return 123 def home(name): print(f"welcome {name} to home page") time.sleep(1) return name def time_count(func): def wrapper(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print(f"{func} time is {start-end}") # time is -1.0039079189300537 return res return wrapper home = time_count(home) res = home('egon') print(f"res: {res}") #res: egon
def deco(func): def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res return wrapper
在被裝飾函數正上方,并且是單獨一行寫上@裝飾器名
import time def time_count(func): #裝飾器 # func = 最原始的index def wrapper(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print(f"{func} time is {start-end}") # time is -1.0005171298980713 return res return wrapper @time_count # home = time_count(home) def home(name): print(f"welcome {name} to home page") #welcome egon to home page time.sleep(1) return name res = home('egon') print(f"res: {res}") #res: egon
注意無參裝飾器只套兩層。
import time current_user = {'username': None} def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res1 = func(*args, **kwargs) return res1 user = input('username: ').strip() pwd = input('password: ').strip() if user == 'nick' and pwd == '123': print('login successful') current_user['username'] = user res1 = func(*args, **kwargs) return res1 else: print('user or password error') return wrapper @login def index(): print('welcome to index') time.sleep(1) res = index() #username: nick #password: 123 #login successful #welcome to index
我們首先看看三層閉包怎么運用。
def f1(y): def f2(): x = 1 def f3(): print(f"x: {x}") # x: 1 print(f"y: {y}") # x: 1 return f3 return f2 f2 = f1(2) f3 = f2() f3()
在函數中嵌入裝飾器
import time current_user = {'username': None} def auth(engine='file'): def login(func): def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() if engine == 'file': print('base of file') if user == 'nick' and pwd == '123': print('login successful') current_user['username'] = user res = func(*args, **kwargs) return res else: print('user or password error') elif engine == 'mysql': print('base of mysql, please base of file') return wrapper return login @auth(engine='file') def index(): print('welcome to index') time.sleep(1) res = index()
username: nick
password: 123
base of file
login successful
welcome to index
沒錯,裝飾器不僅可以是函數,還可以是類,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器主要依靠類的__call__方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法。
class Foo(object): def __init__(self, func): self._func = func def __call__(self): print ('class decorator runing') self._func() print ('class decorator ending') @Foo def bar(): print ('bar') bar() functools.wraps
使用裝飾器極大地復用了代碼,但是他有一個缺點就是原函數的元信息不見了,比如函數的docstring、__name__、參數列表,先看例子:
# 裝飾器 def logged(func): def with_logging(*args, **kwargs): print func.__name__ # 輸出 'with_logging' print func.__doc__ # 輸出 None return func(*args, **kwargs) return with_logging # 函數 @logged def f(x): """does some math""" return x + x * x logged(f)
不難發現,函數 f 被with_logging取代了,當然它的docstring,__name__就是變成了with_logging函數的信息了。好在我們有functools.wraps,wraps本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器里面的 func 函數中,這使得裝飾器里面的 func 函數也有和原函數 foo 一樣的元信息了。
from functools import wraps def logged(func): @wraps(func) def with_logging(*args, **kwargs): print func.__name__ # 輸出 'f' print func.__doc__ # 輸出 'does some math' return func(*args, **kwargs) return with_logging @logged def f(x): """does some math""" return x + x * x
一個函數還可以同時定義多個裝飾器,比如:
@a @b @c def f (): pass
它的執行順序是從里到外,最先調用最里層的裝飾器,最后調用最外層的裝飾器,它等效于
f = a(b(c(f)))
現在我們來看一下裝飾器在哪些地方特別耀眼,以及使用它可以讓一些事情管理起來變得更簡單。
裝飾器能有助于檢查某個人是否被授權去使用一個web應用的端點(endpoint)。它們被大量使用于Flask和Django web框架中。這里是一個例子來使用基于裝飾器的授權:
from functools import wraps def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated
日志是裝飾器運用的另一個亮點。這是個例子:
from functools import wraps def logit(func): @wraps(func) def with_logging(*args, **kwargs): print(func.__name__ + " was called") return func(*args, **kwargs) return with_logging @logit def addition_func(x): """Do some math.""" return x + x result = addition_func(4) # Output: addition_func was called
以上就是“Python命名空間、作用域和裝飾器怎么使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。