7月28号小结
目录
函数
函数的参数
全局变量和局部变量
迭代器和生成器
匿名函数
递归调用
高阶函数
装饰器
函数
函数的作用:将面向过程的代码变得模块化,能够实现重复调用。
以前使用过的函数有:input、print、type等。
函数的语法
-
使用def关键字定义函数,def后面跟函数名(本质:变量)、圆括号和冒号。
-
圆括号中放形参(形参是向函数内部传参的变量)
-
函数内部代码块通常第一行写多行注释,作为解释函数作用的注释。
-
代码块结束以后使用return,将函数内部产生的结果返回到函数外部。
-
函数定义完,使用函数时叫做调用函数,如何调用?
函数名后面跟圆括号
-
调用函数时需要在圆括号中写实参传递给形参。
实例
def rangeSum(start, end):
"""计算某个范围的和"""
sum = 0
for i in range(start, end + 1):
sum += i
print(sum)
return sum
# print('fd')
# 调用函数
print(rangeSum(1, 100))
print(rangeSum(1, 1001))
- 函数:可以降低代码段冗余度,也可以给代码阅读者更清晰的思路(一个函数代表一个功能)。
return的作用
- 能够将函数内部产生的结果返回到函数外部。
- 结束函数的执行。
- return后面可以放任何东西。(比如:变量(一个或者多个),变量与变量的运算,字符串等其他东西)
实例:将判断一个年份是否是闰年的代码封装成函数
def is_leap_year(year):
"""判断是否是闰年"""
if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
return '闰年'
else:
return '平年'
# for i in range(1, 2023):
# print(is_leap_year(i))
print(is_leap_year(2005))
总结:
封装函数(自定义函数)在不改变原有代码执行逻辑、顺序的基础上做封装。
函数执行的顺序
从调用函数开始执行,函数定义时,程序不参与1执行。
例子(在Python Tutor中进行运行,去一步步看程序执行的过程)
a = 0
def is_leap_year(year):
"""判断是否是闰年"""
if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
return '闰年'
else:
return '平年'
# for i in range(1, 2023):
# print(is_leap_year(i))
print(is_leap_year(2005))
函数的参数
-
默认值参数
自定义一个函数,函数的功能是计算某个范围中整数的和,默认计算1-100的和或者也可以自定义范围计算和
def rangeSum(start=1, end=100): sum = 0 for i in range(start, end + 1): sum += i return sum print(rangeSum()) print(rangeSum(200, 1000))
-
位置参数
形参和实参之间的顺序要一一对应
print(rangeSum(100, 1000)) print('|', '\t', 1, 2, 3)
-
关键字参数
形参和实参之间的对应关系
关键字参数传参方式:
调用函数时:rangeSum(形参1 = 值1, 形参2 = 值2)
print(rangeSum(start=100, end=1000))
print(rangeSum(end=1000, start=100))
那个形参等于什么值,相当于在调用函数时提前规定好了。
-
位置参数和关键字参数混用
位置参数必须在前面,关键字参数在后面。
def numSum(x, y, z): print(f'x:{x},y:{y},z:{z}') return x + y + z print(numSum(10, 20, z=30)) print(numSum(10, z=50, y=20)) # print(numSum(z=10, y=50, 20)) # ---> 错误:positional argument follows keyword argument
-
不定长参数(带*的变量)
不定长参数包含:*args,**kwargs
一个*不定长参数必须放在两个**的不定长参数前面
def selfTest(*args, **kwargs): # pass不参与程序执行,仅仅是保证代码结构的完整性 # pass print(args, kwargs) selfTest(1, 2, 2, 4, x=1, y=2, z=3)
传参:一个*不定长参数只能接收"位置参数";两个**的不定长参数只能接收"关键字参数"。
为什么使用不定长参数?
不定长参数可以一次性传入N个参数(N>=0),可以解决实参和形参数量不一致的问题。
- *args接受位置参数后将所有数据转换成一个元组。
- **kwargs接受关键字参数后将所有数据转换成一个字典。
-
参数指定数据类型
- 给形参赋默认值,默认值时什么数据类型等于传参的数据类型。
- 直接以"冒号数据类型(形参: int)"的形式创建形参。
- 在定义函数时使用"-> int"表明此函数返回什么数据类型。
def rangeSum(start=1, end=100) -> int: sum = 0 for i in range(start, end + 1): sum += i return sum rangeSum() # ---------------------------- def rangeSum(start: int, end: int): sum = 0 for i in range(start, end + 1): sum += i return sum rangeSum(10, 100)
全局变量和局部变量
按照作用域不同可以将变量分为全局变量和局部变量。
- 全局变量
- 从变量定义开始到程序结束,整个范围都是全局变量的作用域。
- 局部变量
- 在函数或类中创建的变量,从函数或类中变量定义开始导函数或类结束,此方位是局部变量的作用域。
a = 0
def selfTest():
print(a)
b = 1
print(b)
selfTest()
print(a)
# print(b) # NameError:name 'b' is not defined
-
局部变量的作用或意义?
局部变量创建函数或类中,当函数或类被调用时局部变量才开始创建,当函数或类结束时,局部变量被销毁。
global:在函数内部修改全局变量。
nonlocal:在函数A的函数B中修改函数A的变量。
# global和nonlocal x = 100 def func1(x): print(x) # 100 x = 0 print(x) # 0 func1(x) print(x) # 100 # global:在函数内部修改全局变量 """ alobal 变量 变量 = 值 """ y = 100 def func2(): global y y = 666 func2() print(y) # nonlocal:在函数A的函数B中修改函数A的变量 """ nonlocal 变量 变量 = 值 """ def A(): z = 0 def B(): nonlocal z z = 6 B() print(z) A()
迭代器和生成器
一、迭代
迭代在Python中就是属于可以做
for循环遍历
的对象,被叫做可迭代对象。
二、可迭代对象分为容器和迭代器两个,迭代器又有生成器分支。
三、迭代器
性质:
- 使用print无法查看迭代器中的元素。
- 使用len方法也无法查看其中元素个数。
- 迭代器(iterator)
- 迭代器类似于数据结构中的队列(先进先出),迭代器中的元素被取出,迭代器即消失。
- 任何容器型数据类型都可以使用iter方法转为迭代器。
str1 = 'hello' str1_iter = iter(str1) print(str1_iter, type(str1_iter)) list1 = [1, 2, 3] list1_iter = iter(list1) print(list1_iter, type(list1_iter)) # print(len(list1_iter)) # TypeError: object of type 'list_iterator' has no len()
四、如何获取迭代器中的元素
方法一:for循环遍历
方法二:next():适用于取单个元素
print(next(str1_iter)) # 一次只取一个值 print(next(str1_iter)) for i in str1_iter: print(i)
五、迭代器能做什么?
全部元素被取出,迭代器消失,迭代器所占内存被释放,程序就可以减小内存占用。
六、生成器(generator)
性质:
- 生成器是为了生成迭代器(生成就是迭代器,但是迭代器不一定是生成器)。
- 生成器是一个函数。
- 调用生成器函数时,函数不执行,只有在获取元素时生成器函数才执行。并且是获取一个元素才能够让生成器函数执行一次。
- 生成器函数返回内部结果使用yield来代替return(可以同时存在,但是没必要)
- yield执行几次,生成器就产生几个元素。
def myGener(): print('123') yield 100 yield 200 if 100 > 200: yield 300 return 1 yield 400 f = myGener() print(f) print(next(f)) for i in f: print(i)
七、yield和return的区别
- return被触发,函数立即结束。yield不会结束函数的执行。
- return和yield都可以将函数的内部结构返回到外部。
- return和yield后面都是跟表达式。
练习:使用生成器创建
Python001
-Python999
学号def stu_gener(num): for n in range(1, num): yield f'Python{n:0>3d}' fc = stu_gener(1000) print(fc) print(next(fc)) for _ in range(100): print(next(fc)) # for j in fc: # print(j) # ======================= print(next(fc)) print('*' * 20) # rjust的用法(字符串) for l in range(1, 1000): print('Python' + f'{l}'.rjust(3, '0'))
匿名函数
一、概念
Python中有一个lambda表达式,lambda有匿名的意思,并且lambda表达式就是简化的函数,所以被叫做匿名函数。
二、理解
匿名函数和普通函数的关系理解为单分支结构和三目运算符的关系。
三、匿名函数在语法上就是由单一的严格限制的固定语法。在语义上是普通函数的语法糖。
语法糖(糖衣语法),是英国计算机学家提出的一个概念。语法糖就是对已经存在的语法的简化。
语法糖能提高代码的可读性,但是不会影响其功能。
案例:计算两个数字的和
def numSum(num1, num2): return num1 + num2 print(numSum(10, 20)) # ----------------------- numSum_lambda = lambda num1, num2: num1 + num2 print(numSum_lambda(10, 20))
-
匿名函数的语法:
函数名 = lambda 形参: 返回值
函数名(实参)
练习:计算1-100的和(使用匿名函数)
def num_sum(start=1, end=100):
n = 0
for i in range(start, end + 1):
n += i
return n
total_lambda = lambda start, end: sum([i for i in range(start, end + 1)])
print(total_lambda(1, 100))
并不是所有的函数都可以转为匿名函数
练习:使用匿名函数判断年份是否是闰年
year_lambda = lambda year: '闰年' if year % 4 == 0 or year % 100 != 0 and year % 400 == 0 else '平年'
print(year_lambda(2000))
递归调用
一、递归
递归就是一种循环的思想。
函数之间是一个相互调用的过程,递归式对于函数来说的,但是递归是一种特殊的函数调用(自己调自己)。
二、迭代(递推)思想和递归思想
递推思想:斐波那契数列:根据已有的数据推算规律(正向的推算)。
递归思想:根据规律逆向推算。
三、递归虽然是循环的思想,但是要注意循环结束的条件,不然就陷入了死循环。
著名的计算机学家:迭代是人,递归是神。
例子:斐波那契数列
# 迭代(递推)思想 a = 1 b = 1 N = 9 for i in range(N - 2): mid = a + b a = b b = mid print(b) # 递归思想 def fib(n): if n == 1 or n == 2: return 1 else: return fib(n - 1) + fib(n - 2) print(fib(9))
解释递归思想的过程:
fib(9) = fib(8) + fib(7)
fib(8) = fib(7) + fib(6)
fib(7) = fib(6) + fib(5)
fib(6) = fib(5) + fib(4)
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + fib(2)
fib(3) = fib(2) + fib(1)
如何写出递归思想的代码:
- 知道最终结果的计算规律。
- 从需要算的结果向前推。
- 了解递归结束的条件。
- 学会自己调自己的方式。
递归练习:计算n的阶乘
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n - 1)
print(factorial(10))
注意:
-
能使用循环解决的就能使用递归。
-
但是递归使用时需要谨慎,如果涉及计算量很大,有一个拓栈和压栈的过程,拓栈时容易内存溢出。
栈:先进后出(的形式)
高阶函数
一、定义
将一个函数当作另一个函数的参数,叫做高阶函数。
print(max) # <built-in function max>
例子
def func1(): print('这是一个函数') print(func1) func1() a = func1 a() # 自定义函数相当于是定义了一个数据类型为function的变量 def func2(func): print('这是另一个函数') func() func2(func1)
二、Python常用的高阶函数
max、min、sorted、map、reduce
max
、min
:获取容器中最大(最小)的元素
nums = [10, 55, 67, 666, 900]
print(max(nums))
print(min(nums))
max(容器, key=函数):按照函数定义的方式获取容器中的最大值。
min同理
函数的要求:
- 函数有且只有一个形参。
- 必须有返回值。
案例:按照数字中个位数的大小取值。(找出哪个数字的个位数最大)
nums = [10, 55, 67, 666, 900] def unitNum(num): return num % 10 print(max(nums, key=unitNum)) print(max(nums, key=lambda num: num % 10)) print(min(nums, key=unitNum))
sorted(容器,key=函数):按照函数指定的形式对容器中元素排序
函数的要求:
- 函数有且只有一个形参。
- 必须有返回值。
nums = [10, 55, 67, 666, 900] print(sorted(nums, key=lambda num: num % 10))
reduce:根据传递的容器对容器中每个元素做累计。
reduce(函数,容器,初始值)
函数的要求:
- 函数要有两个形参。第一个形参开始指向初始值,然后再指向每次累积的结果。第二个形参指向容器中每个元素。
- 必须有返回值。
from functools import reduce nums = [10, 55, 67, 666, 900] # 对列表中的元素做累乘。 print(reduce(lambda total, num: total * num, nums, 1))
-
reduce和for循环对比:
reduce性能明显不及for循环,但是从代码可读性(优雅的写法)reduce是更好的。
map():根据容器产生一个新的序列
map(函数,容器1,容器2,…容器n…)
函数的要求:
- 函数有N(容器数量)个形参。
- 必须有返回值。
-
map函数的结果是一个可迭代对象,可以将这个可迭代对象使用构造器语法转换为列表、元组、集合等容器
names = ['小明', '小王', '小李'] chinese = [90, 95, 80] english = [60, 70, 80] # {'name':'小明','chinese':90, 'english': 60} map_iter = map(lambda a, b, c: {'name': a, 'chinese': b, 'english': c}, names, chinese, english) print(map_iter) # <map object at 0x000001998DF22700> # for i in map_iter: # print(i) print(list(map_iter))
案例:将列表[10, 5, 30, 100, 900, 666]中每个元素乘以2。
nums = [10, 5, 30, 100, 900, 666] result = map(lambda num: num * 2, nums) print(result) print(list(result)) print(tuple(result)) # <map object at 0x7fe83809dd30> # [20, 10, 60, 200, 1800, 1332] # ()
装饰器
一、装饰器前提
一、Python中装饰器是为了从已经存在的函数体上添加新功能,但是不队员函数体做修改。
二、为什么函数能够被装饰?
- 函数能够作为参数传给另一个函数。
- 函数A还可以将函数B作为其返回值。
- 《流畅的Python》中说道:”Python在的函数是一等公民。“
案例一:函数能够作为参数传给另一个函数。
def func1(a, b):
print('[函数func1]正在被执行')
return a + b
def func2(func, c, d):
print('[函数func2]正在执行')
return func(c, d)
# 分别调用函数
print(func1(1, 2))
print(func2(func1, 1, 2))
案例二:函数内部还可以再创建函数。
def func3(a, b):
print('[函数func3]正在执行')
def func4():
print('[函数func4]正在执行')
return a + b
print(func4())
func3(10, 20)
案例三:函数A还可以将函数B作为其返回值。
def A(a, b):
print('[函数A]正在执行')
def B(c):
print('[函数B]正在执行')
return a + b + c
return B
B = A(10, 20)
print(B)
print(B(30))
二、无参装饰器
案例:在一个已经实现1-100的求和功能的函数上,外加功能进行性能测试。
import time
# 测试函数
def inner(func):
def outer(*args, **kwargs):
start1 = time.time()
result = func(*args, **kwargs)
end1 = time.time()
print(f'程序执行花费{end1 - start1}秒')
return result
print(outer)
return outer
# 功能函数
def numSum(start, end):
total = 0
for i in range(start, end + 1):
total += i
return total
numSum = inner(numSum) # 函数numSum等于变量numSum传递给形参func,inner执行,先打印outer变量代表的函数,再将outer函数返回,重新给numSum赋值
print(numSum) # 打印numSum,等价于打印outer
print(numSum(1, 100)) # 调用numSum等于调用outer变量等于调用outer函数
# 传参1和100,因为装饰器是具有通用性的,所以一个装饰器可以借用给任何函数,所有的函数传参不一定一致,所以使用不定长参数*args和**kwargs接受参数。
# 在outer函数内部有调用了func函数,func函数就是一开始的numSum,将传递的所有参数(*args,**kwargs)再传递给func函数,此时才开始真正的调用功能函数numSum
# 最终既能得到功能函数应该得到的结果,有能够测试成功功能函数的性能。
# 可以将测试函数inner写为语法糖的形式:@测试函数名
# 功能函数
@inner
def numSum1(start, end):
total = 0
for i in range(start, end + 1):
total += i
return total
print(numSum1(1, 50))
# 无参装饰器标准写法
import time
def inner(func):
def outer(*args, **kwargs):
start1 = time.time()
result = func(*args, **kwargs)
end1 = time.time()
print(f'程序执行花费{end1 - start1}秒')
return result
return outer
@inner
def numSum1(start, end):
total = 0
for i in range(start, end + 1):
total += i
return total
print(numSum1(1, 100))
三、有参装饰器
- 有参装饰器:可以在函数体执行功能时再多做验证(Web权限验证)
案例
def limit(kind):
def inner(func):
def outer(*args, **kwargs):
result = func(*args, **kwargs)
if result == '登陆成功':
if kind == '会员':
return result, '尊敬的vvvvvvip用户'
else:
return result, '感谢白嫖用户登录'
else:
return result
return outer
return inner
# 语法糖的参数是登陆账号时程序同时验证账号权限得到的结果
@limit('非会员')
def userLogin(username, pwd):
if username == 'admin' and pwd == '123456':
return '登陆成功'
else:
return '登录失败'
print(userLogin('admin', '123456'))