python3-基础6

时间:2023-03-08 23:35:26
python3-基础6

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

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

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

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
 #用生成器实现range功能
# nu = range(1, 10, 2) range(start, stop, [step])
# l1 = list(nu)
# t1 = tuple(nu)
# print(l1)
# print(t1)
###结果
#[1, 3, 5, 7, 9]
#(1, 3, 5, 7, 9)
##########
def range_fun(start, stop, step):
while start < stop :
yield start
start += step
nu = range_fun(1, 10, 2)
# print(nu) #<generator object range_fun at 0x0000000006B522B0>
# print(next(nu)) #1
# print(next(nu)) #3 for i in nu:
print(i) #####结果
1
3
5
7
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可以返回多次值,可以挂起/保存函数的运行状态

 #生成器实现过滤功能
#实现管道符 # python3 tail.py -f access.log | grep 'error' import time def tail(filepath):
with open(filepath, 'r') as f:
f.seek(0, 2)
while True:
line = f.readline()
if line:
yield line
else:
time.sleep(0.2) def grep(pattern,lines): #定义管道函数,参数patteron表示 |
for line in lines:
if pattern in line:
print(line,end='') grep('error',tail('access.log')) #读取文件 access.log , 过滤 error , 只显示 error相关