欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python函數(shù)高級(jí)(命名空間、作用域、裝飾器)

 更新時(shí)間:2022年05月28日 14:34:22   作者:springsnow  
這篇文章介紹了Python函數(shù)的高級(jí)用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、名稱空間和作用域

1、命名空間(Namespace)

命名空間是從名稱到對(duì)象的映射,大部分的命名空間都是通過(guò) Python 字典來(lái)實(shí)現(xiàn)的。

命名空間提供了在項(xiàng)目中避免名字沖突的一種方法。各個(gè)命名空間是獨(dú)立的,沒(méi)有任何關(guān)系的,所以一個(gè)命名空間中不能有重名,但不同的命名空間是可以重名而沒(méi)有任何影響。

1、一般有三種命名空間:

  • 內(nèi)置名稱空間(built-in names):存放內(nèi)置的名字,如len/eval/enumerate/bytes/max/min/sorted/map/filter....
  • 全局名稱空間(global names):模塊中定義的名稱,記錄了模塊的變量,包括函數(shù)、類、其它導(dǎo)入的模塊、模塊級(jí)的變量和常量。
  • 局部名稱空間(local names):函數(shù)內(nèi)部的名字都是局部名稱空間,不同函數(shù)內(nèi)部的名字互不干涉。

2、命名空間查找順序:

如果找不到變量 runoob,它將放棄查找并引發(fā)一個(gè) NameError 異常:

NameError: name 'runoob' is not defined。
  • 查找順序:假設(shè)我們要使用變量 runoob,則 Python 的查找順序?yàn)椋?strong>局部的命名空間去 -> 全局命名空間 -> 內(nèi)置命名空間。
  • 執(zhí)行順序:先內(nèi)置(Python解釋器啟動(dòng)的時(shí)候才會(huì)生成)-> 全局(文件執(zhí)行的時(shí)候才會(huì)生成)-> 局部(函數(shù)調(diào)用的時(shí)候才會(huì)生成)

3、命名空間的生命周期:

命名空間的生命周期取決于對(duì)象的作用域,如果對(duì)象執(zhí)行完成,則該命名空間的生命周期就結(jié)束。

因此,我們無(wú)法從外部命名空間訪問(wèn)內(nèi)部命名空間的對(duì)象。

如下圖所示,相同的對(duì)象名稱可以存在于多個(gè)命名空間中。

2、作用域:

作用域就是一個(gè) Python 程序可以直接訪問(wèn)命名空間的正文區(qū)域。

全局名稱空間和局部名稱空間中可能會(huì)存在名字相同的變量,但是這兩個(gè)變量互不影響。

Python 中,程序的變量并不是在哪個(gè)位置都可以訪問(wèn)的,訪問(wèn)權(quán)限決定于這個(gè)變量是在哪里賦值的。

變量的作用域決定了在哪一部分程序可以訪問(wèn)哪個(gè)特定的變量名稱。

Python的作用域一共有4種,分別是:

  • L(Local):最內(nèi)層,包含局部變量,比如一個(gè)函數(shù)/方法內(nèi)部。
  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。比如兩個(gè)嵌套函數(shù),一個(gè)函數(shù)(或類) A 里面又包含了一個(gè)函數(shù) B ,那么對(duì)于 B 中的名稱來(lái)說(shuō) A 中的作用域就為 nonlocal。
  • G(Global):當(dāng)前腳本的最外層,比如當(dāng)前模塊的全局變量。
  • B(Built-in): 包含了內(nèi)建的變量/關(guān)鍵字等。,最后被搜索

對(duì)于變量作用域,變量的訪問(wèn)以: L –> E –> G –>B 的 規(guī)則查找。

在局部找不到,便會(huì)去局部外的局部找(例如閉包),再找不到就會(huì)去全局找,再者去內(nèi)置中找。

舉例:

x = 1

def func():
    print(x)  #10

x = 10
func()

內(nèi)置作用域是通過(guò)一個(gè)名為 builtin 的標(biāo)準(zhǔn)模塊來(lái)實(shí)現(xiàn)的,但是這個(gè)變量名自身并沒(méi)有放入內(nèi)置作用域內(nèi),所以必須導(dǎo)入這個(gè)文件才能夠使用它。

在Python3.0中,可以使用以下的代碼來(lái)查看到底預(yù)定義了哪些變量:

import builtins
print(dir(builtins))

Python 中只有模塊(module),類(class)以及函數(shù)(def、lambda)才會(huì)引入新的作用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)是不會(huì)引入新的作用域的,也就是說(shuō)這些語(yǔ)句內(nèi)定義的變量,外部也可以訪問(wèn),

如下代碼:實(shí)例中 msg 變量定義在 if 語(yǔ)句塊中,但外部還是可以訪問(wèn)的。如果將 msg 定義在函數(shù)中,則它就是局部變量,外部不能訪問(wèn)。

if True:
    msg = 'I am from Runoob'
print(msg)
# 'I am from Runoob'

3、全局變量和局部變量

定義在函數(shù)內(nèi)部的變量擁有一個(gè)局部作用域,定義在函數(shù)外的擁有全局作用域。

局部變量只能在其被聲明的函數(shù)內(nèi)部訪問(wèn),而全局變量可以在整個(gè)程序范圍內(nèi)訪問(wèn)。調(diào)用函數(shù)時(shí),所有在函數(shù)內(nèi)聲明的變量名稱都將被加入到作用域中。

# 作用域注意點(diǎn)
x = 1

def f1():  # 定義階段x=1
    print(x)  #1

def f2():
    x = 2  #此x為f2函數(shù)的局部變量,f1無(wú)法直接訪問(wèn)
    f1()

f2()

4、函數(shù)對(duì)象+作用域應(yīng)用

def f1():
    def inner():
        print('from inner')
    return inner

f = f1()  # from inner   。把局部定義的函數(shù)inner()放在全局之中

def bar():
    f()

bar()

5、global關(guān)鍵字修改全局作用域中的變量

函數(shù)內(nèi)可以訪問(wèn)全局變量,但不能直接更新(修改)其值,可以加上 global 引用以更新變量值 :

x = 1

def f1():
    x = 2

    def f2():
        global x  # 修改全局
        x = 3

    f2()

f1()
print(x)  # 3

6、nonlocal關(guān)鍵字修改嵌套作用域中的變量。

如果要修改嵌套作用域(enclosing 作用域,外層非全局作用域)中的變量則需要 nonlocal 關(guān)鍵字了

x = 1

def f1():
    x = 2

    def f2():
        nonlocal x
        x = 3

    f2()
    print(x)  # 3

f1()

二、閉包函數(shù)

閉包:閉是封閉(函數(shù)內(nèi)部函數(shù)),包是包含(該內(nèi)部函數(shù)對(duì)外部作用域而非全局作用域的變量的引用)。

閉包指的是:函數(shù)內(nèi)部函數(shù)對(duì)外部作用域而非全局作用域的引用。

def outter(x):
    x = 1

    def inner():
        print(x)

    return inner #返回的是函數(shù)名(函數(shù)對(duì)象)


f = outter(2)

f()  # 1
f()  # 1
f()  # 1
# 查看閉包的元素
print(f.__closure__[0].cell_contents)  # 1

閉包的意義:返回的函數(shù)對(duì)象,不僅僅是一個(gè)函數(shù)對(duì)象,在該函數(shù)外還包裹了一層作用域,這使得,該函數(shù)無(wú)論在何處調(diào)用,優(yōu)先使用自己外層包裹的作用域。

應(yīng)用領(lǐng)域:

延遲計(jì)算(原來(lái)我們是傳參,現(xiàn)在我們是包起來(lái))、爬蟲領(lǐng)域。

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()

三、函數(shù)裝飾器

裝飾器指的是為被裝飾器對(duì)象添加額外功能。因此定義裝飾器就是定義一個(gè)函數(shù),只不過(guò)該函數(shù)的功能是用來(lái)為其他函數(shù)添加額外的功能。裝飾器的實(shí)現(xiàn)必須遵循兩大原則:

  • 不修改被裝飾對(duì)象的源代碼
  • 不修改被裝飾對(duì)象的調(diào)用方式

裝飾器其實(shí)就是在遵循以上兩個(gè)原則的前提下為被裝飾對(duì)象添加新功能。

不改變函數(shù)體代碼,并且不改變函數(shù)調(diào)用方式,它本質(zhì)就是一個(gè)閉包函數(shù)。

def f1(x):
    def f2():
        print(x)  # 10
    return f2

f2 = f1()
f2()  # f2()

在不改變當(dāng)前函數(shù)的情況下, 給其增加新的功能:

def log(pr):  # 將被裝飾函數(shù)傳入
    def wrapper():
        print("**********")
        return pr()  # 執(zhí)行被裝飾的函數(shù)

    return wrapper  # 將裝飾完之后的函數(shù)返回(返回的是函數(shù)名)


@log
def pr():
    print("我是小小洋")


pr()

# **********
# 我是小小洋

回調(diào)函數(shù)和返回函數(shù)的實(shí)例就是裝飾器。

四、無(wú)參裝飾器

舉例:

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為被裝飾函數(shù)index的內(nèi)存地址,即index = wrapper
index()  # wrapper()

1、被裝飾函數(shù)有返回值:

如果原始的被裝飾函數(shù)index()有返回值的時(shí)候,wrapper()函數(shù)的返回值應(yīng)該和index()的返回值相同,也就是說(shuō),我們需要同步原始的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

2、被裝飾函數(shù)需要傳參:

如果原始的被裝飾函數(shù)index()方法需要傳參,那么我們之前的裝飾器是無(wú)法實(shí)現(xiàn)該功能的,由于有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

3、裝飾器模板

def deco(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

4、裝飾器語(yǔ)法糖:

在被裝飾函數(shù)正上方,并且是單獨(dú)一行寫上@裝飾器名

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

五、帶參數(shù)的裝飾器

注意無(wú)參裝飾器只套兩層。

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

我們首先看看三層閉包怎么運(yùn)用。

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()

3、有參三層裝飾器:

在函數(shù)中嵌入裝飾器

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

六、類裝飾器

沒(méi)錯(cuò),裝飾器不僅可以是函數(shù),還可以是類,相比函數(shù)裝飾器,類裝飾器具有靈活度大、高內(nèi)聚、封裝性等優(yōu)點(diǎn)。使用類裝飾器主要依靠類的__call__方法,當(dāng)使用 @ 形式將裝飾器附加到函數(shù)上時(shí),就會(huì)調(diào)用此方法。

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

使用裝飾器極大地復(fù)用了代碼,但是他有一個(gè)缺點(diǎn)就是原函數(shù)的元信息不見(jiàn)了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看例子:

# 裝飾器
def logged(func):
    def with_logging(*args, **kwargs):
        print func.__name__      # 輸出 'with_logging'
        print func.__doc__       # 輸出 None
        return func(*args, **kwargs)
    return with_logging

# 函數(shù)
@logged
def f(x):
   """does some math"""
   return x + x * x

logged(f)

不難發(fā)現(xiàn),函數(shù) f 被with_logging取代了,當(dāng)然它的docstring,__name__就是變成了with_logging函數(shù)的信息了。好在我們有functools.wraps,wraps本身也是一個(gè)裝飾器,它能把原函數(shù)的元信息拷貝到裝飾器里面的 func 函數(shù)中,這使得裝飾器里面的 func 函數(shù)也有和原函數(shù) 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

七、裝飾器順序

一個(gè)函數(shù)還可以同時(shí)定義多個(gè)裝飾器,比如:

@a
@b
@c
def f ():
    pass

它的執(zhí)行順序是從里到外,最先調(diào)用最里層的裝飾器,最后調(diào)用最外層的裝飾器,它等效于

f = a(b(c(f)))

八、裝飾器使用場(chǎng)景

現(xiàn)在我們來(lái)看一下裝飾器在哪些地方特別耀眼,以及使用它可以讓一些事情管理起來(lái)變得更簡(jiǎn)單。

授權(quán)(Authorization)

裝飾器能有助于檢查某個(gè)人是否被授權(quán)去使用一個(gè)web應(yīng)用的端點(diǎn)(endpoint)。它們被大量使用于Flask和Django web框架中。這里是一個(gè)例子來(lái)使用基于裝飾器的授權(quán):

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

日志(Logging)

日志是裝飾器運(yùn)用的另一個(gè)亮點(diǎn)。這是個(gè)例子:

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

到此這篇關(guān)于Python函數(shù)高級(jí)用法的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論