一、上堂回顾
1.默写题目
1.封装函数,将某个字符串中的大写字母转为小写,小写字母转为大写,将新的字符串返回【参数使用默认参数】
#注意1 :明确是否需要设置参数和返回值 #注意2:默认参数操作的形参列表【关键字参数操作的是实参列表】 def customSawp(s="abc"): #注意3:在字符串的操作中,但凡涉及到更改字符串的操作,都会生成一个新的字符串 newStr = s.swapcase() return newStr #ord() chr()
2.封装函数,使用二分法在某个列表中查找指定元素,将索引返回,如果未查找到,则返回-1
#注意1 :明确是否需要设置参数和返回值 #以升序为例 def search(l,key): #注意2:二分法查找的条件:必须是有序的列表 #注意3:二分法查找的思路:通过折半缩小查找范围【和中间下标对应的元素进行比对】 left = 0 right = len(l) -1 #处理查找的过程 while left <= right: #获取中间下标 middle = (left + right) // 2 if key > l[middle]: left = middle + 1 elif key < l[middle]: right = middle - 1 else: # print(middle) #break #注意4:return的作用:结束函数,携带返回值返回 #注意5:break和return之间的区别 return middle """ #处理查找不到的情况 if left > right: return -1 if key not in l: return -1 """ return -1
3.举例说明函数的特殊用法
def test(): pass #1.变量可以指向函数 f = test f() #2.函数名也是一个变量 test = 10 #test() #3.函数可以作为参数使用 def fun(f): f() fun(test)
4.已知列表list1 = [1,2,3,4,5],使用列表生成式生成一个新的列表,newList = [3,6,9,12,15]
#注意1:[需要生成的列表的元素规律 for循环 判断条件] #注意2:for循环一般情况下是一个已知的列表 list1 = [1,2,3,4,5] list2 = [i * 3 for i in list1] #[2,4] list3 = [i for i in list1 if i % 2 == 0]
2.知识点回顾
作业
def isPrime(num): result = True for i in range(2,num): if num % i == 0: result = False break if result and num != 1: return True else: return False def getCount(n): c = 0 for x in range(2,n + 1): if isPrime(x): c += 1
二、变量的作用域
1.概念
1.1出现的原因
变量的作用域指的是一个变量可以被使用【访问】的范围
程序中的变量并不是在任何地方都可以访问的,访问权限取决于这个变量被定义在什么位置【直接定义,语句中,函数中】
1.2作用范围划分
分类:
a.L:Local,局部作用域
b.E:enclosing,函数作用域【体现在闭包中】
c.G:Global,全局作用域
d.B:Built-in,內建作用域【内置作用域】
1.3变量查找规则
L-->E--->G--->B
注意:全局作用于和內建作用域并没有严格的界限,主要由书写的先后顺序决定查找规则
代码演示:
#1.四种变量由不同的命名 num3 = 30 #全局作用域 num4 = int(2.9) #内置作用域【系统内置功能返回的值赋值给一个变量】 def outer(): num1 = 10 #函数作用域 def inner(): num2 = 20 #局部作用域【局部作用域并不是绝对的】 #结论:在闭包的内部函数中,可以访问全局,内置,函数 print(num1,num2,num3,num4) return inner f = outer() f() #2.四种变量有相同的命名访问规则: #结论:如果所有的变量重名的情况下,就近原则 x = 10 x = int(3.2) def outerFunc(): x2 = 20 def innerFunc(): x1 = 30 print(x) return innerFunc f1 = outerFunc() f1() #注意:全局金额局部使用较多,在闭包存在的情况下使用到函数作用域 #3.注意问题 #if语句 if True: msg = "abc" print(msg) #while语句 n = 0 while n < 2: msg1 = "fhajgh" n += 1 print(msg1) #函数 def test(): msg2 = "hello" #print(msg2) #结论:Python中只有模块【module】,类【class】和函数【def,lambda】 会引入新的作用域 # 其他的代码块,比如if语句,while语句,for语句,try-except语句是不会引入新的作用域的 # 【在这些语句中定义的变量,在语句外面也可以进行访问】 #在函数内部定义的变量,仅限于在函数内部使用,一旦出了函数,该变量则将不存在【内存中被销毁或者被释放】 def show(a,b): sum = a + b show(10,20) print(sum)
2.全局变量和局部变量
全局变量:变量定义在函数外面,在当前整个py文件中都可以任意访问
局部变量:变量定义在函数中,只能在当前函数中访问
代码演示:
#4.全局变量和局部变量 total = 0 #全局变量 def sum(num1,num2): total = num1 + num2 #局部变量 print("函数内部的局部变量:",total) print("全局变量:",total) print("函数外部的全局变量:",total) sum(10,20) #注意:尽量避免全局变量和局部变量重名的情况 【当重名,在函数内部会优先访问局部变量】
3.global和nonlocal关键字的使用
3.1global
代码演示:
num = 1 def fun1(): #需求:访问全局num #解决办法:在访问全局变量之前作出声明 global num #告诉编译器,此处的num是全局的 print(num) #1 #UnboundLocalError: local variable 'num' referenced before assignment num = 123 print(num) #123 fun1() #如果在函数内部出现了和全局变量重名的局部变量,而在局部变量定义之前要访问全局变量,则需要使用global关键字对全局变量进行声明 a = 10 def test(): global a a = a + 1 print(a) test()
3.2nonlocal
代码演示:
#注意;nonlocal使用在闭包中 #讨论变量名重名的情况 x = 0 #全局作用域 def outer(): x = 1 #函数作用域 def inner(): #结论:将变量的局部作用域修改为函数作用域 nonlocal x x = 2 #局部作用域 print("inner:",x) inner() print("outer",x) outer() print("global",x) """ inner:2 outer:1 ----->2【添加nonlocal x的声明】 global:0 """
三、迭代器
1.可迭代对象
概念:可以直接作用于for循环的对象【实体】被称为可迭代对象:Iterable
分类:可以直接作用于for循环的数据类型:
a.集合数据类型,包含list,tuple,dict,set,string
b.generator,包含()生成器和yield的生成器
判断:isinstance() 可以用来判断一个对象【实体】是否是可迭代对象
instance:对象,实体,实例
代码演示:
#第一步:导入模块 from collections import Iterable #import collections #isinstance(数据,Iterable) print(isinstance([],Iterable)) print(isinstance((),Iterable)) print(isinstance({},Iterable)) print(isinstance(set([]),Iterable)) print(isinstance("hello",Iterable)) print(isinstance((x for x in range(10)),Iterable)) print(isinstance(10,Iterable)) print(isinstance(True,Iterable))
2.迭代器
概念:不但可以直接作用于for循环,还可以使用next()获取元素值,同时符合这两个条件的数据被称为迭代器【Iterator】
目前而言:只有生成器是迭代器
判断:isinstance() 可以用来判断一个对象【实体】是否是迭代器
代码演示:
from collections import Iterator #isinstance(数据,Iterable) print(isinstance([],Iterator)) print(isinstance((),Iterator)) print(isinstance({},Iterator)) print(isinstance(set([]),Iterator)) print(isinstance("hello",Iterator)) print(isinstance((x for x in range(10)),Iterator)) #True print(isinstance(10,Iterator)) print(isinstance(True,Iterator)) #可以使用iter()可以将可迭代对象转换为迭代器【除了生成器之外的可迭代对象】 #总结:可迭代对象不一定是迭代器,迭代器肯定是可迭代对象 list1 = [435,5,4] a = iter(list1) print(next(a)) #next()获取迭代器中的元素,当所有的元素获取完毕之后,继续调用next,会出现StopIteration list2 = [435,5,4] b = iter(list2) #工作原理 while True: try: # 获取下一个值 x = next(b) except StopIteration: print("元素已经获取完成") break #等价于上面的while for x in b: pass #【面试题】:说明可迭代对象和迭代器之间的区别和联系
四、装饰器【掌握】
代码演示:
def now(): print("拼搏到无能为力,坚持到感动自己") #now() #f = now #变量可以指向函数,函数名也是一个变量,所以变量可以当做函数调用 #f() #思考问题:给now函数增加功能,比如:求和,但是不能修改now函数----》装饰器 #概念:假设我们要增强now函数的功能,但是又不希望修改now函数,这种在代码运行期间动态给函数增加功能的的方式被称为装饰器 #本质:装饰器实际上就是一个闭包【将需要装饰的函数作为参数传给闭包,然后将装饰之后的结果返回】 #好处:在不用修改原代码前提下增加函数的功能【动态性】 #1.简单的装饰器 #a.书写闭包 #b.给外部函数设置参数 def outer(fun): def inner(): #c.在内层函数中调用需要被装饰的函数 fun() #d.增加新的功能 print("hello world") return inner #e.使用闭包 f = outer(now) #函数可以作为参数使用 f() #注意:增加的功能可以写在原函数调用的前面或者后面,没有严格的区分 #注意:outer被称为装饰器,inner被称为装饰器的工作核心 #练习:给下面的函数增加功能,打印九九乘法表 def show(): for i in range(10): print(i) show() def outer1(f): def inner1(): f() #增加新的功能 for i in range(1,10): for j in range(1,i + 1): print("%dx%d=%d"%(j,i,i*j),end=" ") print("") return inner1 f1 = outer1(show) f1() #2.有参数的装饰器 def getAge(age): print(age) #问题:使用getAge得到年龄,但是输入的是负数则会得到不符合常理的结果 getAge(10) #10 getAge(-5) #-5 #需求:在不修改原代码的基础上,进行数据的过滤:当用户输入的age为负数的时候 ,将其置为0 def wrapper(f): #注意;当原函数有参数,装饰器的作用是为了操作原函数中的参数,给inner设置参数 def inner(num): #增加新的功能:过滤数据 if num < 0: num = 0 f(num) return inner f2 = wrapper(getAge) f2(10) f2(-5) """ 总结:通过这样的方式,将边界检查的逻辑隔离到单独的函数中,一般使用装饰器操作 """ #3.使用 @ 标识符将装饰器直接应用到函数 #格式:@装饰器的名字【外部函数的函数名】 #注意;使用@之后,装饰器必须先被定义,然后再使用到原函数,此时原函数也相当于被增加新功能 def wrapper1(f): def inner(num): if num < 0: num = 0 f(num) return inner @wrapper1 def getAge1(age): print(age) getAge1(-5) getAge(10) #4.带有不定长参数的装饰器 def wrapper(fun): def inner(*args,**kargs): fun(*args,**kargs) print("hello") #新功能 return inner @wrapper def fun1(a,b): print(a + b) fun1(10,20) @wrapper def fun2(a,b,c): print(a * b * c) fun2(1,2,3) #5.将多个装饰器应用在同一个函数上 def wrapper11(fun): def inner(*args,**kargs): fun(*args,**kargs) print("装饰器11~~~~") return inner def wrapper12(fun): def inner(*args,**kargs): fun(*args,**kargs) print("装饰器12~~~~") return inner @wrapper11 @wrapper12 def test(): print("test") test() """ test 装饰器12~~~~ 装饰器11~~~ """ """ 总结; 1.就近原则【多个装饰器同时出现,谁最后出现,则最先被执行】 2.多个装饰器应用于同一个函数的时候,原函数只被执行一次 """
五、函数递归
1.概念
一个函数调用本身,被称为函数递归【递归调用】
递归包含了一种隐式的循环,它会重复执行某段代码,。但是这种重复无须用条件控制【但凡使用循环可以解决的问题,一般使用递归解决】
使用递归解决问题需要考虑的因素:
a.找到一个临界值【临界条件】
b.寻找相邻两次循环之间的关系【公式】
2.使用
1.斐波那契数列
2.求和
代码演示;
#斐波那契数列 """ 1 2 3 4 5 6 7 8 9 10 1,1,2,3,5,8,13,21,34,55,89.... 需求:报一个数,输出在数列中对应的数 规律: a.临界值:第一个位置和第二个位置上的数是固定的,都是1 b.公式:第n个位置上的数 = 第n - 1个位置上的数 + 第 n- 2位置上的数 num = 5 func(5) = func(4) + func(3) func(5) = func(3) + func(2) + func(2) + func(1) func(5) = func(2) + fun(1)+ func(2) + func(2) + func(1) = 5 ..... func(n) = func(n - 1) + func(n - 2) func(num) = func(num - 1) + func(num - 2)------>公式 """ def func(num): if num == 1 or num == 2: return 1 else: #都需要通过前面的两个位置上的数进行求和 result = func(num - 1) + func(num - 2) print(num) return result print(func(1)) #1 print(func(2)) #1 print(func(10)) #55 #使用递归:计算1~某个数之间所有整数的和 def func1(num): sum = 0 n = 1 while n <= num: sum += n n += 1 return sum print(func1(100)) """ 过程分析:求1~5之间的所有整数的和 1+2+3+4+5 sum(1) = 1 sum(2) = sum(1) + 2 = 1 + 2 sum(3) = sum(2) + 3 = sum(1) + 2 + 3 = 1 + 2 + 3 sum(4) = sum(3) + 4 = sum(1) + 2 + 3 + 4 = 1 + 2 + 3 + 4 .... sum(n) = sum(n - 1) + n -----》公式 规律: 1.临界值:1 2.公式:sum(n) = sum(n - 1) + n """ def sum(num): if num == 1: return 1 else: return sum(num - 1) + num #总结:使用递归函数的好处代码简单,缺点:过深的调用会导致栈溢出【内存泄漏】