学习心得:
通过这一章的作业,使我对正则表达式的使用直接提升了一个level,虽然作业完成的不怎么样,重复代码有点多,但是收获还是非常大的,有点找到写代码的感觉了,遗憾的是,这次作业交过,这次集训就结束了,后面视频中的作业和内容将不再做讲解,也没有作业批改,不论如何这次集训对于自己提升还是蛮多的,真的要感谢路飞这个学习平台。
学习笔记:
三元运算:
三元运算又称三目运算,是对简单的条件语句的简写。
#简单条件语句 if 条件成立: val=1 else: val=2 #改成三元运算 val=1 if 条件成立 else 2
小例子:
a = 2 b = 5 val = a if a <b else b print(a)
函数:
函数的参数类型:位置参数、关键字参数、缺省参数、非固定参数
*args
def send_alert(msg,*args,age): for u in args: print("报警发送给",u) #如果参数中出现 *agrs,传递的参数就可以不再是固定个数,传过来的所有参数打包成元组 #传参数有两种方式,一种是方式一,这种情况下所有参数被打包成元组 #另一种方式是方式二,直接传一个元组或列表,注意一定要在元组或列表前面加*,不然会把他们当成一个参数 #其实本质就是args就是一个元组 #一般情况下非固定参数放在最后,后面不加其它参数,这里后面加个age是为了实验效果,由于args会截获所有位置参数,所以这里在给age传参数的时候,必须要指明,不然也会被args截获,从而没有age的错 #方式一: send_alert("别再浪了","alex","eric","rain",age=22) #方式二: send_alert("别再浪了",*["alex","eric","rain"],age=22)
**kwargs
def func(name,*args,**kwargs): print(name,args,kwargs) func("alex") #alex () {} 可以看出后面两个参数是可以不传的,不传就为空 func("alex",22,"tesla","500w",addr="山东",num=1244) #alex (22, 'tesla', '500w') {'addr': '山东', 'num': 1244} #args会截获位置参数,而kwargs会截获关键字参数 d={"degress":"primary school"} func("peiqi",d) #peiqi ({'degress': 'primary school'},) {} func("peiqi",**d) #peiqi () {'degress': 'primary school'} 这个和上面args一样的道理
局部变量:
(1)局部变量和全局变量在某种程度上是两个独立的空间,相同的变量名代表不同的变量,所以局部并没有修改全局变量,而是在局部创建一个相同变量名的变量。
name="black girl" def change_name(): name="黑色的女孩" print(name) change_name() #黑色的女孩 print(name) #black girl
(2)查找变量遵循从自身开始按LEGB方向查找,比如在全局调用变量,那么就从身G开始,按GB方向查找,不会往后找。
(3)由于局部变量和全局变量是两个独立空间,那如果想在局部修改(重新赋值方式)全局的变量,怎么办?
name="black girl" def change_name(): global name #声明name是全局的那个变量,声明要放在变量前面,不然会报错!在生产环境中不建议在局部修改全局变量 name="黑色的女孩" print(name) change_name() #黑色的女孩 print(name) #黑色的女孩
注意下面这种方法虽然能修改变量的值,但其实并没有修改变量本身,变量并没有变。
name=["alex","black girl","peiqi"] def change_name(): global name del name[2] print(name) change_name() #['alex', 'black girl'] print(name) #['alex', 'black girl']
变量自身有没有变可以看内存地址,如果内存地址变了,说明这个变量变了
name=1 print(id(name)) #1358758352 name=2 print(id(name)) #1358758384 name=["alex","black girl","peiqi"] print(id(name)) #12374408 del name[2] print(id(name)) #12374408
嵌套函数:
#情形一 def func1(): print("alex") def func2(): print("eric") func1() #alex #分析:为什么结果只打印了一个alex,而没有打印eric? #这是因为函数定义的时候不会被执行,如果想执行必须调用,题中调用了函数1,而函数1在执行的时候先是打印了alex, # 然后定义了函数2,但这只是定义了函数2,并没有执行,所以只打印了alex #情形二 def func1(): print("alex") def func2(): print("eric") func2() func1() #alex eric #分析:为什么这次两个都打印了? #这是因为在执行函数1时,先打印了alex,然后定义了fun2,定义函数2之后,又执行了函数2,所以两个都打印了。 #情形三 age = 19 def func1(): age =73 print(age) def func2(): age = 84 print(age) func2() func1() #73 84 #这个没什么好说的。 #情形四 age = 19 def func1(): age =73 print(age) def func2(): print(age) func2() func1() #73 73 #这个也没什么好说的,按照LEGB的原则去找,fun2在自己内部找不到age变量,就到它的父级去找,在父级找到了就打印了 #情形五 age = 19 def func1(): def func2(): print(age) age = 73 func2() func1() #73 #由于定义age是在执行func2之前,所以age存在于他的父级作用域,所以它就在父级作用域找到了age,所以打印了73 #情形六 age = 19 def func1(): def func2(): print(age) func2() age = 73 func1() #报错:NameError: free variable 'age' referenced before assignment in enclosing scope #分析:为什么会报错? #虽然父级作用域存在age变量,但func2在调用的时候age还没有被定义,这就出现了调用在定义之前,所以报错,这个和global那个比较类似 #情形七 age = 19 def func1(): global age def func2(): print(age) func2() age = 73 func1() #19 print(age) #73 #这个global声明是在最上面的,所以fun2在自身没找到后,往父级找,其实在父级也没找到(父级中的age也是全局的,只是在局部被重新赋值了), # 然后往global找,找到了age=19,所以就打印了。打印73是因为被重新赋值导致的 # 情形八 age = 19 def func1(): global age def func2(): print(age) age = 73 func2() func1() #73 #func2在执行之前age已经被重新赋值了,所以打印的是73
作用域:
在python中函数就是一个作用域
代码定义完成后,作用域已经生成,作用域链向上查找
匿名函数:
def calc(x,y): return x*y lambda x,y:x*y #声明一个匿名函数 #上面这两个函数执行效果是一样的,但是第二个函数不好执行,如果想执行需要将它赋给一个变量,如下: func=lambda x,y:x*y #赋给一个变量之后这样就好执行了 print(func(3,8)) #24 ############################################################################################# def calc(x,y): if x<y: return x*y else: return x/y #lambda不支持复杂的逻辑语句,像上面这样就不行,lambda支持最复杂的就是三元运算,匿名本质上就是把多行语句变成了一行 func=lambda x,y:x*y if x<y else x/y print(func(16,8)) #2.0 #匿名函数主要一个作用就是和其它一些方法搭配使用(像上面这样匿名函数赋值给一个变量,和普通函数没什么区别) def f2(n): return n*n print(list(map(f2,data))) #用普通函数实现乘方 print(list(map(lambda x:x*x,data))) #map函数的作用就是将后面的参数一个一个放入前面的函数执行。 #匿名函数的作用: #1.节省代码量 #2.看着高级
深入分析:
# ---CASE 1 fs = map(lambda i:(lambda j: i*j), range(6)) print([f(2) for f in fs]) #---CASE 2 fs = [lambda j:i*j for i in range(6)] print([f(2) for f in fs]) #---CASE 3 fs = [] for i in range(6): fs.append(lambda j:i*j) if i==3: break print([f(2) for f in fs]) #---CASE 4 fs = [(lambda i:lambda j:i*j)(i) for i in range(6)] print([f(2) for f in fs])
搞会这几题只需弄懂一个问题,lambda函数是否执行并且在什么时候执行。
递归:函数在执行的过程中执行自己。
import sys print(sys.getrecursionlimit()) #默认递归深度 sys.setrecursionlimit(1500) #设置递归深度 def resursion(n): print(n) resursion(n+1) resursion(1)
为什么要限制递归深度?
简单的理解就是递归函数执行之后,之前执行的所有函数都没有结束,这样会占用大量的内存空间,最终把内存撑爆。
深层次的理解就是函数的栈帧的关系。
递归的作用:用来解决复杂的数学问题,比如斐波那契数列、汉诺塔、多级评论数、二分查找、求阶乘等
求阶乘:
任何大于1自然数n阶乘表示方法:
n! = 1*2*3*4...*n
n! = n*(n-1)!
内置函数:
eval() 执行括号内的字符串,如果是代码或运算就执行,但只能执行一行代码,多行无法执行,它可以拿到返回值,但是无法执行函数。
exec() 可以执行多行代码,如果代码中有函数,这个函数是拿不到返回值的,也就是说只能执行,但没有结果。
code=''' def foo(): print("run foo") return 1234 foo() ''' #这个函数必须顶行写,不然报错!!! res=exec(code) #run foo print(res) #none,也就是说exec没有返回值 print(eval("1+3+6+8")) #18,虽然eval不能执行多行代码,但是通过此方法还是能证明它有返回值。 print(exec("1+3+6+8")) #None,再次证明exec没有返回值
ord() 查看字符的ASCII码
chr() 将看ASCII码编号对应的字符
print(ord("a")) #97 print(chr(97)) #a
sum() 求和
a=[100,-33,-22,180,30] print(sum(a)) #255
print()
msg="又回到最初的起点" msg2="记忆中你青涩的脸" f=open("那些年.txt","w",encoding="utf8") print(msg,msg2,end="",sep="|",file=f) #又回到最初的起点|记忆中你青涩的脸
dir() 打印当前程序的变量名
vars() 打印当前程序的变量名和变量值
locals() 打印当前局部变量的变量名和变量值
globals() 打印全局变量名和变量值
print(dir()) #['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__'] print(vars()) #{'__spec__': None, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Lowry/PycharmProjects/lufyy/dir_var.py', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000060E3C8>, '__doc__': None, '__name__': '__main__', '__cached__': None, '__package__': None} print(locals()) #如果在全局,结果同上,如果在函数里面,那么只打印局部变量和变量值 print(globals()) #不论在哪,都是只打印全局变量名和变量值
round() 保留几位小数
print(round(1.22234342,2)) #1.22 保留两位小数
装饰器:
写一个装饰器
生成器:
列表生成式:li = [ i*i for i in range(10) ] 这个生成的是一个列表
这种生成式用法只能用在列表或元组里
斐波那契
只要函数里面有yield,这个函数名()就变成了一个生成器,无论里面有没有return。
return在生成器里,代表生成器的中止,直接报错。
next
唤醒生成器并断续执行
send
唤醒并继续执行
发送一个信息到生成器内部
def range2(n): count=0 while count<n: count+=1 sign=yield count if sign=="stop": break g=range2(10) print(next(g)) print(next(g)) g.send("stop") print(next(g))
迭代器:
可直接用于for循环的数据类型有以下几种:
一类是集合数据类型,如 list、tuple、dict、set、str 等
一类是generator ,包括生成器和带yield的generator function。
可以直接用于for循环的对象统称为可迭代对象:iterable
可使用instance()判断一个对象是不是iterable对象
from collections import Iterable #Iterable第一个字母必须要大写 print(isinstance([],Iterable)) #True
而生成器不但可以被for循环,还可以被next()函数调用并返回下一个值,直到最后抛出stopiteration错误表示无法继续
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator (只要满足这个条件的都迭代器,生成器只是迭代器的一种)
可使用instance()判断一个对象是不是iterator对象
from collections import Iterator print(isinstance((i for i in range(10)),Iterator)) #True print(isinstance([],Iterator)) #False
小结:
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterbtor类型,它们表示一个惰性计算序列
集合数据类型如list、dict、str乖都是Iterable但不是Iterbtor,不过可以通过iter()函数将可迭代对象变成迭代器对象。
python3的for循环本质上就是通过不断调用next()函数实现的。