Bootstrap

10-Python基础编程之函数

概念

写了一段代码实现了某个小功能:然后把这些代码集中到一块,起一个名字;下一次就可以根据这个名字再次使用这个代码块,这就是函数写了一段代码实现了某个小功能:然后把这些代码集中到一块,起一个名字;下一次就可以根据这个名字再次使用这个代码块,这就是函数。

作用:方便代码的重用;分解任务,简化程序逻辑;使代码更加模块化。

函数分为:内建函数;三方函数;自定义函数。

基本使用

简单定义:

def 函数名():
	函数体

函数调用:

函数名()

参数

单个参数

场景:需要动态的调整函数体中某一个处理信息,则可以以参数的形式接收到相关数据。

def 函数名(参数名称):
	函数体(函数体中,可以直接以变量的方式使用该参数)

# 调用
函数体(参数值)

形参和实参的概念:
上述函数定义中,参数名称即为形参;
在调用函数的时候,传递的真实数据,即为实参。

def test(num): # num是形参
    print(num**1)
    print(num**2)
    print(num**3)
test(5) # 5是实参

输出:
5
25
125

多个参数

场景:需要动态的调整函数体中多个处理信息时,则可以以 逗号 做分割,接收多个参数。

def 函数名(参数名称1,参数名称2):
	函数体(函数体中,可以直接以变量的方式使用所有参数)

调用方式:

  1. 函数名(参数1, 参数2,参数3…)
    形参和实参:一一对应。
  2. 函数名(参数名称1=参数1,参数名称n = 参数n…)
    可以指明形参名称称为“关键字参数”,不需要严格按照顺序。
def Sum(num1, num2):
    print(num1,num2)
    print(num1 + num2)
Sum(4,5)
Sum(num2=5,num1=4)

输出:
在这里插入图片描述

不定长参数

场景:如果函数体中,需要处理的数据,不确定长度,则可以以不定长参数的方式接收数据。

方式一:

def 函数名(*args):
	函数体(元组,函数体中,可以直接以元组变量的方式使用该参数)
函数名(参数1,参数2,参数3...)
def mySum(*t):
    print(t, type(t))
    result = 0
    for v in t:
        print(v)
        result += v
    print(result)
mySum(4,5,6,7)

输出:
在这里插入图片描述
方式二:

def 函数名(**dic):
	函数体(字典,函数体中,可以直接以字典变量的方式使用该参数)
函数名(参数名1=参数1,参数名2=参数2,参数名3=参数3...)
def Sum(**kwargs):
    print(kwargs,type(kwargs))
Sum(name="sz",age=12)

输出:
在这里插入图片描述
装包与拆包:
把传递的参数,包装成一个集合,称之为“装包”。
把集合参数,再次分解成单独的个体,称之为”拆包”。

def test(*args):
    print(args)
    # 拆包
    print(*args)
    
test(1,2,3,4)

输出:
(1, 2, 3, 4)
1 2 3 4

# 根据此,我们就能用一个输入来直接进行多个参数的输入
def mySum(a,b,c,d):
    print(a+b+c+d)
def test(*args):
    print(args)
    #拆包
    print(*args)
    #mySum((1,2,3,4))
    #mySum(args[0],args[1],args[2], args[3])
    mySum(*args)
test(1,2,3,4)

输出:
在这里插入图片描述

def mySum(a,b):
    print(a)
    print(b)
def test(**kwargs):
    print(kwargs)
    # 拆包操作
    # 应该使用 **进行拆包操作
    # print(**kwargs)
    mySum(**kwargs)
test(a=1,b=2)

输出:
在这里插入图片描述

缺省参数

当我们使用一个函数的时候,如果大多数情况下,使用的某个数据是一个固定值,或者属于主功能之外的小功能实现:则可以使用默认值 · 这种参数,称为"缺省参数”。

def 函数名(变量名1=默认值1,变量名2=默认值2):
	函数体(函数体中,即使外界没有传递指定变量,也可以使用,只不过值是给定的默认值)

函数名(变量1,变量2) # 此处如果是缺省参数,则可以不填写
def hit(somebody="DB"): # 若不写somebody="DB",只写somebody且函数内不写参数,则会报错
    print("我想打",somebody)
hit() 

输出:
我想打 DB

注意事项

值传递:是指传递过来的,是一个数据的副本;修改副本,对原件没有任何影响。
引用传递:是指传递过来的,是一个变量的地址通过地址,可以操作同一份原件。

但在Python当中,只有引用传递(地址传递)。
在这里插入图片描述
但是如果数据类型是可变类型,则可以改变原件;如果数据类型是不可变类型(在Python中不可变的数据类型有3种,分别是整型、字符串和元组),则不可以改变原件。

  1. 首先我们看看不可变的:
    在这里插入图片描述
    发现他的地址是不一样的
    在这里插入图片描述
  2. 来看看可变的:
    在这里插入图片描述
    发现他的地址是一样的
    在这里插入图片描述

返回值

场景:当我们通过某个函数,处理好数据之后,想要拿到处理的结果。

# 语法
def 函数():
	函数体
	return 数据

注意:return的后续代码不会被执行;只能返回一次;如果想要返回多个数据,可先把多个数据包装成一个集合,整体返回。

示例:

# 对于单个返回
def mySum(a,b):
    result = a+b
    return result

res = mySum(6,7)
print(res)

# 对于多个返回
def caculate(a,b):
    he = a+b
    cha = a-b
    return (he,cha)

he,cha=caculate(6,7)
res = caculate(6,7)
print(res[0])
print(res[1])
print(he,cha)

输出:
13
13
-1
13 -1

使用描述

当我们编写三方函数,为了方便他人使用,就需要描述清楚我们所写的函数功能以及使用方式等信息。

# 定义格式
''' 直接在函数体的最上面,添加三个双引号对注释 '''
def 函数():
	"""
	这里写帮助信息
	"""

查看函数使用文档:help(函数)

一般的函数描述包括:

  1. 函数的功能
  2. 参数(含义、类型、是否可以省略、默认值)
  3. 返回值(含义、类型)

示例:
在这里插入图片描述

偏函数

概念:当我们写一个参数比较多的函数时,如果有些参数,大部分场景下都是某一个固定值,那么为了简化使用,就可以创建一个新函数,指定我们要使用的函数的某个参数,为某个固定的值,这个新函数就是“偏函数”。

# 法1:自己写一个新的
def test(a,b,c,d=1):
    print(a+b+c+d)
def test1(a,b,c=1,d=4): # 偏函数
    test(a,b,c,d)
test1(1,2)

# 法2:利用newFunc = functools.partial(函数, 特定参数=偏值)
import functools
newFunc = functools.partial(test,c=1) # 指定函数,和什么值做偏值
print(newFunc, type(newFunc))
newFunc(1,2)

输出:
在这里插入图片描述

示例:

# 利用偏函数
# 在往后的一段时间内,我都需要把一个二进制的字符串,转换成为对应的十进制数据
numStr='100100'
import functools
int2 = functools.partial(int, base=2)
print(int2(numStr))

输出:36

高阶函数

概念:当一个函数A的参数,接收的又是另一个函数时,则把这个函数A或为是“高阶函数”。

# 函数本身可以作为数据传递给另一个变量
def test(a,b):
    print(a + b)
print(test)
print(id((test)))
test2 = test
test2(1,2)

输出:
在这里插入图片描述

# 拿sorted举例
l=[{"name":"sz","age":18},{"name": "sz2", "age": 19},{"name":"SZ3","age" : 18.5}]
def getKey(x):
    return x["age"]
result = sorted(l, key=getKey) # key指定一个函数
print(result)

输出:
[{‘name’: ‘sz’, ‘age’: 18}, {‘name’: ‘SZ3’, ‘age’: 18.5}, {‘name’: ‘sz2’, ‘age’: 19}]

示例:
计算两个动态数据(利用回调函数)

# caculate称为高阶函数
def caculate(num1, num2, caculateFunc): # 参数caculateFunc接受的就是另外一个函数
    result = caculateFunc(num1, num2)
    print(result)
def sum(a, b):
    return a + b
def jianfa(a, b):
    return a-b
caculate(6,2,jianfa)

输出:4

返回函数

概念:是指一个函数内部,它返回的数据是另外一个函数,把这样的操作成为“返回函数”。

# 根据不同参数,获取不同操作,做不同计算
def getFunc(flag):
    # 1.再次定义几个函数
    def sum(a,b,c):
        return a+b+c
    def jian(a,b,c):
        return a-b-c
    # 2.根据不同的flag值,来返回不同的操作函数
    if flag == "+":
        return sum
    elif flag == "-":
        return jian
result = getFunc("+")
# print(result, type(result))
res =result(1,3,5)
print(res)

输出:9

匿名函数

概念:称为lambda函数,指没有名字的函数。

语法结构:lambda 参数1, 参数2… :表达式
注意事项:只能写一个表达式,并且不能直接return;表达式的结果就是返回值;只适用一些简单的操作。

# 1.
result=(lambda x,y:x+y)(1,2)
print(result)
# 2.
newFunc = lambda x,y:x+y
print(newFunc(4,5))
# 3.
l=[{"name":"sz","age":18},{"name": "sz2", "age": 19},{"name":"SZ3","age" : 18.5}]
result = sorted(l, key= lambda x: x["age"]) 
print(result)

输出:
在这里插入图片描述

闭包

概念:在函数嵌套的前提下,内层函数引用了外层函数的变量(包括参数),外层函数又把内层函数当做返回值进行返回。这个内层函数+所引用的外层变量,称为"闭包。
在这里插入图片描述

# 函数定义理解
def test():
    a = 10
    def test2():
        print(a)
    return test2
newFunc = test()
newFunc()

作用:外层函数,根据不同的参数,来生成不同作用功能的函数。
示例:

# 根据配置信息,生成不同的分割线函数
def line_config(content, length):
    
    def line():
        print("-"*(length //2)+ content + "-"*(length // 2))
    return line
line1 = line_config("闭包",40)
line1()

输出:

注意事项:

  1. 闭包中,如果要修改引用的外层变量需要使用nonlocal变量声明,否则当做是闭包内,新定义的变量。
# 查看默认情况
def test():
    num = 16
    def test2():
        num = 666
        print(num)
    print(num) # 查看外部函数num
    test2() # 调用内部函数,查看内部函数输出结果
    print(num) # 调用内部函数后,再次查看外部函数的num,发现并没有改变,说明内部的不会改变
    return test2
result = test()

# 若想在内部改变
def test():
    num = 16
    def test2():
        nonlocal num # 设置为非局部变量
        num = 666
        print(num)
    print(num) # 查看外部函数num
    test2() # 调用内部函数,查看内部函数输出结果
    print(num) # 调用内部函数后,再次查看外部函数的num,发现改变了
    return test2
result = test()

输出:
在这里插入图片描述

  1. 当闭包内,引用了一个,后期会发生变化的变量时,一定要注意
    在这里插入图片描述
def test():
    funcs =[]
    for i in range(1,4):
        def test2(num):
            #num1
            def inner():
                print(num)
            return inner
        funcs.append(test2(i))
    return funcs
newFuncs = test()
print(newFuncs) # 发现是存储了四次函数
newFuncs[0]() # 调用第一个
newFuncs[1]()
newFuncs[2]()

输出:
在这里插入图片描述

装饰器

作用:在函数名以及函数体不改变的前提下,给一个函数附加一些额外代码。

设计思路:

  1. 业务逻辑代码非常多就造成了每一份逻辑代码,在调用具体的功能函数之前都需要去做一个验证,代码冗余度就比较大,代码的复用性比较差,代码的维护性比较差。
  2. 直接在功能函数里面,去修改,方便代码的重用。
  3. 函数名字不能发生改变。
  4. 函数体内部的代码不能发生改变。
  5. 装饰器的执行时间是立即执行。
def check(func):
    def inner():
        print("登录验证操作....")
        func()
    return inner
@check # 在此处就执行了check函数
def fss():
    print("发说说")
# @check 等于操作 fss = check(fss)
fss()

输出:
登录验证操作…
发说说

进阶使用:

  1. 装饰器可以叠加
    从上到下装饰,从下到上执行。
# 装饰器1
def zhuangshiqi_line(func):
    def inner():
        print("-"*20)
        func()
    return inner
# 装饰器2
def zhuangshiqi_star(func):
    def inner():
        print("*"* 30)
        func()
    return inner
@zhuangshiqi_line
@zhuangshiqi_star # 等效于print_content = zhuangshigi_star(print_content)
def print_content():
    print("菜就多练")
print_content()

输出:
在这里插入图片描述

  1. 对有参函数进行装饰:无论什么场景,保证函数调用参数个数一致;为了通用,可以使用不定长参数,结合拆包操作进行处理。
def zsq(func):
    def inner(*args, **kwargs): # 接受随机参数,组为元组和字典
        print("_"* 30)
        print(args, kwargs)
        func(*args, **kwargs) # 打印是要进行拆包的
    return inner
@zsq
def pnum(num, num2, num3):
    print(num,num2,num3)
@zsq
def pnum2(num):
    print(num)
pnum(123,222,num3=666)
pnum2(999)

输出:
在这里插入图片描述

  1. 对有返回值的函数进行装饰:无论什么场景,保证函数返回值一致。
# 对有返回值的函数进行装饰:无论什么场景,保证函数返回值一致。
def zsq(func):
    def inner(*args, **kwargs): # 接受随机参数,组为元组和字典
        print("_"* 30)
        # print(args, kwargs)
        res = func(*args, **kwargs) # 打印是要进行拆包的
        return res
    return inner
    
@zsq
def pnum(num, num2, num3):
    print(num,num2,num3)
    return num+num2+num3
@zsq
def pnum2(num):
    print(num)
    
res1 = pnum(123,222,num3=666)
res2 = pnum2(999)
print(res1)
print(res2)

输出:
在这里插入图片描述

  1. 带参数的装饰器
# 装饰器参数
def getzsq(char):
    # 装饰器
    def zsq(func):
        def inner():
            print(char * 30)
            func()
        return inner
    return zsq

@getzsq("-") # 能够改变其中的前置打印参数
def f1():
    print('666')

f1()

输出:
在这里插入图片描述

生成器

概念:
是一个特殊的迭代器(迭代器的抽象层级更高)。所以, 拥有迭代器的特性:惰性计算数据,节省内存;能够记录状态,并通过next0)函数,访问下一个状态;具备可迭代特性。但是,如果打造一个自己的迭代器,比较复杂需要实现很多方法,所以,就有一个更加优雅的方式"生成器”。

创建方式:

  1. 生成器表达式——把列表推导式的[]修改成()
l=(i for i in range(1,10000000) if i% 2 == 0)
print(l)
print(next(l))
print(next(l))
print(l.__next__())

输出:
在这里插入图片描述

  1. 生成器函数——函数中包含yield语句,这个函数的执行结果就是“生成器”(yield,可以去阻断当前的函数执行,然后,当使用next()数,或者,__ next__()都会让函数继续执行,然后,当执行到下一个yield语句的时候,又会被暂停)。
def test():
    print("xxx")
    yield 1
    print("a")
    yield 2
    print("b")
    yield 'c'
    print("c")
    yield 4
    print("d")
    yield 5
    print("e")
g= test()
print(g)
print(next(g))
print(next(g))
print(next(g))

输出:
在这里插入图片描述

访问方式:

生成器具备可迭代特性:next()函数等价于生成器__next__()或for in

Send方法:
send方法有一个参数,指定的是上一次被挂起的yield语句的返回值相比于__next__()。可以额外的给yield 语句传值(其中注意第一次调用t.send(None))

def test():
    # print("xxx")
    res1=yield 1#"ooo"
    print(res1)
    res2 = yield 2
    print(res2)
g = test()
# print(g.__next__())
# print(g.__next__())
#print(g.send("ooo"))
print(g.send(None))
print(g.send(666))

输出:
1
666
2

关闭生成器:

g.close()
后续如果继续调用,会抛出StopIteration异常提示

def test():
    print("xxx")
    yield 1
    print("a")
    yield 2
    print("b")
    yield 'c'
    print("c")
    yield 4
    print("d")
    yield 5
    print("e")
g= test()
print(g)
print(next(g))
print(next(g))
g.close() #!!!会直接跳出
print(next(g))

输出:
在这里插入图片描述
注意:如果碰到return生成器只会遍历一次,会直接终止,抛出StopIteration异常提示。

递归函数

概念:函数A内部,继续调用函数A体现;传递和回归。

# 比如拿阶乘举例子
def jiecheng(n):
    if n == 1:
        return 1
        #n!= 1
    return n * jiecheng(n-1)
result = jiecheng(4)
print(result)

输出:
24
在这里插入图片描述

函数的作用域

变量的作用域:
指的是变量的作用范围——可操作范围;Python是静态作用域,也就是说在Python中,变量的作用域源于它在代码中的位置;在不同的位置,可能有不同的命名空间。(其中命名空间是 作用域的体现形式;不同的具体的操作范围。)

Python的空间分为:
在这里插入图片描述
在这里插入图片描述

a = 999
def test():
    # 这里如果,直接使用赋值表达式,赋值给一个变量,其实是代表,定义一个新的变量
    a = 6
    print(a)
    def tes2():
        nonlocal a
        a = 77
print(a)
test()
print(a)

输出:
999
6
999

全局改变:

a = 999
def test():
    # 这里如果,直接使用赋值表达式,赋值给一个变量,其实是代表,定义一个新的变量
    global a
    a = 6
    print(a)
print(a)
test()
print(a)

输出:
999
6
6
在这里插入图片描述

a = 999
def test():
    # 这里如果,直接使用赋值表达式,赋值给一个变量,其实是代表,定义一个新的变量
    a = 6
    print(a)
    def tes2():
        nonlocal a
        a = 77
    print(locals())
    print(globals())
# print(a)
test()
# print(a)

输出:
在这里插入图片描述

;