python 命名空间 作用域,python函数装饰器详解

  python 命名空间 作用域,python函数装饰器详解

  本文介绍Python函数的高级用法,通过示例代码详细介绍。对大家的学习或者工作都有一定的参考价值,有需要的朋友可以参考一下。

  00-1010 1.命名空间和范围1。命名空间)1。一般来说,有三种名称空间:2。命名空间搜索序列33603。命名空间生命周期:2。范围:3。全局变量和局部变量4。功能对象范围应用5。Global关键字修改全局范围6中的变量。nonlocal关键字修改嵌套范围内的变量。二。封闭函数的应用领域:III。功能装饰四。无参数装饰器1。修饰函数有一个返回值:II。修饰函数需要传递参数:III。装饰模板4。Decorator语法sugar : V .带参数的Decorator 3。三层参数装饰器3360 VI。班级装饰七。装饰序列八。装饰者使用场景授权(Authorization)

  

目录

  

一、名称空间和作用域

  命名空间是名称到对象的映射,大部分命名空间都是通过Python字典实现的。

  命名空间提供了一种避免项目中名称冲突的方法。每个命名空间都是独立的,互不相关,所以一个命名空间不能有重名,但是不同的命名空间可以重名,没有任何影响。

  

1、命名空间(Namespace)

  内置命名空间(built-in names):存储内置名称,如len/eval/enumerate/bytes/max/min/sorted/map/filter.全局命名空间(global names):在模块中定义的名字,它记录了模块的变量,包括函数、类、其他导入的模块、模块级变量和常量。本地命名空间(local names):函数内部的名字都是本地命名空间,不同函数内部的名字互不干扰。

  

1、一般有三种命名空间:

  如果找不到变量runoob,它将放弃搜索并抛出NameError异常:

  NameError:未定义名称“runoob ”.

  搜索顺序:假设我们要使用变量runoob,Python的搜索顺序是:局部的命名空间去 -g.

  t; 全局命名空间 -> 内置命名空间。

  • 执行顺序:先内置(Python解释器启动的时候才会生成)-> 全局(文件执行的时候才会生成)-> 局部(函数调用的时候才会生成)
  •   

      

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

      命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。

      因此,我们无法从外部命名空间访问内部命名空间的对象。

      如下图所示,相同的对象名称可以存在于多个命名空间中。

      

      

      

    2、作用域:

      作用域就是一个 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

      

      

    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函数高级用法的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持盛行IT软件开发工作室。

    郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

    留言与评论(共有 条评论)
       
    验证码: