zoukankan      html  css  js  c++  java
  • Python的学习之旅———函数对象、函数嵌套、名称空间与作用域、装饰器

    一 函数对象

    1 可以被引用

    2 可以当作参数传递

    3 返回值可以是函数

    4 可以当作容器类型的元素

    1 def foo():
    2     print('from foo')
    3  
    4 func=foo
    5  
    6 print(foo)
    7 print(func)
    8 func()

    二 函数嵌套

      函数可以嵌套调用

      嵌套定义

    名称空间:存放名字的地方,准确的说名称空间是存放名字与变量值绑定关系的地方

    一、名称空间又分为:

      内置名称空间:在python解释器启动时产生,存放一些python内置的名字

      全局名称空间:在执行文件时产生,存放文件级别定义的名字

      局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间
      用来存放该函数内定义的名字,该名字在函数调用时生效,在函数调用结束后失效

    二、加载顺序

      加载顺序:内置--->全局--->局部

    三、名字的查找顺序

      局部->全局->内置

    四、作用域

      定义:作用的范围

      一、分为

           全局作用域:全局存活,全局有效:可以用globals()查看

           局部作用域:临时存活,局部有效可以用locals()查看

            二、改变全局变量

            1、可以用global在局部改变全局变量

             声明全局变量,如果在局部要对全局变量修改,需要在局部也要先声明该全局变量:

    2、可变变量可以不需要global在函数内进行修改

    l=[]
    def f2():
        l.append('f2')
    
    f2()
    print(l)

    3、、nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量

    三、作用域关系

    在函数定义时就已经固定于调用位置无关,在调用函数时,必须回到函数原来定义的位置去找作用域关系

    LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__

    locals是函数内的名字空间,包括局部变量和形参

    enclosing 外部嵌套函数的名字空间(闭包中常见)

    globals 全局变量,函数定义所在模块的名字空间

    builtins 内置模块的名字空间

    四 闭包函数

    内部函数包含外部作用域而非全局作用域的引用。

    闭包函数的依据是,函数内部未找到变量相应的值时,会先向上一级函数中寻找

    变量值。

    二 闭包的意义与应用

    为了装饰器做准备 (我是这么觉得的)

    五 装饰器

    装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。

    强调装饰器的原则:

    1 不修改被装饰对象的源代码

    2 不修改被装饰对象的调用方式

    装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

    装饰器的原则:在不修改函数调用方式 和函数本身代码的情况下。增加新功能开放封闭原则,对扩展是开放的,对修改是封闭的。

    无参装饰器

     1 import time
     2 
     3 def timmer(func):
     4     # func=index #最原始的index函数的内存地址
     5     def inner():
     6         start_time=time.time()
     7         func()
     8         stop_time=time.time()
     9         print('run time is :[%s]' %(stop_time-start_time))
    10     return inner
    11 
    12 @timmer #index=timmer(index)
    13 def index():
    14     time.sleep(3)
    15     print('welcome to index page')
    16 index()

    有几点是关键  代码在执行到@timmer 做了两件事

    1将 index()函数名‘index’作为参数传给timmer 于是有了  timmer(index),

    2将timmer()(函数)赋值给了index,(在python中变量名是可以反复使用的,这种操作我觉得就是死皮赖脸的做到调用方式不变的目的)index=timmer()。同时触发函数timmer()。

    inner是一个闭包函数,所以 4 # func=index #最原始的index函数的内存地址 。因为有闭包函数的特性,所以inner在内部的函数func()在寻找func值的时候会向上一层寻找

    于是找到了 func=index。这样就有了inner()内部函数 func()变成了 index(),inner函数最后又将自己return给timmer() ,但此时 inner函数并未触发函数的调用。

    这时index=timmer()实际上是=inner 此时 @timmer的任务就完成了(得到了inderx=inner),代码直接跳到16行进行index() 此时的index()==inner()函数触发.但此时的index()已经不是

    最原始的index()了,而是inner函数,只不过我们想做到调用方式不变而强行使inderx=inner。

    接下来的顺序 5 -6-7-14-15-8-9.

    装饰器 实现的要素

    1 闭包函数找名称空间的时候可以向上一层寻找。
    2 由于作用域的原因 inner函数是无法在外部调用的 但是return打破了层级限制。
    3 变量名是可以反复使用的,使调用方式不变

    装饰器修订

     1 import time
     2 from functools import wraps
     3 
     4 def timmer(func):
     5     @wraps(func)              #help等功能
     6     def inner(*args,**kwargs):
     7         start_time=time.time()
     8         res=func(*args,**kwargs)        
     9         stop_time=time.time()
    10         print('run time is :[%s]' %(stop_time-start_time))
    11         return res                          
    12 
    13     return inner
    14 @timmer
    15 def index():
    16     '''
    17     index function
    18     :return:
    19     '''
    20     time.sleep(3)
    21     print('welcome to index page')
    22     return 123
    23 
    24 @timmer #home=timmer(home) #home=inner
    25 def home(name):
    26     time.sleep(2)
    27     print('welcome %s to home page' %name)
    28     return 456

    为了得到功能函数的返回值, 需要有如下

     8         res=func(*args,**kwargs)        
     9         stop_time=time.time()
    10         print('run time is :[%s]' %(stop_time-start_time))
    11         return res       

    将功能函数的返回值赋给res 然后将res保存下来. 

    有参装饰器

     1 import time
     2 current_status={'user':None,'login_status':False}
     3 def auth(egine='file'):
     4     # egine='file'
     5     def wrapper(func):
     6         def inner(*args,**kwargs):
     7             if current_status['user'] and current_status['login_status']:
     8                 res = func(*args, **kwargs)
     9                 return res
    10 
    11             if egine == 'file':
    12                 u='egon'
    13                 p='123'
    14             elif egine == 'mysql':
    15                 print('mysql auth')
    16                 u = 'egon'
    17                 p = '123'
    18             elif egine == 'ldap':
    19                 print('ldap auth')
    20             else:
    21                 pass
    22             name = input('username>>:').strip()
    23             pwd = input('password>>:').strip()
    24             if name == u and pwd == p:
    25                 print('login successfull')
    26                 current_status['user'] = name
    27                 current_status['login_status'] = True
    28                 res = func(*args, **kwargs)
    29                 return res
    30         return inner
    31     return wrapper
    32 @auth(egine='ldap') #@wrapper #index=wrapper(index) #index=inner
    33 def index():
    34     time.sleep(3)
    35     print('welcome to index page')
    36     return 123

    wrapper 是无参装饰器,现在wrapper内部需要参数egine,所以需要想办法给egine传参,那么我们就用到了闭包函数.wrapper外面再包一个参数,于是有了auth(egine) ,装饰的时候会写为

    @auth(egine) 上面代码写成@auth(egine='file'),只是给了一个默认的初始值.在运行代码时候,见到@auth(egine='file') 先忘了他是装饰器 先执行auth(egine='file')函数 于是得到了return出来的wrapper 于是有了@wrapper 也就是我们的无参装饰器.

    所以有参装饰器的本质 就是无参装饰器的闭包函数.装饰器最多是三层就够了.

    装饰器是有顺序的.修饰下面的代码(如果下面还有多个装饰器).

    有参装饰器有三层  

    1外层  传参给内部的核心功能

    2 中层 作用不改变被装饰函数调用方式

    3 核心层 是内部功能层

    偏函数

    看了一个比较容易理解的例子:

    def add(a,b):

          return a+b;

    add(3,5)

    add(4,7)

    以上两个是我们正常调用,那么如果我们知道一个已知的参数a= 100,我们如何利用偏函数呢?

    import functools import partial as pto

    puls = pto(add,100)

    result = puls(9)

    result的结果就是109。

    在这里偏函数表达的意思就是,在函数add的调用时,我们已经知道了其中的一个参数,我们可以通过这个参数,重新绑定一个函数,就是pto(add,100),然后去调用即可。

    对于有很多可调用对象,并且许多调用都反复使用相同参数的情况,使用偏函数比较合适。

    https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819893624a7edc0e3e3df4d5d852a352b037c93ec000

  • 相关阅读:
    POJ 1328 Radar Installation
    POJ 1700 Crossing River
    POJ 1700 Crossing River
    poj 3253 Fence Repair (贪心,优先队列)
    poj 3253 Fence Repair (贪心,优先队列)
    poj 3069 Saruman's Army(贪心)
    poj 3069 Saruman's Army(贪心)
    Redis 笔记与总结2 String 类型和 Hash 类型
    数据分析方法有哪些_数据分析方法
    数据分析方法有哪些_数据分析方法
  • 原文地址:https://www.cnblogs.com/surehunter/p/7641909.html
Copyright © 2011-2022 走看看