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

原文地址:https://www.cnblogs.com/surehunter/p/7641909.html