一、知识储备
一 *args,**kwargs
def index(name,age):
print(name,age)
def wrapper(*args,**kwargs):
#args=(1,2,2,3,4,4,5),kwargs={'x':1,'y':2}
index(*args,**kwargs) #index(1,2,2,3,4,4,5,y=2,x=1)
wrapper(1,2,2,3,4,4,5,x=1,y=2) #不可以这样调用函数,不然调用index函数的时候实参是这样的index(1,2,2,3,4,4,5,y=2,x=1)
wrapper('egon',age=18) #要这样调用才可以
二 函数对象:函数可被当做数据传递
- 函数可以当做参数传给另外一个函数
- 一个函数的返回值也可以是一个函数(打破函数的层级限制)
def f1():
def f2():
print('f2')
return f2
f=f1()
f()
三 名称空间与作用域
名称空间
- 分三种
内置名称空间:python解释器启动则生效
全局名称空间:执行python文件时生效
局部名称空间:调用函数时,临时生效,函调用结束则失效
- 加载顺序:先内置,再全局,最后有可能产生局部
- 查找名字的顺序:先局部,再全局,最后内置
作用:
- 分两种
全局作用域:全局存活,全局有效
局部作用域:临时存活,局部有效
强调:作用关系在函数定义阶段就已经固定,与调用位置无关
二、闭包函数
#闭包函数定义:定义在函数内部的函数,特点是:包含对外部作用域而不是对全局作用域名字的引用,该函数就称之为闭包函数
x=1
def outter():
x=2
def inner():
print(x)
return inner
f=outter()
def f1():
x=1000000000
f()
f1()
运行结果为2,inner函数就是闭包函数,引用的是x=2的作用域,不能引用自己层级的,而是引用自己外层的
#函数体内内部需要一个变量,有两种解决方案
#一种是:以参数的形式传入
from urllib.request import urlopen
def get(url):
return urlopen(url).read()
print(get('http://www.baidu.com'))
print(get('http://www.baidu.com'))
print(get('http://www.baidu.com'))
#另外一种:包起来
from urllib.request import urlopen
def get(url):
def inner():
return urlopen(url).read() #inner函数的返回值是urlopen(url).read(),是爬网页的代码
return inner
baidu=get('http://www.baidu.com') #baidu等于get函数的返回值innner
print(baidu()) #打印的是调用baidu这个函数,baidu=inner,所以打印的结果是inner函数的返回值urlopen(url).read()
print(baidu())
print(baidu())
这样就不用每次都传参数进行调用函数,两种方式执行的结果是一样的,都是爬百度这个网页,也就是百度的源代码
三、简单装饰器
1、为什么要用装饰器:开放封闭原则,对扩展是开放的,对修改是封闭的
2、什么是装饰器
- 用来装饰它人,装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象
- 遵循的原则:1、不修改被装饰对象的源代码 2、不修改被装饰对象的调用方式
- 目标是:在遵循原则1和2的前提,为被装饰器对象添加上新功能
变量是引用,函数是调用
import time
#
def timmer(func):
# func=index #最原始的index函数的内存地址
def inner():
start_time=time.time()
func()
stop_time=time.time()
print('run time is :[%s]' %(stop_time-start_time))
return inner
@timmer #index=timmer(index) #index等于timemer函数的返回值inner,@timmer表示将其正下方的index函数当做timmer函数的实参传给timmer,并赋值给index函数
def index():
time.sleep(3)
print('welcome to index page')
index() 此处index=inner
四、无参装饰器修订
import time
from functools import wraps
def timmer(func):
@wraps(func)
def inner(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs) #func=index,res=index函数的返回值
stop_time=time.time()
print('run time is :[%s]' %(stop_time-start_time))
return res
return inner
@timmer #timmer函数就是一个装饰器,index=timmer(index)
def index():
'''
index function #定义描述信息
:return:
'''
time.sleep(3)
print('welcome to index page')
return 123
@timmer #home=timmer(home) #home=inner
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
return 456
res=index() # res=inner()
print(res)
res=home('egon') #inner('egon')
print(res)
print(index.__doc__) #打印描述信息
print(help(index))
五、无参装饰器之auth
import time
current_status={'user':None,'login_status':False}
def auth(func):
def inner(*args,**kwargs):
if current_status['user'] and current_status['login_status']:
res = func(*args, **kwargs)
return res #函数遇到return就终止
name=input('username>>:').strip()
pwd=input('password>>:').strip()
if name == 'egon' and pwd == '123':
print('login successfull')
current_status['user']=name
current_status['login_status']=True
res=func(*args,**kwargs)
return res
return inner
@auth #index=auth(index)
def index():
time.sleep(3)
print('welcome to index page')
return 123
@auth
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
return 456
index()
home('egon')
六、有参装饰器
import time
current_status={'user':None,'login_status':False}
def auth(egine='file'):
# egine='file'
def wrapper(func):
def inner(*args,**kwargs):
if current_status['user'] and current_status['login_status']:
res = func(*args, **kwargs)
return res
if egine == 'file':
u='egon'
p='123'
elif egine == 'mysql':
print('mysql auth')
u = 'egon'
p = '123'
elif egine == 'ldap':
print('ldap auth')
else:
pass
name = input('username>>:').strip()
pwd = input('password>>:').strip()
if name == u and pwd == p:
print('login successfull')
current_status['user'] = name
current_status['login_status'] = True
res = func(*args, **kwargs)
return res
return inner
return wrapper
@auth(egine='ldap') #@wrapper #index=wrapper(index) #index=inner
解释:先执行auth(egine='ldap'),调用这个装饰器函数,这个函数的返回值是wrapper,接着运行@wrapper,index=index=wrapper(index),index=inner
def index():
time.sleep(3)
print('welcome to index page')
return 123
index() #inner()
七、加多个参装饰器
import time
current_status={'user':None,'login_status':False}
def timmer(func):
def inner(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('run time is :[%s]' %(stop_time-start_time))
return res
return inner
def auth(egine='file'):
# egine='file'
def wrapper(func):
def inner(*args,**kwargs):
if current_status['user'] and current_status['login_status']:
res = func(*args, **kwargs)
return res
if egine == 'file':
u='egon'
p='123'
elif egine == 'mysql':
u = 'egon'
p = '123'
elif egine == 'ldap':
u = 'egon'
p = '123'
else:
pass
name = input('username>>:').strip()
pwd = input('password>>:').strip()
if name == u and pwd == p:
print('login successfull')
current_status['user'] = name
current_status['login_status'] = True
res = func(*args, **kwargs)
return res
return inner
return wrapper
#@timmer #放在此处表示统计正下方所有函数的执行时间,包括auth这个装饰器函数的时间
@auth(egine='ldap') #@wrapper #index=wrapper(timmer_inner)
@timmer #index=timmer(index) #装饰器是有顺序的,放在index函数正上方表示统计index函数的执行时间,
def index():
time.sleep(3)
print('welcome to index page')
return 123
index() #inn
此示例中增加timmer和auth两个装饰器
八、迭代器
1 什么叫迭代:迭代是一个重复过程,每次重复都是基于上一次的结果来的
2 为什么要用迭代器?
l=['a','b','c']
n=0
while n < len(l):
print(l[n])
n+=1
- 对于序列类型:字符串,列表,元组,可以使用基于索引的迭代取值方式,而对于没有索引的类型,如字典,
集合、文件,这种方式不再适用,于是我们必须找出一种能不依赖于索引的取值方式,这就是迭代器
3 可迭代的对象:只要对象内置有__iter__方法,obj.__iter__
4 迭代器对象:对象既内置有__iter__方法,又内置有__next__,如文件对象
注意:可迭代对象不一定是迭代器对象,而迭代器对象一定是可迭代的对象
'''
#可迭代的对象
# 'hello'.__iter__ 字符串
# [1,2].__iter__列表
# (1,2).__iter__ 元组
# {'a':1}.__iter__ 字典
# {1,2,3}.__iter__ 集合
#既是可迭代对象,又是迭代器对象
# open('a.txt','w').__iter__ 文件类型
# open('a.txt','w').__next__
#迭代器对象的用处
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #获得一个迭代器对象iter_dic
print(iter_dic.__next__())
print(next(iter_dic)) #iter_dic.__next__()等于next(iter_dic)
print(next(iter_dic))
执行结果是
a
b
c
with open('a.txt','r') as f:
print(next(f))
print(next(f))
对于文件类型也可以用迭代器,把文件里面的内容取出来。这样就获取到了一个不依赖于索引的取值方式
l=[1,2,3,4,5]
iter_l=l.__iter__() #定义一个迭代器对象
print(iter_l)
print(next(iter_l)) #就可以基于迭代器的next方法进行取值而不依赖于索引
print(next(iter_l))
print(next(iter_l))
执行结果:
<list_iterator object at 0x000002A3C3FC0A90>
1
2
3
#基于迭代器对象的迭代取值(不依赖索引)
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #制作成迭代器
while True:
try:
i=next((iter_dic))#然后一个一个的取值
print(i)
except StopIteration:
break
这样就不会因为一直next而报错
dic={'a':1,'b':2,'c':3}
for i in dic: #iter_dic=dic.__iter__()
print(i)
for循环就是基于迭代器的原理实现的
迭代器的优缺点:
- 优点:
提供了一种统一的迭代取值方式,该方式不再依赖于索引
更节省内存,因为同一时间只有next才取一个值
- 缺点:
无法统计长度
一次性的,只能往后走,不能往前退,无法获取指定位置的值
from collections import Iterable,Iterator
print(isinstance('hello',Iterable)) #判断hello这个字符串是否是可迭代的对象
print(isinstance('hello',Iterator)) #判断hello这个字符串是否是迭代器对象
执行结果:
True
False
九、生成器
定义:只要函数内部出现yield关键字,那么再调用该函数,将不会立即执行函数体代码,会得到一个结果,该结果就是生成器对象
def func():
print('===>first')
yield 1
print('===>second')
yield 2
print('====>third')
yield 3
g=func() #调用这个函数,因为代码里面有yield,当调用时会生成生成器,也就是迭代器,g此时就是一个迭代器
print(g)
不会执行函数体代码,会得到如下
<generator object func at 0x0000027AE16EEE60>
#生成器本质就是迭代器
print(next(g)) #执行函数体代码并打印函数的返回值,碰到yield就暂停函数
print(next(g)) #从上次暂停的位置继续执行函数代码。直到遇到yield,并打印函数的返回值
print(next(g))
执行结果
===>first
1
===>second
2
====>third
3
yield的功能:
- 为我们提供了一种自定义迭代器的方式
- 对比return,可以返回多次值,挂起函数的运行状态,return只能返回一个值,遇到return函数就停止并退出
def func():
print('===>first')
yield 1
print('===>second')
yield 2
print('====>third')
yield 3
g=func()
for i in g:
print(i)
执行结果:
===>first
1
===>second
2
====>third
3
def func():
print('===>first')
yield 1
print('===>second')
yield 2
print('====>third')
yield 3
print(next(func()))
print(next(func()))
print(next(func()))
执行结果为
===>first
1
===>first
1
===>first
1
因为每次都生成一个新的生成器,也就是迭代器,所以每次next的时候都是一样的
#自定义功能,可以生成无穷多个值,因为同一时间在内存中只有一个值
def my_range(start,stop,step=1):
while start < stop:
yield start
start+=step
g=my_range(1,5,2) #调用这个函数会生成一个生成器g
print(next(g))
print(next(g))
执行结果为:
1
3
def my_range(start,stop,step=1):
while start < stop:
yield start
start+=step
for i in my_range(1,1000000000000000000000000000000000000000000,step=2):
print(i)
执行结果会生成无穷多个值,并且不会卡,因为每次只生成一个值在内存中
# tail -f access.log | grep '404'实现此功能
import time
def tail(filepath):
with open(filepath,'rb') as f:
f.seek(0,2) #移动到文件的最后一行
while True:
line=f.readline()
if line:
yield line
else:
time.sleep(0.2)
def grep(pattern,lines):
for line in lines:
line=line.decode('utf-8')
if pattern in line:
yield line
g=grep('404',tail('access.log')) #会生成一个生成器g
for line in g: #for循环会对生成器一个一个的取值,相当于next的作用
print(line)
yield可以把一个函数的执行结果制作成一个生成器,然后通过for循环一个一个打印结果
#yield的表达式形式的应用
def eater(name):
food_list=[]
print('%s 开动啦' %name)
while True:
food=yield food_list #food=‘骨头’
print('%s 开始吃 %s' %(name,food))
food_list.append(food)
g=eater('alex')
g.send(None) #next(g)=g.send(None),send有两个作用,一个是给yield传值,一个是next的作用,并且是先传值给yield,再从上次暂停的位置往下走,注意第一次要初始化函数,必须传值为None
print(g.send('骨头')) #执行函数体代码,直到遇到yield就暂停函数,并打印函数的返回值food_list,函数的返回值是yield后面的值,跟return一样,但return只能返回一个值
print(g.send('shi')) #从上次暂停的位置往后走
yield的表达式形式可以从函数外部给函数传多个值
def f1():
while True:
x=yield
print(x)
g=f1()
next(g)
g.send(1)
g.send(1)
g.close() #关闭
十、面向过程编程
强调:面向过程编程绝对不是用函数编程那么简单
面向过程的编程思想:核心是过程二字,过程即解决问题的步骤,即先干什么再干什么
基于该思想去编写程序就好比在设计一条流水线,是一种机械式的编程思想
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差
#实现在a目录中递归寻找文件中含有你好的文件,列出文件的绝对路径
import os
def init(func): #定义装饰器函数
def inner(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return inner
def search(filepath,target): #找到一个文件路径就往下个阶段传一次
g = os.walk(filepath)
for dirname, _, files in g:
for file in files:
abs_file_path = r'%s\%s' % (dirname, file)
target.send(abs_file_path)
@init #opener=init(opener)
def opener(target):
while True:
abs_file_path=yield
with open(abs_file_path,'rb') as f:
target.send((f,abs_file_path)) #send可以传多个值给yield,但要以列表或元组的形式括起来
@init
def cat(target):
while True:
f,abs_file_path=yield
for line in f: #遍历文件的每一行
res=target.send((line,abs_file_path)) #target=grep(),生成一个生成器target,res等于grep函数的返回值,也就是yield后面的值tag
if res: #如果res是真就退出循环,表示文件中有你好了,就不把文件的每一行都读出来了,就证明这个文件中有你好了
break
@init
def grep(pattern,target):
tag=False #保证res的初始值是False
pattern = pattern.encode('utf-8') #encode是编码,decode是解码,因为line是以二进制格式打开的,而pattern是个”你好”,要将其编码为utf-8才可以,也就是bytes形式才可以进行匹配
while True:
line,abs_file_path=yield tag
tag=False #保证每次进行配置时res都是False
if pattern in line:
target.send(abs_file_path)
tag=True
@init
def printer():
while True:
abs_file_path=yield
print(abs_file_path)
search(r'D:\张大志迈远\pythontest\day4\day4\a',opener(cat(grep('你好',printer()))))
十一、三元表达式
常规的if和else模式
name=input('>>: ')
if name == 'alex':
print('SB')
else:
print('NB')
三元表达式,中间是条件判断,条件成立写到左边,条件不成立写到右边,就可以把if和else写到一行去
name = input('>>: ')
print('SB' if name == 'alex' else 'NB')
比较大小的操作就可以这样写了
def my_max(x,y):
return x if x > y else y
print(my_max(2,3))
十二、列表解析与生成器表达式
egg_list=[]
for i in range(10):
if i >= 3:
res='egg%s' %i
egg_list.append(res)
print(egg_list)
写成如下格式和上面的结果是一样的
l=['egg%s' %i for i in range(10) if i >= 3] #列表解析,可以使编程变的简单
print(l)
执行结果都是
['egg3', 'egg4', 'egg5', 'egg6', 'egg7', 'egg8', 'egg9']
#生成器表达式
g=('egg%s' %i for i in range(10) if i >= 3) #g就是一个生成器
print(g)
print(next(g))
print(next(g))
执行结果:
<generator object <genexpr> at 0x0000021C1E86DE60>
egg3
egg4
练习:将列表中的名字变成大写,除了以sb结尾的
names=['egon','alex_sb','wupeiqi','yuanhao']
names=[name.upper() for name in names if not name.endswith('sb')]
print(names)
十三、序列化
我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化
user={'name':'egon','pwd':'123'} #user是一个字典
with open('db.txt','w',encoding='utf-8') as f:
f.write(str(user)) #写到文件中的时候不能以字典的形式,不然会报错,要改成字符串的形式写到文件中
with open('db.txt','r',encoding='utf-8') as f:
data=f.read()
print(type(data)) #发现从文件中读的时候也是字符串,这样就不是原来的字典形式了,序列化就是可以把字典保存成一个中间状态,然后读的时候又能从中间状态返回到字典
import json
user={'name':'egon','pwd':'123','age':18}
with open('db.json','w',encoding='utf-8') as f:
f.write(json.dumps(user)) #把字典以json能够识别的格式写到文件里
写到文件中是如下格式
{"name": "egon", "pwd": "123", "age": 18} #发现单引号都变成双引号了,变成json格式了,注意json格式不能识别单引号
with open('db.json','r',encoding='utf-8') as f:
data=f.read()
dic=json.loads(data) #通过json反序列化转化回原来的格式与json.dumps相对
print(dic)
print(dic['age']) #转化回字典格式后就可以取值了
打印结果:
{'name': 'egon', 'pwd': '123', 'age': 18}
18
可以简写成以下方式
import json
user={'name':'egon','pwd':'123','age':18} #定义一个字典
json.dump(user,open('db1.json','w',encoding='utf-8')) #将字典中的内容以json格式写到文件中
dic=json.load(open('db1.json','r',encoding='utf-8')) #反序列化文件中的内容转化回字典格式
print(dic,type(dic),dic['name']) #这样就可以在字典中取值了
网友评论