回顾一下函数的知识,我列出了函数知识的提纲,可以根据提纲回忆复习一下:
1.函数传参的方式——关键字传参和按顺序传参
2.默认参数
3.传参顺序——一般先位置传参再关键字传参
4.指定传位置参数——/
5.指定传关键字参数——*
6.参数打包成元组——在形参前加*
7.多参数返回解包操作
8.元组打包传参后面的参数必须用关键字参数
9.参数打包成字典——在形参前加**
10.解包元组,然后传参
11.解包字典,然后传参
12.LEGB规则——多层函数变量调用规则
13.global关键字——调用全局变量的关键字
14.nonlocal关键字——调用外层变量的关键字
15.
def myfunc1(s, vt, o):
return "".join((o, vt, s))
print(myfunc1("我", "爱上", "Python"))
print(myfunc1(o="我", vt="爱上", s="Python"))
# 两种传参方式
def myfunc2(s, vt , o="Study"):
return "".join((s, vt ,o))
print(myfunc2("我", "爱"))
print(myfunc2("我", "爱", "Pyhton"))
# 默认参数
def myfunc3(vt, s="苹果", o="我"):
return "".join((o, vt, s))
print(myfunc3("吃了"))
print(abs(-1.5))
print(sum([1, 2, 3], start=4))
#! print(abs(x = -1.5))是不被允许的,会报错
#! 同理,sum(start=4, [1, 2, 3])也是会报错的
# 关键字传参在位置传参后面
def abc1(a, /, b, c):
print(a, b ,c)
abc1(1, 2, 3)
abc1(1, b = 2, c = 3)
# 函数形参有/,说明在/之前的参数在传参时必须是位置参数而不能是关键字参数
def abc2(a, *, b, c):
print(a ,b ,c)
abc2(1, b = 2, c = 3)
abc2(a = 4, b = 5, c = 6)
# 函数形参有*,说明在*之后的参数传参时必须是关键字参数
def myfunc4(*args):
print("有{}个参数".format(len(args)))
print("第2个参数是:{}".format(args[1]))
print(args)
print(type(args))
myfunc4(1, 2, 3, 4, 5)
# 在参数前加*,可以将传入的位置参数打包成一个元组
def myfunc6():
return 1, 2, 3
print(myfunc6)
x, y, z = myfunc6()
print(x, y, z)
# 多参数返回,解包操作
def myfunc8(*args, a, b):
print(args, a, b)
myfunc8(1, 2, 3 ,a = 4, b = 5)
# 如果出现了打包成元组的操作,那么后面参数传参时必须用关键字参数传参,以区分元组打包传参
def myfunc9(**kwargs):
print(kwargs)
myfunc9(a = 1, b = 2, c = 3)
# 参数前出现**,那么就是字典打包传参,传参时要按照:键=值的方式传参,那么就会在函数内新建一个字典,键值对就是我们传参时候设置的形式。
def myfunc10(a, *b, **c,d):
print(a, b, c, d)
myfunc10(1, 2, 3, 4, x = 5, y = 6, d = 8)
# 综合运用,同理,字典打包传参后面的参数同样必须用关键字传参
def myfunc11(a, b, c, d):
print(a, b, c, d)
args = (1, 2, 3, 4)
myfunc11(*args)
# 传参时用*代表解包元组
kwargs = {'a':1, 'b':2, 'c':3, 'd':4}
myfunc11(**kwargs)
# 使用**对传参时的字典进行解包,此时值会被保留下来,键会被舍弃
#* LEGB规则,python变量引用顺序:从当前作用域开始寻找变量,如果没找到就往上一层作用域寻找,
#* 没找到就再上一层。
#TODO 具体步骤:当前作用域局部变量->外层作用域变量->再外层作用域变量->…->当前模块全局变量->pyhton内置变量
x = 880 #* 全局变量
def myfunc12():
x = 770 #*局部变量
print(x, id(x))
myfunc12()
print(x, id(x))
#TODO 使用global关键字,可以调用全局变量,甚至可以在函数内部创建全局变量
x = 250
def myfunc13():
global x, y
#TODO 调用全局变量x,创建全局变量y
x = 260
y = 250
print(x, y, id(x), id(y))
myfunc13()
print(x, y, id(x), id(y))
#TODO 使用nonlocal关键字,可以在嵌套的函数内部修改外层函数的变量,
#! 但是前提是外层函数定义了某个变量,不然使用nonlocal会因为找不到外层函数的变量而报错
def myfunc14():
x = 250
#* myfunc14内的变量x
def myfunc15():
nonlocal x
#TODO 调用myfunc14内的变量x,如果x未定义,则报错
#! 倘若没有这句nonlocal,那么会在内层函数内新建一个x变量,但是外层函数的x依然没有得到改变
#! 也就是说想要修改外层函数的变量,要用这些关键字,不然根据LEGB规则,即使同名,先使用的依然是内层函数的变量x
x = 260
print(x, id(x))
myfunc15()
print(x, id(x))
myfunc14()
#! 当然,不使用nonlocal关键字,也可以在内层函数中调用外层函数的变量,但是不能修改,参考下面的power函数,原理是LEGB规则
#* 什么是闭包?
#* 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包
#* 闭包就像是一个流水线,而闭包的外层函数是一个工厂,通过给外层函数传递参数,来确定工厂的流水线
#TODO 考虑下面的例子,power是次方函数,接收参数exp,exp=2就是求2次方,exp=3就是求三次方,
#TODO 外层函数作为工厂,决定加工性质,而内层函数闭包作为流水线,将产品生产出来
#* 闭包,使得我们能够使用内层函数
def power(exp):
def exp_of(base):
return base ** exp
return exp_of
square = power(2)
cube = power(3)
print(square(2), square(5), cube(2), cube(5))
#* 通过将工厂power赋一参数,得到流水线————内层函数,然后赋值给变量,
#* 这时,变量就是内层函数,也就是流水线,然后再进而给内层函数赋值,得到产品————结果
#* 内层函数具有记忆外层函数作用域的特性,因此可以用来记忆外层函数的变量
#* 通过nonlocal实现记忆外层函数变量的功能
def outer():
x = 0
y = 0
def inner(x1, y1):
nonlocal x, y
x += x1
y += y1
print("现在,x = {}, y = {}".format(x, y))
return inner
move = outer()
move(1, 2)
move(-2, 2)
#* 可以将函数作为参数传递给函数
def myfunc():
print("正在调用myfunc...")
def report(func):
print("我要开始调用函数了...")
func()
print("调用函数完毕")
report(myfunc)
#? 什么是装饰器?有一个目标函数,这个函数是我们想要调用的,
#? 装饰器就是创建一个闭包,然后对目标函数进行加工修饰,
#? 达到不改动目标函数的同时,增加函数的功能
#TODO 所以装饰器是通过闭包+函数作为参数来实现的
import time
def time_master(func):
#* 闭包第一层函数参数用来接收外界函数
def call_func():
print("开始运行程序...")
start = time.time()
func()
stop = time.time()
print("结束程序运行...")
print("一共耗费了{:.12f}秒".format(stop - start))
return call_func #* 返回内层函数
@time_master #*通过@,我们将@下面的目标函数进行@内容的装饰,增加函数功能
#! @time_master本质是:myfunc15 = time_master(myfunc15)
#! 所以,装饰器的本质就是将以定义好的函数myfunc15传入闭包内加工装饰成新的函数,
#! 然后返回给myfunc15自己,那么myfunc15本身就相当于给自己加了装饰。
def myfunc15():
time.sleep(2)
print("I love Python")
myfunc15()
#* 多重装饰,装饰顺序为从下至上
def add(func):
def inner():
x = func()
return x + 1
return inner
def cube(func):
def inner():
x = func()
return x * x * x
return inner
def square(func):
def inner():
x = func()
return x * x
return inner
@add
@cube
@square
def test():
return 2
print(test())
#* 如果想给装饰器传入参数,那么我们可以给装饰器外层,即闭包外层再嵌套一个函数
#* 这个函数专门接收参数
def logger(msg):
def time_counter(func):
def call_func():
start = time.time()
func()
stop = time.time()
print("小{0}睡了{second:.2f}秒".format(msg, second = stop - start))
return call_func
return time_counter
@logger("A")
def funA():
time.sleep(1)
print("正在调用funA...")
@logger(msg = "B")
def funB():
time.sleep(1)
print("正在调用funB...")
funA()
funB()
squareX = lambda x : x * x
print(squareX(3))
print(type(squareX))
y = [lambda y: y * y, 2, 3]
print(y[0](y[1]), y[0](y[2]))
mapped = map(lambda x : ord(x) + 10, "Python")
print(list(mapped))
def counter():
i = 0
while i <= 5:
yield i
#* yield将函数变成生成器函数
#* 使用yield,将会暂停函数,但是函数的进程将会被记住,然后返回一个值,一次调用返回一次值
i += 1
print("生成器:")
print(counter())
#* 当调用生成器函数,会返回生成器对象
for i in counter():
#* 从此我们知道,生成器实际上是一个迭代器,通过for的i每次调用生成器,
#* 相对于启动-关闭-启动-关闭...,由于for循环会自动调用next,因此在print i的时候
#* 会打印生成器的元素
print(i)
c = counter()
print(next(c))
#TODO 通过next可以将生成器的元素打印出来
print("生成器不支持c[2]")
def fib():
back1, back2 = 0, 1
while 1:
yield back1
back1, back2 = back2, (back1 + back2)
f = fib()
print(next(f))
print(next(f))
print(next(f))
print(next(f))
def add(x, y):
"""两数做和
x (int): 其中一个加数
:param y:另外一个加数
:param return:返回和
"""
result = x + y
print(f"两数相加的结果是:{result}")
return result
add(1, 9)