Python函数知识
回顾一下函数的知识,我列出了函数知识的提纲,可以根据提纲回忆复习一下:
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,创建全局变量yx = 260y = 250print(x, y, id(x), id(y))
myfunc13()
print(x, y, id(x), id(y))#TODO 使用nonlocal关键字,可以在嵌套的函数内部修改外层函数的变量,
#! 但是前提是外层函数定义了某个变量,不然使用nonlocal会因为找不到外层函数的变量而报错
def myfunc14():x = 250 #* myfunc14内的变量xdef myfunc15():nonlocal x #TODO 调用myfunc14内的变量x,如果x未定义,则报错#! 倘若没有这句nonlocal,那么会在内层函数内新建一个x变量,但是外层函数的x依然没有得到改变#! 也就是说想要修改外层函数的变量,要用这些关键字,不然根据LEGB规则,即使同名,先使用的依然是内层函数的变量xx = 260print(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 ** expreturn exp_of
square = power(2)
cube = power(3)
print(square(2), square(5), cube(2), cube(5))
#* 通过将工厂power赋一参数,得到流水线————内层函数,然后赋值给变量,
#* 这时,变量就是内层函数,也就是流水线,然后再进而给内层函数赋值,得到产品————结果#* 内层函数具有记忆外层函数作用域的特性,因此可以用来记忆外层函数的变量
#* 通过nonlocal实现记忆外层函数变量的功能
def outer():x = 0y = 0def inner(x1, y1):nonlocal x, yx += x1y += y1print("现在,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 + 1return inner
def cube(func):def inner():x = func()return x * x * xreturn inner
def square(func):def inner():x = func()return x * xreturn 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_funcreturn 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 = 0while 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, 1while 1:yield back1back1, 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 + yprint(f"两数相加的结果是:{result}")return result
add(1, 9)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
