路飞学城-Python开发集训-第3章

学习心得:

  通过这一章的作业,使我对正则表达式的使用直接提升了一个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()函数实现的。

原文地址:https://www.cnblogs.com/sq5288/p/9076275.html