一.今日内容概 1. 函数本质就是一个内存地址. 用起来和变量一模一样
2. 闭包
内层函数对外层变量的使用
1. 保护变量
2. 让一个变量常驻内存
3. 迭代器
可迭代对象: __iter__
迭代器: __iter__ __next__
from collections import Iterable, Iterator
isinstance(对象, Iterable)
isinstance(对象, Iterator)
模拟for循环:
lst = []
it = lst.__iter__()
while 1:
try:
el = it.__next__()
except StopIteration:
break
list() => for => __iter__() __next__()
4. 生成器
本质就是迭代器
1. 节省内存
2. 惰性机制
3. 只能向前
生成器函数. 包含yield
yield: 把一个函数变成生成器函数. 也可以返回结果但是函数不会终止. 分段执行一个函数
return: 结束函数的调用. 返回结果
生成器保存的是代码
send() 给上一个yield位置传值
5. 各种推导式
列表推导式 [结果 for循环 if判断]
字典推导式
集合推导式
6. 生成器表达式
(结果 for循环 if条件)
面试题(挑战一下)
7. 内置函数
exec
eval
compile
可以动态执行字符串类型的代码
匿名函数: lambda 参数: 返回值
sorted() 排序
filter() 筛选
map() 映射
callable() 判断是否可以被调用 ()
8. 装饰器
可以在不改变原来的代码的基础上给函数添加新的功能
通用装饰器
def wrapper(fn):
def inner(*args, **kwargs):
'''之前'''
ret = fn(*args, **kwargs)
'''之后'''
return ret
return inner
@wrapper
def func():
pass
func()
9. 递归
函数自己调用自己
官方网站上给的递归最大深度是1000
你测试也就是998, 997
大概的预习方向:
1. 二分法
2. 面试题
3. 模块 re, json, pickle, time, random, collections, queue
二 函数名
(一)函数名:本质就是一个变量名
1.play()中()表示是不是可以被调用,callable.
Play本质是一个内存地址
(二)函数名的作用
1.函数赋值
2.函数可以作为参数进行传递
fn可以接受任意函数,收到的函数什么都没干,直接执行.
如果在fn()的前面和后面加入一些代码,在不改变原有模式下,加入一些必要逻辑.
3.函数名可以作为返回值,返回
a和inner是平级的,都是outer()内的局部变量.a和inner都可以返回.inner指向的也是内存地址.ret
可以实现在函数外边拿到了内部的函数,实现了对内部函数的调用.
def outer(): def inner(): print("我是内置函数") print("我是外置函数") a=123 return a,inner() ret=outer()#我是外置函数 我是内置函数 print(ret)#(123, None)#因为inner()没有return,所以返回值是none
4.函数名可以作为集合类的元素
例:这两种写法有什么区别?
def f1(): print("今天吃了1个包子") def f2(): print("今天吃了2个包子") def f3(): print("今天吃了3个包子") def f4(): print("今天吃了4个包子") lst1=[f1(),f2(),f3(),f4()] '''今天吃了1个包子 今天吃了2个包子 今天吃了3个包子 今天吃了4个包子''' print(lst1)#因为没有return,所以全部是none[None, None, None, None] lst2=[f1,f2,f3,f4] print(lst2)#[, , , ]
三 闭包
(一)含义:在内部函数中访问外层函数的变量
结果就是”哇哈哈哈”,这就是一个闭包函数.
1.好处:可以保护你的变量不受侵害.原因是这是一个局部变量,不能修改.
全局变量也是不安全,只需要一个global就可以改变
a="白蛇" def f1(): global a a="胡辣汤" print(a)#胡辣汤 def f2(): print(a)#胡辣汤 f1() f2()
如何保护这个a,如下列代码
def f1(): a = "白蛇"#局部变量 def inner(): print(a) return inner() ret=f1()#白蛇
这个方式是无法改变a的.虽然a是局部变量.
2.可以让一个变量常驻内存.让内部变量常驻内存.
def outer(): a=10 def inner(): return a return inner ret=outer()#ret就是inner r=ret()#调用inner()函数 print(r)#10
这样就把ret()在任意时段执行.
但是这个这个a是不能回收的,Python中为了保证inner()正常运行,a必须存在.不能倍Python回收.所有导致变量a常驻内存.
存在的问题:Python有自动垃圾回收,如下面a,b就会被回收.
3.闭包的应用:爬取一个网站.
网络请求:www.baidu.com
服务器接收后返回,然后经过浏览器处理后,呈现的现状页面.
右键:查看源代码
low版的爬虫:
首先,导入可以发送请求的一个包:
拿到网页上的源代码:
from urllib.request import urlopen content=urlopen("https://www.renrenche.com/bj/?plog_id=b190c6142f4a8a8867f0ea8c278b2044").read().decode("utf-8") print(content)#打印原代码
后面就是爬取数据.
比如每次想拿这个网站的内容,都需要上面的内容,需要反复请求.
具体需求:1.不能被随意改动,2.内容每次都去网络请求,很慢.
def outer(): from urllib.request import urlopen content=urlopen("https://www.renrenche.com/bj/?plog_id=b190c6142f4a8a8867f0ea8c278b2044").read().decode("utf-8") def inner(): return content return inner print("开始爬取") o=outer()#o就是inner,这个爬取效率很低., print("爬取完毕") print("第一次爬取") print(o())#此后的爬取就非常容易了,因为该网址变成了一个常驻内存的变量了. print("第二次爬取") print(o())
四 迭代器
(一)迭代和for的关系
1.可以for循环的:str\list\tuple\dict\set\range\open
2.不能循环的:int\bool
3.dir()的作用,可以查看某个数据类型可以执行那些操作.
4.可迭代的里面有__iter__;不可迭代的没有.
5.通过__iter__可以拿到内存地址.
6.iterator迭代器.
lst=["包子","粥","馒头","咸菜"] it=lst.__iter__() print(it)#print(dir(it))#__iter__() for i in it: print(i)
迭代器本身也是可以被迭代的.
7.迭代器的核心功能__next__
超过了就会报错,停止迭代.
8.迭代器:老师拿着名单,对人员点名,迭代器是就是名单.他只能点第一个比如周杰伦,之后就是下一个,然后下一个.迭代器不知道后面是不是排队,只能向后面迭代,不能向前迭代.当看到stopinteraction,就停止迭代.
9.特点:不管数据类型->可以跨过数据类型的屏障,完成所有数据类型的迭代工作
所有for可以迭代的数据类型很多.也可迭代文件.可以对便利的东西进行迭代.
10.只要有__iter__()和__next__()就是迭代器.与可迭代对象不同,和迭代器没有关系,只要有__iter__()就是可迭代对象.
所以可迭代对象包括迭代器.
迭代器一定是可迭代的对象,但可迭代对象不能一定是迭代器.
11.for到底做了什么?
(1)获取迭代器
(2)获取元素
(3)处理异常.
12.如何模拟for循环(重点)
try:
except stopiteration
你去尝试,如果出事了,我兜着.但只兜着stopiteration
lst=["包子","粥","馒头","咸菜"] it=lst.__iter__() while 1: try: el=it.__iter__() print(el) except StopIteration: break
五 生成器
1.生成器的特点:
(1)省内存(生成器)
(2)惰性机制:比如乡村大夫不喊下一个,下一个就不会进来,只要迭代器不执行__next__就不执行.
(3)只能下一个,不能反复.
2.迭代器拿完元素后,迭代器就失败了.需要重新拿迭代器.
lst=["今天","明天","昨天"] it=lst.__iter__() print(it.__next__()) print(it.__next__()) print(it.__next__()) it=lst.__iter__()#重新拿迭代器才能奏效 print(it.__next__()) print(it.__next__())
3.未来的使用中,不这么使用,用迭代器最多最多是for循环.
4.如果用官方的方法来判断XXX数据是否可迭代对象,迭代器.
iterable:可迭代的
iterator:迭代器
isinstance:判断XXX数据是否XX类型.
#True Flase
5.generator生成器
函数中有了yield,也表示返回,就有了生成器函数.
生成器的函数运行的时候,不会执行你的函数,获取一个生成器.
生成器的执行__next__()
只能拿取一个yield
def chi(): print("第一顿") yield "饺子" print("第二顿") yield "包子" ret=chi() print(ret)#r1=ret.__next__()#第一顿 print(r1)#饺子 r2=ret.__next__()#第二顿 print(r2)#包子
3.yield的作用:(1)把一个普通的函数变成生成器函数.(2)可以分段把一个函数去运行.(3)也可以返回结果,但函数不会终止.
4.生成器的作用:
如果数据量很大的问题,存储是一个大问题.
def order(): lst=[] for i in range(1000): lst.append(i) return lst ret=order()#数据非常大,占用较大内存 print(ret)
对上述代码进行优化:
def order(): for i in range(1000): yield "学员编号",i+1 ret=order() print(ret.__next__())#('学员编号', 1) print(ret.__next__())#('学员编号', 2)
这样生成器,大量的节省内存.因为生成器保存的是代码,不是数据,而第一个程序保存的是数据,不是代码.
生成器几乎不占内存.
例:人一生吃20000个鸡蛋,但如果一次性买20000个不现实,可以买个鸡,想吃就让它下单。这个鸡就是生产器。
5.send()也可以像__next__一样执行生成器中的代码,都是执行到下一个yield.
send可以给上一个yield位置传值
def chi(): print("我要吃饭了") yield "大米饭" print("我不吃主食") yield "锅包肉" print("我喜欢吃素食") c=yield "汉堡包" print(c) yield "吃饱" gen=chi() print(gen.__next__())#我要吃饭了 大米饭 print(gen.__next__())#我不吃主食 锅包肉 print(gen.__next__())#我喜欢吃素食 汉堡包 print(gen.__next__())#None 吃饱
先算=右边的yield,然后才是a,a=yield是分开执行的.
def chi(): print("我要吃饭了") a=yield "大米饭" print(a) print("我不吃主食") b=yield "锅包肉" print(b) print("我喜欢吃素食") c=yield "汉堡包" print(c) yield "吃饱" gen=chi() print(gen.__next__())#我要吃饭了 大米饭 gen.send("大龙虾")#大龙虾 我不吃主食 print(gen.__next__())#None 我喜欢吃素食 汉堡包 print(gen.__next__())#None 吃饱
如果最后一个yield不能传值,不会再有yield,一定会报错.
这个是很少见的,应用很少.
第一个启动的时候使用__next__,不用send(),因为不能赋值.
def chi(): print("我要吃饭了") a=yield "大米饭" print(a) print("我不吃主食") b=yield "锅包肉" print(b) print("我喜欢吃素食") c=yield "汉堡包" print(c) yield "吃饱" gen=chi() print(gen.__next__())#我要吃饭了 大米饭 gen.send("大龙虾")#大龙虾 我不吃主食 gen.send("海鲜")#海鲜 我喜欢吃素食 gen.send("火腿肠")#火腿肠 #吃饱,不执行,也就是send执行前一个yield,不执行后面的yield
五.各种推导式
1.创建一个列表,里面放10000件衣服
lst=[] for i in range(1000): lst.append("学员编号:%d"%(i+1)) print(lst) #整理成一句话 lst=["学员编号%d"%(i+1) for i in range(1000)] print(lst)
这就是列表推导式:[]
字典推到器:{}
集合推导式:{}
元组没有推导式
2.列表推导式:[添加的内容 for循环 if条件]
#生成一个列表1,4,9,16,25 lst=[i**2 for i in range(1,6)] print(lst)
3.字典推导式:{k:v for循环 if条件}
lst=["今天","明天","后天","昨天"] dic={(i+1):lst[i] for i in range(len(lst))} print(dic)
4.集合推导式:{k for循环 if条件}
字典的要求的key是可哈希的
集合就是不存value的字典,只存key
s={i for i in range(1,15,2)} print(s)#{1, 3, 5, 7, 9, 11, 13}
5.没有元组推导式
6.生成器表达式(结果 for循环,if条件)
7.yield from:可以把列表中的数据进行返回.
六.内置函数
Python直接给你的,直接拿来用的
hash(哈希)值 数字的本身就本身,字符串的哈希也是一个数字
id()是十进制的内存地址.
help:ctrl+点,就可以看到字符串,不用percharm的时候用help
6.查看内存属性dir()
7.callable:判断你给的参数是否可以被调用.判断传进来的东能否+(),下面程序中1不能加(),会报错.
8.eval和exec 和compile:意义:可以执行动态的代码,执行字符串代码.
s="6+7+13 *6" ret=eval(s) print(ret)
9. 把字符串的东西还原成列表和字典
exec没有返回值.pycharm的错误不一定是错误.一般情况,pycharm的错误是错误的.
compile:编译.可以把程序预编译一部分代码.提高一部分代码的效率.
Python是一门解释性编程语言.Python的缺点就是慢,来一行解释一行.
compile(代码,文件,执行模式)
想要整体的提高运行速度,需要整体优化.
如果第一个参数给了代码,就不要给文件了,这两个部分是互斥的.
single:当程序出现交互了
不能用exal,必须用exec调用
9,复数:complex,实部+虚部
10.数学的工具,MATLAB
11.矩阵分析:numpy矩阵分析
12.二进制:bin(5) #0b101,0b表示二进制.
八进制:oct(9)#0o11
十六进制:hex(18)#0x12
print(bin(2))#0b10 print(oct(8))#0c10 print(hex(16))#0x10
13.abs绝对值
14.pow求次幂
15.divmod
16.round
17.frozenset冻结的集合,可以把不可哈希的set可哈希.
list对应tuple
18.enumerate
lst=["包子","粥","馒头","咸菜"] for i in range(len(lst)): print(i,lst[i]) ''' 0 包子 1 粥 2 馒头 3 咸菜''' for i,el in enumerate(lst): print(i,el) ''' 0 包子 1 粥 2 馒头 3 咸菜''' for i,el in enumerate(lst,100): print(i,el) '''100 包子 101 粥 102 馒头 103 咸菜'''
19.all和any
all可以把等同于and,
any的作用相当于or
print(all([5,1,"haha","hehe"]))#True print(all([5,0,"haha","hehe"]))#False print(any([5,1,"haha","hehe"]))#True print(any([5,0,"haha","hehe"]))#True
20.slice,切片,切片很难用
s="您好啊,我是jay" s1=slice(1,5) print(s[s1])#好啊,我
主要应用在:很多字符串,按照同样的切割方式的时候可以用.
21.bytearry:字节数组
22.ord和chr
ord把字符放进去,可以在其位置告诉你.
chr在计算机编码位置给,可以找到这个字
23,asci#判断你的文字是不是ASCII的范畴.
24.repr:cPython,我们写的字符串最终转换成C里面的字符串.
把字符串还原回最应该显示的样子,Python是比较随意的,通过repr可以还原成c语言.
25.r””:原样输出,和repr没有关系.
在正则表达式中会应用到.
26.format():格式化.
(3)科学计数法
lst=["包子","粥","馒头","咸菜"] lst1=["今天","明天","后天","昨天"] lst2=[1,2,3,4] z=zip(lst,lst1,lst2) print(z)#for el in z: print(el)
27.zip拉
水桶效应,最短的能合上,最长的用不上.
28.匿名函数lambda x,y:x+y
fn=lambda x,y:x+y ret=fn(2,3) print(ret) def cal(x,y): return x+y ret=cal(2,3) print(ret)
一行代码搞定一个函数
语法:lambda 参数:返回值
29.sorted:排序,是正序,加上reverse就是倒叙.
lst=[5,9,15,64,85,34,25,126,445,61] lst1=sorted(lst) print(lst1)#[5, 9, 15, 25, 34, 61, 64, 85, 126, 445] lst2=sorted(lst,reverse=True) print(lst2)#[445, 126, 85, 64, 61, 34, 25, 15, 9, 5]
30.sorted可以给出自己的排序规则
比如:按照名字的长短排序:
sorted有三个函数(iter,key,reverse) #key必须是一个函数.函数会把前面每一项进行迭代.
排序规则:内部会自动的迭代前面的那个数字,迭代出来的每一项后给后面的key,并调用这个函数.然后根据函数返回的数据进行排序.
lst=["牛栏山","牛儿","门杰雷夫","kenments","勇士"]
#根据名字的长度排序
def fn(x):#x就是列表的每一项 return len(x) lst1=sorted(lst,key=fn,reverse=True)#key 必须是一个函数 #把可迭代对象迭代出来传递给函数key,根据函数的执行结果进行排序. print(lst1)
例如:根据给的年龄进行排序
lst = [{ "name":"alex", "age":34, "hobby":"吹牛"}, { "name": "wusir", "age": 14, "hobby": "吹牛牛"}, { "name": "taibai", "age": 31, "hobby": "吹牛牛牛"}, { "name": "taihei", "age": 12, "hobby": "吹牛牛牛牛"}, { "name": "tailv", "age": 48, "hobby": "吹牛牛牛牛牛"},] #根据年龄排序: def func(dic): return dic["age"] print(sorted(lst,key=lambda dic:dic["age"])) #[{'name': 'taihei', 'age': 12, 'hobby': '吹牛牛牛牛'},\ { 'name': 'wusir', 'age': 14, 'hobby': '吹牛牛'}, \ { 'name': 'taibai', 'age': 31, 'hobby': '吹牛牛牛'}, \ { 'name': 'alex', 'age': 34, 'hobby': '吹牛'},\ { 'name': 'tailv', 'age': 48, 'hobby': '吹牛牛牛牛牛'}] #按照"牛"个数排序 def fn(dic): return len(dic["habyy"]) print(sorted(lst,key=lambda dic:len(dic["hobby"])))
print(sorted(lst,key=lambda dic:dic["hobby"].count("牛")))
#[{'name': 'alex', 'age': 34, 'hobby': '吹牛'}, \ { 'name': 'wusir', 'age': 14, 'hobby': '吹牛牛'},\ { 'name': 'taibai', 'age': 31, 'hobby': '吹牛牛牛'},\ { 'name': 'taihei', 'age': 12, 'hobby': '吹牛牛牛牛'},\ { 'name': 'tailv', 'age': 48, 'hobby': '吹牛牛牛牛牛'}]
lambda主要应用在这里.
按照”牛”的多少排序:
30.筛选:filter
把可迭代对象,进行迭代,把每一项数据传递给前面,根据函数返回的ture和false来决定改元素,是否保留.
31.映射函数map():把可跌对象中每一项,传递给前面的函数,把函数执行的结果作为整个运算的结果.
lst=[5,9,15,64,85,34,25,126,445,61] ret=map(lambda x:x**2,lst) # lst1=[i for i in ret] # print(lst1)#[25, 81, 225, 4096, 7225, 1156, 625, 15876, 198025, 3721] lst2=[] for i in ret: lst2.append(i) print(lst2)#[25, 81, 225, 4096, 7225, 1156, 625, 15876, 198025, 3721]
主要应用:把每一个ip地址进行相同操作.
32,reduce和map相反,处理大量数据的核心思想.
33.hadoop:大数据
七.装饰器
1.开闭原则
开:对增加功能开放
闭:对修改源代码封闭,对已经写好的代码不能修改,防止今天修改,明天修改,把修改的代码变的面目全非.
例如:
反复修改一段代码是比较忌讳的.比如不同的人修改代码,就乱套了,代码是走不远的.
2.装饰器:就是在不改变源代码的基础上,给代码添加新功能.
女娲开始造人是zaoren()
但是三年大旱,需要加入浇水water()需要加上代码
但是这也是不对的,
现在也不对,可以把ret换成zaoren
没有三年大旱:
def zaoren(): print("女娲造人,吹口气") zaoren()
三年大旱后:
def zaoren(): print("女娲造人,吹口气") zaoren() #三年大旱 def water(fn): def inner(): print("浇水") fn() return inner zaoren=water(zaoren) zaoren()
这样已经使用zaoren()程序的又可以继续zaoren()了,没有改变.
这就是装饰器的作用,这是装饰器的雏形.
3.装饰器的基本雏形
fn就是目标函数,需要装饰的函数
inner局势装饰之后的效果.
返回inner:就是方便别人调用.
例如:第二故事:yue():在yue之前需要问问行情:
def wrapper(fn): def inner(): print("Alex最近行情怎么洋?") fn() print("alex你骗我") return inner def yue(): print("约一约") yue=wrapper(yue) yue() #这两种表达方式一样 @wrapper def yue(): print("约一约") yue()
@wrapper等同于yue=wrapper(yue)
@wraps(fn)伪装inner,就像一个fn函数,方便后面的人解读.
from functools import wraps#导入wraps模块 def wrapper(fn): @wraps(fn) def inner(): print("Alex最近行情怎么洋?") fn() print("alex你骗我") return inner @wrapper def yue(): print("约一约") yue()
装饰器最大的作用,在不改变函数源代码的基础上添加新功能.
4.万能参数
def wrapper(fn): def inner(*args,**kwargs):#args元祖,**kwargs对应字典 print("哈哈哈") fn(*args,**kwargs) print("呵呵呵") return inner @wrapper def chi(): print("嗯嗯嗯") chi()
后面的函数可以随便使用.
def wrapper(fn): def inner(*args,**kwargs):#args元祖,**kwargs对应字典 print("哈哈哈") fn(*args,**kwargs) print("呵呵呵") return inner @wrapper def chi(username,password): print("用%s" % username) print("用%s" % password) chi("alex","jintian")
待返回值的装饰器
def wrapper(fn): def inner(*args,**kwargs):#args元祖,**kwargs对应字典 print("哈哈哈") ret=fn(*args,**kwargs)#把接收的数据打散发出去 print("呵呵呵") return ret return inner @wrapper def chi(username,password): print("用%s" % username) print("用%s" % password) return 10000 r=chi("alex","jintian")#调用的是inner() print(r)
5.需要对装饰器进行升级改造
from functools import wraps def wrapper(fn):#目标函数,被装饰的函数 @wraps(fn)#改变inner的名字 def inner(*args,**kwargs):#args元祖,**kwargs对应字典 print("哈哈哈") ret=fn(*args,**kwargs)#把接收的数据打散发出去 print("呵呵呵") return ret return inner @wrapper def func(): pass
6.这种思想:AOP,面向切面编程,就是在好好的东西切开,然后再切开的前面和后面插入一段代码,进行装饰.python里面叫装饰器.
7.装饰器的应用:
HR的工作
在执行以下操作之前,需要登录校验.
有登录的功能:
一旦登录成功
flag=False#没登录
#应用:再执行以下操作前,需要登陆验证 flag=False#没登录 def login(): username=input("请输入名字:") password=input("请输入密码:") if username=="alex" and password=="123": print("登陆成功") global flag#引入全局变量 flag=True else: print("登陆失败")
加上装饰器:登录验证装饰器:
#加上装饰器,登陆验证装饰器 def login_verify_wrapper(fn): def inner(*args,**kwargs): while 1: if flag: ret=fn(*args,**kwargs) return ret else: login() return inner @login_verify_wrapper def add(): pass @login_verify_wrapper def dele(): pass
8.带有参数的装饰器
控制装饰内部参数的位置.
执行流程:先执行wrapper_out,然后执行@
#带有参数的装饰器 def wapper_out(flag):#装饰器的参数 def wapper(fn): def inner(*args,**kwargs): if flag==True: print("alex行情怎么洋") ret=fn(*args,**kwargs) return ret return inner return wapper @wapper_out(True)#执行流程:先执行wrapper_out(True) 返回装饰器 再和@联合起来@wapper def yue(): print("我要约") yue()
9.多个装饰器装饰同一个函数
离最近的先包装,逐一包装.
九.递归
1.贪吃蛇:吃一个长一节,但吃自己无限循环.
def func(): print("哈哈") func() func()
无限开拓空间,就报错了.
不要用递归.
python的递归层数最大是1000,不能超过1000.在window最大998.在苹果中是997.
整个程序的稳定性是非常差的.
递归的第一个应用:遍历文件夹
window的文件夹就是一个树
计算机的树是相反的.
大家都约定俗成,是根节点和页节点.
如果报我的电脑砍掉,c盘就是根节点.
树形结构的便利就是递归的应用.
os.path.join(s,el)#拼接文件路径.
isdir#判断是否为文件夹
递归的入口
千万不能执行.
树形结构的深度递归.
数据结构.
十,商品的信息如何存最简单
eval写入文件
读取文件,使用eval 把文件中的字典和字符串还原.
优化后:
def wrapper(fn):
def inner():
print("Alex最近行情怎么洋?")
fn()
print("alex你骗我")
return inner
def yue():
print("约一约")
yue=wrapper(yue)
yue()
#这两种表达方式一样
@wrapper
def yue():
print("约一约")
yue()