Albert-w

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

函数嵌套调用: 再调用一个函数的过程中,又调用了其他的函数

函数的嵌套定义:在一个函数的内部,又定义另外一个函数,函数内部定义的变量,在外部不能被调用

名称空间:一种隔离的概念,专门用来存放名字的地方,准确的说是存放名字与变量值绑定关系的地方,一个内存空间与另一个内存空间完全隔离

python中,有哪些名称空间,名称空间和函数没毛关系

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

  全局名称空间:再执行文件时,存放文件级别定义的名字,没有缩进定义的名字,就是全局的名字

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

                      该名字在函数调用时生效,在函数调用结束后失效。也就是函数内定义的名字。

 优先掌握

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

 取值顺序: 局部---》全局---》内置      (参照点为局部)  ,需要注意的是:在全局无法查看局部的,在局部可以查看全局的,

 

1、作用域即范围
       - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效  globals()
    - 局部范围(局部名称空间属于该范围):临时存活,局部有效  locals()
2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,

 

global  在局部位置修改全局参数

nonlocal    只在局部生效,在局部位置修改上层参数

 

优先掌握: 作用域关系,在函数定义时就已经固定,与调用位置无关

      再调用函数时,必须必须回到函数原来定义的位置去找作用域关系。

 

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间

  

闭包函数:闭合起来 , 包裹关系,内部函数,包含对外部作用域的一个引用

  1、定义在函数内部的函数

  2、包含对外部作用域名字的引用,而不是对全局作用域名字的引用,那么该内部函数就称为闭包函数

 1 #闭包函数
 2 x=1
 3 def  f1():
 4     x=11111111111
 5     def f2():     #定义在函数f1内部的函数
 6         print(x)  #调用外部作用域的名字x,此x并不是全局下的x
 7     return f2   #任何得地方都可以调用,打破只限于局部使用
 8 
 9 func=f1()  #func 拿到的是f2内存地址,无论func在哪运行,都以x=1111111111 这个值为准
10 print(func)
11 
12 x=100
13 func()
14 print(func())  #空值
15 #f2()称为闭包函数   

#结论
<function f1.<locals>.f2 at 0x00000000067FFC80>
11111111111
11111111111
None

 

 闭包函数有什么用

 闭包函数应用:延迟计算、惰性计算

  闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

  应用领域:延迟计算(原来我们是传参,现在我们是包起来)

 1 #爬网页小程序
 2 import requests   #导入request模块
 3 
 4 def get(url):    #定义一个get函数,参数为url
 5     return requests.get(url).text    #返回得到网站内容的文本信息
 6 print(get('http://www.baidu.com'))
 7 
 8 #方法二:
 9 def index(url):    #定义页面函数
10     def get():     #定义get函数
11         print(requests.get(url).text)  #输出得到的网站内容文本信息
12     return get     #返回get信息,即返回网址内容文本信息,使得任意位置可以调用baidu_web = index('http://www.baidu.com')  #调用index()函数
13 baidu_web()   #执行函数
14 
15 #反法三
16 
17 def index(url):
18     x = 'xxxx'
19     y = 'yyyy'
20     def wrapper():
21         x
22         y
23         return requests.get(url).text
24     return wrapper
25 baidu_web = index('http://www.baidu.com')
26 baidu = baidu_web.__closure__    #closure 是内部 闭合 函数  , 生成一个元组, 元组内容是 x  y url 的值构成的元素
27 baidu0 = baidu_web.__closure__[0].cell_contents # 查看元组里面的值
28 baidu1 = baidu_web.__closure__[1].cell_contents
29 baidu2 = baidu_web.__closure__[2].cell_contents
30 print(baidu)
31 print(type(baidu))
32 print(baidu0)
33 print(type(baidu0))
34 print(baidu1)
35 print(type(baidu1))        
36 print(baidu2)
37 print(type(baidu2))    
38 
39 ##结果
40 (<cell at 0x0000000008DF0C48: str object at 0x0000000008A5C5D0>, <cell at 0x0000000008DF0E88: str object at 0x0000000006782D88>, <cell at 0x0000000008DF0B88: str object at 0x0000000006782F10>)
41 <class 'tuple'>
42 http://www.baidu.com
43 <class 'str'>
44 xxxx
45 <class 'str'>
46 yyyy
47 <class 'str'>

  

装饰器   :  (闭包函数的一种应用场景)  装饰他人的工具,装饰器目的是为他人添加新功能

开放封闭原则:对扩展是开放的,对修改是封闭的

装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象

装饰器所遵循的原则:

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

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

装饰器的目的:

  在循序1和2原则的前提下,为其他新功能函数添加

 1 #装饰器
 2 #统计函数执行时间,函数执行为源代码,新增统计时间
 3 
 4 #@装饰器名A,必须写在被装饰对象B的正上方,并且是单独一行,把正下方的函数名B当做参数传给@后的函数名A,然后吧返回值在赋值给这个函数B
 5 
 6 import time
 7 
 8 def timmer(func):      #装饰器  定义一个timmer函数,参数为 func
 9     # func=index
10     def wrapper():      #定义wrapper函数
11         start=time.time()   #开始时间
12         func()              #运行函数
13         stop=time.time()    #结束时间
14         print('run time is %s' %(stop-start))  #统计函数执行时间
15     return wrapper      #返回函数内存地址
16 
17 
18 @timmer         # 相当于   index=timmer(index)
19 def index():    # 源代码  定义 index函数  并用  index()方式调用
20     time.sleep(3)
21     print('welcome to index')
22 @timmer         # 相当于  home=timmer(home)
23 def home():     ## 源代码  定义 home函数  并用  home()方式调用
24     time.sleep(2)
25     print('welcome to home page')
26 
27 #index=timmer(index)   相当于上面的  @timmer   有其一即可
28 #home=timmer(home)     相当于上面的  @timmer   有其一即可
29 
30 index()    #调用函数
31 home()
32 
33 ####结论 ####
34 welcome to index
35 run time is 3.000300168991089
36 welcome to home page
37 run time is 2.000199794769287

 上面为无参函数, def index():   def home():      ,如果是有参函数   def home(name):  

就需要传参,则 

def wrapper(*args, **kwargs):    #  以保障可以允许任意场景使用

1 def timmer(func):      #装饰器  定义一个timmer函数,参数为 func
2     # func=index
3     def wrapper(*agrs, **kwargs):      #定义wrapper函数,添加参数,
4         start=time.time()                     #开始时间
5         func(*agrs, **kwargs)              #运行函数
6         stop=time.time()                      #结束时间
7         print('run time is %s' %(stop-start))  #统计函数执行时间
8     return wrapper     #返回函数内存地址

 无参装饰器

 1 #无参装饰器
 2 #原函数为 index() ,现在添加装饰器,满足登录认证,从文件db.txt中验证账号密码,确认后进入页面  from index
 3 
 4 current_user={'user':None}    # 建立记录用户名的字典
 5 def auth(func):    #定义认证函数,带func参数
 6     def wrapper(*args,**kwargs):   #定义wrapper函数,可传任意参数
 7         if current_user['user']:   #判断用户名
 8             return func(*args,**kwargs)   #返回认证函数 func
 9 
10         name=input('name: ').strip()  #输入用户名
11         password=input('password: ').strip() #输入密码
12 
13         with open('db.txt', encoding='utf-8') as f:   #打开数据库字典文件
14             user_dic = eval(f.read())   #eval用来执行f.read()读取函数,并把读取的内网返回给user_dic
15         if name in user_dic and password == user_dic[name]:  #判断账号密码如果都正确
16             res=func(*args,**kwargs)    #执行func函数,并赋值给res
17             current_user['user']=name   #将输入的name传给用户字典
18             return res   #返回res 内存地址
19         else:
20             print('user or password error')   #用户名和密码有错的话,提示有错
21     return wrapper   #返回wrapper函数内存地址
22 
23 @auth #index=auth(index) index=wrapper   #应用装饰器  
24 def index():    #定义index函数
25     print('from index')
26 index()     #执行index函数

 

 1 #有参装饰器版本
 2 #可以通过多个认证方式认证   file  MySQL 等等
 3 
 4 current_user={'user':None}
 5 def auth(auth_type='file'):
 6     def deco(func):
 7         def wrapper(*args, **kwargs):
 8             if auth_type == 'file':
 9                 if current_user['user']:
10                     return func(*args, **kwargs)
11                 name = input('name: ').strip()
12                 password = input('password: ').strip()
13 
14                 with open('db.txt', encoding='utf-8') as f:
15                     user_dic = eval(f.read())
16                 if name in user_dic and password == user_dic[name]:
17                     res = func(*args, **kwargs)
18                     current_user['user'] = name
19                     return res
20                 else:
21                     print('user or password error')
22             elif auth_type == 'mysql':
23                 print('mysql')
24 
25             elif auth_type == 'ldap':
26                 print('ldap')
27             else:
28                 print('not valid auth_type')
29         return wrapper
30     return deco
31 @auth(auth_type='mysql') #@deco  #index=deco(index)
32 def index():
33     print('from index')
34 @auth(auth_type='file')
35 def home(name):
36     print('welcome %s' %name)
37 index() #wrapper()
38 home('egon')

 

 装饰器补充内容:  ‘’‘   ’‘’  三引号

 查看函数备注可以通过 help 查看 :   print(help(func))

 1 #未加wraps 时
 2 def wrapper(f):   #定义修饰函数
 3     def wrapper_function(*args, **kwargs):
 4         """这个是修饰函数"""
 5         return f(*args, **kwargs)
 6     return wrapper_function
 7     
 8 @wrapper
 9 def wrapped():   #定义被修饰函数
10     """这个是被修饰的函数"""
11     print('wrapped')
12 
13 print(wrapped.__doc__)  # 输出`这个是修饰函数`
14 print(wrapped.__name__)  # 输出`wrapper_function`
15 
16 ####结果
17 这个是修饰函数
18 wrapper_function

 

 1 # 添加 wraps 后
 2 from functools import wraps
 3 
 4 def wrapper(f):
 5     @wraps(f)   #在最内层的函数上面添加
 6     def wrapper_function(*args, **kwargs):
 7         """这个是修饰函数"""
 8         return f(*args, **kwargs)
 9     return wrapper_function
10     
11 @wrapper
12 def wrapped():
13     """这个是被修饰的函数
14     """
15     print('wrapped')
16 
17 print(wrapped.__doc__)  # 输出`这个是被修饰的函数`
18 print(wrapped.__name__)  # 输出`wrapped`
19 
20 ####结果 
21 这个是被修饰的函数
22 wrapped

 

 一个函数头顶上有多个装饰器吗?   可以。

 1 ##多个装饰器
 2 import time
 3 from functools import wraps
 4 
 5 current_user={'user':None}
 6 
 7 def timmer(func):
 8     @wraps(func)
 9     def wrapper(*args,**kwargs):
10         start=time.time()
11         res=func(*args,**kwargs)
12         stop=time.time()
13         print('run time is %s' %(stop-start))
14         return res
15     return wrapper
16 def auth(auth_type='file'):
17     def deco(func):
18         def wrapper(*args, **kwargs):
19             if auth_type == 'file':
20                 if current_user['user']:
21                     return func(*args, **kwargs)
22                 name = input('name: ').strip()
23                 password = input('password: ').strip()
24 
25                 with open('db.txt', encoding='utf-8') as f:
26                     user_dic = eval(f.read())
27                 if name in user_dic and password == user_dic[name]:
28                     res = func(*args, **kwargs)
29                     current_user['user'] = name
30                     return res
31                 else:
32                     print('user or password error')
33             elif auth_type == 'mysql':
34                 print('mysql')
35 
36             elif auth_type == 'ldap':
37                 print('ldap')
38             else:
39                 print('not valid auth_type')
40         return wrapper
41     return deco
42 
43 # 多个装饰器,那个在前先生效那个
44 # 直接修饰正下方的函数
45 @auth() # @deco #index=deco(index) #wrapper
46 @timmer #index=timmer(wrapper)
47 # @auth() # @deco #index=deco(index) #wrapper
48 def index():
49     '''这是index函数'''
50     time.sleep(3)
51     print('welcome to index')
52     return 123
53 
54 # print(index.__doc__)
55 # print(help(index))
56 
57 index()
58 
59 
60 #####结果
61 name: lalala
62 password: 123
63 welcome to index
64 run time is 3.0002999305725098
 

 ####练习题####

一:编写函数,(函数执行的时间是随机的)
二:编写装饰器,为函数加上统计时间的功能
三:编写装饰器,为函数加上认证的功能

四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式

五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录

六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果

七:为题目五编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中

扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中

八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作

九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
注意:时间格式的获取
import time
time.strftime('%Y-%m-%d %X')

######################################

迭代器 (Iterator):是一个重复的过程,每一次重复,都是基于上一次的结果而来

取值:就是一个循环的过程,

依赖于索引的方式迭代取值:字符串 列表 元组

如列表取值:   

l=['a','b','c','d']
count=0
while count < len(l):
  print(l[count])
  count+=1

不依赖于索引的方式取值:字典取值(无索引,非序列类型)  该方式就是迭代器

可迭代对象 iterable:  凡是对象下有  __iter__ 方法: 对象.__iter__ , 该对象就是可迭代对象

# s='hello'
# l=['a','b','c','d']
# t=('a','b','c','d')
# dic={'name':'egon','sex':'m',"age":18}
# set1={1,2,3}
# f=open('db.txt')

# s.__iter__()
# l.__iter__()
# t.__iter__()
# dic.__iter__()
# set1.__iter__()
# f.__iter__()

 

执行iter的到的是迭代器对象,是一个内存地址

迭代器对象有next方法

使用next方法可以去到字典的key键值

 

迭代器对象:

#1 有__iter__ , 执行得到仍然是迭代本身,执行 iter之后才会有 next
#2 有__next__  , 一次取一个值

 

# l=['a','b','c','d']    列表
# i=iter(l)              迭代器

迭代器对象的优点:

#1:提供了一种统一的,可以不依赖于索引的迭代方式   

#2:迭代器本身,比起其他数据类型更省内存  (同一时间只有一个值)

 

迭代器对象的缺点:
#1:一次性,只能逐步往后取值,不能回退,不如索引取值灵活
#2:无法预知什么时候取值结束,即无法预知长度

 文件是迭代器对象,既可以__iter__ , 又可以 __next__

 1 #迭代器
 2 l=['a','b','c','d']     #列表
 3 dic={'name':'lalala','sex':'m',"age":28}  #字典
 4 iter_l=iter(l)      #取值,取的是元素的内存地址
 5 iter_dic=iter(dic)  #取值,取得是字典key键的内存地址
 6 while True:   #建立循环   取值
 7     try:       #消除异常
 8         # print(next(iter_l))   #挨个取列表的值
 9         # print(next(iter_dic))  #取字典的key值
10         k=next(iter_dic)  #   
11         print(k,dic[k])   #取字典的key值和value值
12     except StopIteration:  #如果出现此异常,则终止循环
13         break
14 
15 #### 结果 
16 name lalala
17 sex m
18 age 28
 

 for循环原理

 1 # for循环原理   实际上就是迭代器  for循环比while循坏更便捷
 2 # 先调用__iter__() 方法
 3 # 凡是能被for循环执行循环的,都是可迭代器对象
 4 l = ['a', 'b' , 'c', 'd',]
 5 for item in l : #iter_l = l.__iter__()  #从l中逐个取值,赋值给item, 这里的item可以任意定义
 6     print(item)
 7 
 8 with open('a.txt') as f:   用with方式打开文件
 9     # for line in f: #i=f.__iter__()   #for循环文件内容,调用__iter__(),然后执行next,将结果返回给 line      line = f.__iter__()
10     #     print(line)
11     print(f is f.__iter__())   #常见的类型里面,只有文件是迭代器对象,其余都是可迭代器对象,迭代器对象既要有 __iter__(),又要有 next()
 

 生成器   (generator) : 只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码

生成器就是迭代器,满足迭代器所有的特点,在next()函数才会往下执行

 1 #生成器
 2 #生成器就是迭代器,因此可以这么取值
 3   # res=next(g)
 4   # print(res)
 5 #yield的功能:
 6 # 1 把函数的结果做生迭代器(以一种优雅的方式封装好__iter__,__next__)
 7 # 2 函数暂停与再继续运行的状态是由yield保存
 8 
 9 def func():
10     print('first')    #
11     yield 11111111    #碰到 yield赞停,将后面的返回值返回
12     print('second')    #再次执行的时候,从yield之后开始执行
13     yield 2222222
14     print('third')
15     yield 33333333
16     print('fourth')
17 
18 g=func()   #执行该函数
19 # print(g)
20 # next(g)
21 
22 # from collections import Iterator
23 # print(isinstance(g,Iterator))
24 
25 # print(next(g))    #可以通过next取值
26 # print('======>')
27 # print(next(g))
28 # print('======>')
29 # print(next(g))
30 # print('======>')
31 # print(next(g))
32 
33 for i in g: #i=iter(g)   #可以通过for循环取值
34     print(i)
35 
36 
37 ####结果
38 first
39 11111111
40 second
41 2222222
42 third
43 33333333
44 fourth
 
 1 #用生成器实现range功能
 2 # nu = range(1, 10, 2)     range(start, stop, [step])
 3 # l1 = list(nu)
 4 # t1 = tuple(nu)
 5 # print(l1)
 6 # print(t1)
 7 ###结果
 8 #[1, 3, 5, 7, 9]
 9 #(1, 3, 5, 7, 9)
10 ##########
11 def range_fun(start, stop, step):
12     while start < stop :
13         yield start
14         start += step
15 nu = range_fun(1, 10, 2)
16 # print(nu)   #<generator object range_fun at 0x0000000006B522B0>
17 # print(next(nu))    #1
18 # print(next(nu))    #3
19 
20 for i in nu:
21     print(i)
22 
23 #####结果
24 1
25 3
26 5
27 7
28 9

 

 实现一个有无穷值的类型:

 1 #实现无穷值函数,该函数的值同一时间在内存中只存在一个值,所以不会撑爆内存
 2 def func(n):
 3     print('=== start ===')
 4     while True:      #死循环
 5         yield n      #生成器,遇到yield 暂停,返回n,内存中同一时间只有这一个值
 6         n+=1
 7 
 8 g=func(0)  #调用函数
 9 
10 # print(next(g))
11 # print(next(g))
12 # print(next(g))
13 for i in g:   #取值
14     print(i)

 

 1 #生成器 实现一个无穷的序列
 2 def my_range(start,stop):
 3     while True:
 4         if start == stop:
 5             raise StopIteration  #满足条件时,抛出异常。 raise 是自己抛出异常 StopIteration
 6         yield start #2   返回 start的值
 7         start+=1 #3  返回值+1
 8 
 9 g=my_range(1,3)
10 #
11 # print(next(g))
12 # print(next(g))
13 # print(next(g))
14 
15 for i in my_range(5,10):  #for循环遇到异常自动停止循环
16     print(i)
17 
18 ###结果
19 5
20 6
21 7
22 8
23 

  

#yield与return的比较?
#相同:都有返回值的功能
#不同:return只能返回一次值,而yield可以返回多次值,可以挂起/保存函数的运行状态

 1 #生成器实现过滤功能  
 2 #实现管道符  # python3 tail.py -f access.log | grep 'error'
 3 
 4 import time
 5 
 6 def tail(filepath):
 7     with open(filepath, 'r') as f:
 8         f.seek(0, 2)
 9         while True:
10             line = f.readline()
11             if line:
12                 yield line
13             else:
14                 time.sleep(0.2)
15 
16 
17 def grep(pattern,lines):   #定义管道函数,参数patteron表示 |
18     for line in lines: 
19         if pattern in line:
20             print(line,end='')
21 
22 grep('error',tail('access.log'))   #读取文件 access.log , 过滤 error , 只显示 error相关

 

 

 

 



 

posted on 2019-04-15 16:00  Albert-w  阅读(209)  评论(0编辑  收藏  举报