>  기사  >  백엔드 개발  >  Python의 네임스페이스, 범위 및 데코레이터를 사용하는 방법은 무엇입니까?

Python의 네임스페이스, 범위 및 데코레이터를 사용하는 방법은 무엇입니까?

PHPz
PHPz앞으로
2023-05-09 19:04:17729검색

    1. 네임스페이스 및 범위

    1. 네임스페이스(Namespace)

    네임스페이스는 이름에서 객체로의 매핑입니다. 대부분의 네임스페이스는 Python 사전을 통해 구현됩니다.

    네임스페이스는 프로젝트에서 이름 충돌을 방지하는 방법을 제공합니다. 각 네임스페이스는 독립적이고 관계가 없으므로 한 네임스페이스에 중복된 이름이 있을 수 없지만 서로 다른 네임스페이스는 아무런 영향 없이 중복된 이름을 가질 수 있습니다.

    1. 일반적으로 세 가지 네임스페이스가 있습니다.
    • 내장 네임스페이스(내장 이름): len/eval/enumerate/bytes/max/min/sorted/map/filter....

    • 글로벌 네임스페이스(전역 이름)과 같은 내장 이름을 저장합니다. 모듈 내 이름은 함수, 클래스, 기타 가져온 모듈, 모듈 수준 변수 및 상수를 포함하여 모듈의 변수를 기록합니다.

    • 로컬 네임스페이스(로컬 이름): 함수 내부의 이름은 모두 로컬 네임스페이스이며, 서로 다른 함수 내부의 이름은 서로 간섭하지 않습니다.

    Python의 네임스페이스, 범위 및 데코레이터를 사용하는 방법은 무엇입니까?

    2. 네임스페이스 검색 순서:

    runoob 변수를 찾을 수 없으면 검색을 포기하고 NameError 예외가 발생합니다.

    NameError: name 'runoob' is not defined。
    • 검색 순서: 변수를 사용한다고 가정합니다. runoob, Python 검색 순서는 로컬 네임스페이스 -> 글로벌 네임스페이스 -> 내장 네임스페이스 로 이동합니다.

    • 실행 순서: 내장 먼저(파이썬 인터프리터가 시작될 때 생성됨) -> 전역(파일이 실행될 때 생성됨) -> 로컬(함수 호출 시 생성됨)

    3. 네임스페이스 수명 주기:

    네임스페이스의 수명 주기는 개체의 범위에 따라 다릅니다. 개체 실행이 완료되면 네임스페이스의 수명 주기가 종료됩니다.

    따라서 외부 네임스페이스에서는 내부 네임스페이스의 개체에 액세스할 수 없습니다.

    아래 그림과 같이 동일한 객체 이름이 여러 네임스페이스에 존재할 수 있습니다.

    Python의 네임스페이스, 범위 및 데코레이터를 사용하는 방법은 무엇입니까?

    2. 범위:

    범위는 Python 프로그램이 직접 액세스할 수 있는 네임스페이스의 본문 영역입니다.

    글로벌 네임스페이스와 로컬 네임스페이스에 동일한 이름을 가진 변수가 존재할 수 있지만, 이 두 변수는 서로 영향을 미치지 않습니다.

    Python에서는 프로그램 변수에 모든 곳에서 액세스할 수 없습니다. 액세스 권한은 변수에 값이 할당된 위치에 따라 다릅니다.

    변수의 범위에 따라 프로그램의 어느 부분이 특정 변수 이름에 액세스할 수 있는지가 결정됩니다.

    Python에는 다음과 같은 4가지 유형의 범위가 있습니다.

    • L(로컬) : 함수/메서드 내부와 같은 지역 변수를 포함한 가장 안쪽 계층입니다.

    • E(인클로징): 비지역 및 비글로벌 변수를 포함합니다. 예를 들어 두 개의 중첩된 함수가 있고 함수(또는 클래스) A에 함수 B가 포함된 경우 B의 이름에 대해 A의 범위는 로컬이 아닙니다.

    • G(Global): 현재 모듈의 전역 변수와 같은 현재 스크립트의 가장 바깥쪽 레이어입니다.

    • B(내장): 내장 변수/키워드 등이 포함되어 있습니다. , 그리고 마지막으로 검색되었습니다.

    변수 범위의 경우 변수 액세스는 다음 규칙에 따라 검색됩니다. L –> E –>G >B.

    로컬에서 찾을 수 없으면 로컬에서 찾습니다(예: 클로저). 전역적으로 찾은 다음 내장에서 찾습니다.

    Python의 네임스페이스, 범위 및 데코레이터를 사용하는 방법은 무엇입니까?

    예:

    x = 1
    
    def func():
        print(x)  #10
    
    x = 10
    func()

    내장 스코프는 내장이라는 표준 모듈을 통해 구현되지만, 변수 이름 자체는 내장 스코프에 들어가지 않으므로 이 파일을 가져와서 사용해야 합니다.

    Python3.0에서는 다음 코드를 사용하여 어떤 변수가 미리 정의되어 있는지 확인할 수 있습니다.

    import builtins
    print(dir(builtins))

    Python에서는 모듈, 클래스 및 함수(def, 람다)만 새 함수를 도입합니다. 도메인, 기타 코드 블록(예: if /elif/else/, try/out, for/while 등)은 새로운 범위를 도입하지 않습니다. 즉, 이러한 문에 정의된 변수는 외부에서도 액세스할 수 있습니다.

    다음 코드: 예제에서, msg 변수는 if 문 블록에 정의되어 있지만 여전히 외부에서 액세스할 수 있습니다. msg가 함수에 정의되어 있으면 지역 변수이므로 외부에서 접근할 수 없습니다.

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

    3. 전역 변수와 지역 변수

    함수 내부에 정의된 변수는 지역 범위를 가지며, 함수 외부에서 정의된 변수는 전역 범위를 갖습니다.

    局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。

    # 作用域注意点
    x = 1
    
    def f1():  # 定义阶段x=1
        print(x)  #1
    
    def f2():
        x = 2  #此x为f2函数的局部变量,f1无法直接访问
        f1()
    
    f2()

    4、函数对象+作用域应用

    def f1():
        def inner():
            print('from inner')
        return inner
    
    f = f1()  # from inner   。把局部定义的函数inner()放在全局之中
    
    def bar():
        f()
    
    bar()

    5、global关键字修改全局作用域中的变量

    函数内可以访问全局变量,但不能直接更新(修改)其值,可以加上 global 引用以更新变量值 :

    x = 1
    
    def f1():
        x = 2
    
        def f2():
            global x  # 修改全局
            x = 3
    
        f2()
    
    f1()
    print(x)  # 3

    6、nonlocal关键字修改嵌套作用域中的变量。

    如果要修改嵌套作用域(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()

    1、被装饰函数有返回值:

    如果原始的被装饰函数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

    2、被装饰函数需要传参:

    如果原始的被装饰函数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

    3、装饰器模板

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

    4、装饰器语法糖:

    在被装饰函数正上方,并且是单独一行写上@装饰器名

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

    3、有参三层装饰器:

    在函数中嵌入装饰器

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

    八、装饰器使用场景

    现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

    授权(Authorization)

    装饰器能有助于检查某个人是否被授权去使用一个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

    日志(Logging)

    日志是装饰器运用的另一个亮点。这是个例子:

    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의 네임스페이스, 범위 및 데코레이터를 사용하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제