python之函数

1、函数基本概念
1.1、什么是函数

具备某一功能的工具/集合体称之为函数
事先准备工具的过程称之为函数的定义
遇到应用场景拿来就用称之为函数的调用

1.2、为何要有函数

函数分为两大类:内置函数、自定义函数

内置函数(python3解释器定义好的,我们只需要拿来使用即可)
优势:拿来主义,提升开发效率

自定义函数:(把程序中实现某一功能一块代码组织整理到一起)
优势:
1、增强程序的组织结构性、提升可读性
2、减少代码冗余
3、提升程序的可维护性与扩展性

1.3、如何使用函数
使用原则:先定义,后调用

定义函数的语法:
def 函数名(参数1, 参数2, 参数3...):
       """文档注释"""
       代码1
       代码2
       代码3
       ...
       return 返回值

调用函数的语法:
函数名(1,2,3...)
1.4、函数的使用分为两个阶段
定义阶段:申请内存空间把函数体代码放进去,然后把内存地址绑定给函数名 => 函数名=函数的内存地址

调用阶段:通过函数名找到函数体代码,加括号触发函数体代码运行
函数名() => 函数的内存地址(),会触发函数体代码的运行

函数的使用一定要分两个阶段去看
1、定义阶段: 只检测语法,不执行代码
2、调用阶段: 执行函数体代码

如果发生的是语法错误,定义阶段就会立马检测出来
如果发生的不是语法错误,而是逻辑错误,只能在调用阶段检测到

# 示范一
def bar():
    print('from bar')
    
def foo():
    print('from foo')
    bar()

foo()

# 示范二
def foo():
    print('from foo')
    bar()  # 定义foo的阶段不会执行函数体代码,即不会执行bar函数
           # 在调用foo的时候bar已经定义了 所以不会报错

def bar():
    print('from bar')

foo()
1.5、函数定义的三种形式
# 1、无参函数
def test():
	print('这是一个测试函数')

# 2、有参函数
# 例:计较两个数字的大小
def max2(x, y):
    if x > y:
        print(x)
    else:
        print(y)
max2(10, 11)
max2(20, 21)

# 3、空函数(两种定义方式)
def register():
	pass

def login():
	...
1.6、函数调用的三种形式
# 1、语句形式:单纯的调用
# 使用内置函数
print("hello world")
# 使用自定义函数
def test():
	print('这是一个测试函数')
test()

# 2、表达式方式的调用
def max2(number1, number2):
    if number1 > number2:
        return number1
    else:
        return number2
print(max2(10, 11) * 12) # 132 拿到函数的返回值与12相乘
# 等价于:
x = max2(10, 11)
print(x * 12)

# 3、可以当做参数传给另外一个函数
def max2(number1, number2):
    if number1 > number2:
        return number1
    else:
        return number2
res = max2(max2(10, 11), 12) # 拿到前两者的最大值与第三个继续比较
print(res)
1.7、函数的返回值
# 1、函数体内没有return或return后没有值:默认返回是None
def func():
    # return None
    pass
res = func()
print(res)

# 2、return 值:返回的就是这一个值
def func():
    return 123
res = func()
print(res)

# 3、return 值1,值2,值3:返回一个小元组(值1,值2,值3)
def func():
    return 123, "abc", 3.1
res = func()
print(res, type(res))

# 补充:函数内可以有多个return,return是函数结束的标志,函数体代码但凡运行到一个return就会立刻终止整个函数的运行,然后将return后的值当做本次调用的产品返回
def func():
    print("first")
    return 111
    print('second')
    return 2222
    print('third')
    return 3333
res = func()
print(res) # first 111

def func():
    while True:
        while True:
            return
res = func()
print(res)

函数可以有三种形式的返回值
1return: 返回的就是该值本身
2return1,2,3: 返回一个元组
3、没有return: 默认返回None
1.8、函数的参数分类
函数的参数分为两大类
1、形参:在定义函数阶段,括号内指定的参数,称之为形式参数,简称形参
形参相当于定义在函数内的变量名,用于接收外部传进来的值
def func(x, y):
    # x=10
    # y=11
    print(x)
    print(y)

2、实参:在调用函数阶段,括号内传入的值,称之为实际参数,简称实参
实参相当于变量值,用于为形参赋值的
func(10, 11)

ps:在函数调用时,会将实参的值绑定给形参,这种绑定关系只能在函数内使用,在函数调用完毕后,实参与形参会解除绑定,回收占用的内存空间
2、函数参数
2.1、第一组形参与实参
2.1.1、位置形参、位置实参
1、位置形参:在函数定义阶段按照从左到右的顺序依次定义形参(变量名)
特点:必须被传值,多一个不行少一个也不行
def func(x, y):
    print(x, y)
func(1, 2)
func(1)  # 少一个不行
func(1, 2, 3)  # 多一个也不行

2、位置实参:在函数调用阶段按照从左到右的顺序依次定义实参(传入的变量值)
特点:按照位置与形参一一对应
def func(x, y):
    print(x, y)
func(1, 2)
func(2, 1)
2.1.2、默认参数(默认形参)、关键字实参
1、默认参数
在函数定义阶段就已经为某个形参赋值,该形参称之为有默认值的形参,简称默认形参
特点: 定义阶段就已经被赋值意味着在函数调用阶段可以不用为其赋值
def func(x, y=1111):
    print(x, y)
func(1) # 不传y的值使用默认参数
func(1, 222) # 传递y的值,以实参为准

2、关键字实参:按照key=value的形式为指定的形参传值
特点:指名道姓为某个形参传值,可以完全打乱顺序
def func(x, y):
    print(x, y)
func(1, 2)
func(y=2, x=1)
2.1.3、位置实参与关键字实参总结
可以混用位置实参与关键字实参
注意:
1、不能对同一个形参重复赋值
def func(x, y):
    print(x, y)
func(1,2,x=111) # 报错

2、关键字实参必须跟在位置实参的后面
def func(x, y):
    print(x, y)
func(1, y=2)
func(y=2, 1) # 报错
3.1.4、位置形参与默认参数(默认形参)总结
1、位置形参必须放在默认形参的前面
def func(y=1, x): # 报错
    print(x, y)
    
2、默认参数的值是在函数定义阶段被赋值的,准确地说被赋予的是值的内存地址
m = 2
def func(x, y=m):  # y=>2的内存地址
    print(x, y) # 1 2
m = 333
func(1)

3、虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型
函数最理想的状态:函数的调用只跟函数本身有关系,不外界代码的影响
函数的调用彼此之间应该做到没有关联,所以说默认形参通常应该是不可变类型
l = []
def func(name, hobby, hobbies=l):
    hobbies.append(hobby)
    print('%s的爱好是%s' % (name, hobbies))  # allen的爱好是['阅读']
										    #  lily的爱好是['阅读', '羽毛球']
                                            #  lucy的爱好是['阅读', '羽毛球', '游戏']
func('allen', '阅读')
func('lily', '羽毛球')
func('lucy', '游戏')

# 推荐写法(优化):
def func(name, hobby, hobbies=None):  # 默认形参的值通常应该是不可变类型
    if hobbies is None:
        hobbies = []
    hobbies.append(hobby)
    print('%s的爱好是%s' % (name, hobbies))
func('allen', '阅读')
func('lily', '羽毛球')
func('lucy', '游戏')
2.2、第二组形参与实参
2.2.1、可变长度的参数

可变长度的参数指的是在调用函数时,传入的实参个数不固定,而实参是为形参赋值的,所以对应着也应该有新的形参格式来负责接收不固定长度的实参

2.2.2、形参中带*与**
1*形参名:用来接收溢出的位置实参,溢出的位置实参会被*保存成元组的格式然后赋值紧跟其后的形参名
*后跟的可以是任意名字,但是约定俗成应该是args
def func(x, y, *z):
    print(x, y, z)
func(1, 2, 3)
func(1, 2, 3, 4, 5)
func()  # 报错

2**形参名:用来接收溢出的关键字实参,**会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名
**后跟的可以是任意名字,但是约定俗成应该是kwargs
def func(x, y, **z):
    print(x, y, z)
func(1, 2, a=111, b=222, c=333)
2.2.3、形参中带*与**总结
def func(x, y=2, *args, **kwargs):
    print(x, y, args, kwargs)
func(1, )
func(1, 2, 4, 5, 6, 7, a=1, b=2)

def func(*args, **kwargs):
    print(args, kwargs)
func(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)

# 使用*args可以获取传入不固定参数的和
def add(*args):
    res = 0
    for i in args:
        res += i
    print(res)
add(1, 2, 3)
2.2.4、实参中带*与**
1、实参中带*,*后跟的那个值应该是一个可以被for循环遍历的值
*会将后面的那个值打散成位置实参
func(*(111, 222, 333))  # func(111,222,333)
func(*(111, 222))  # func(111,222)
func(*"hello")  # func("h","e","l","l","o")
func(*{"k1": 111, "k2": 222, })  # func("k1","k2")

2、实参中带**,**后跟的那个值应该是一个字典
**会将后面的那个值打散成关键字实参
func(**{"k1": 111, "k2": 222, })  # func(k2=222,k1=111)
func(**{"x": 111, "y": 222, })  # func(y=222,x=111)
func(**[("x", 111), ("y", 222)])  # func(y=222,x=111)
2.2.5、形参与实参中混用*与**
def func(*args, **kwargs):
    print(args, kwargs)
func(1, 2, 3, 4, 5, a=1, b=2, c=3)
func((1, 2, 3, 4, 5), {'a': 1, 'b': 2, 'c': 3})
func(*(1, 2, 3, 4, 5), **{'a': 1, 'b': 2, 'c': 3})  # func(1, 2, 3, 4, 5, a=1, b=2, c=3)
2.2.6、命名关键字参数
命名关键字参数:在定义函数时,*后定义的参数,称之为命名关键字参数
特点:
1、命名关键字实参必须按照key = value的形式为其传值
def func(x, y, *, a, b):  # 其中,a和b称之为命名关键字参数
    print(x, y)
    print(a, b)
func(1,2,b=222,a=111)

# 命名关键字参数可以有默认值,实参中可以不传
def func(x, y, *, a=11111, b):
    print(x, y)
    print(a, b)
func(1, 2, b=22222)

2.组合使用
形参混用的顺序:位置形参,默认形参, *args, 命名关键字形参, ** kwargs
# 模板
def func(x, y=111, *args, z, **kwargs):
    print(x)
    print(y)
    print(args)
    print(z)
    print(kwargs)

# 示例演示
def func(x, y=111, *args, z, a=123, **kwargs):  # z,a必须按照关键字实参的格式为其赋值,a有默认值,可以不传
    print(x)  # 1
    print(y)  # 2
    print(args)  # (3, 4, 5)
    print(z)  # baidu
    print(a)  # 666
    print(kwargs)  # {'b': 'qq'}

func(1, 2, 3, 4, 5, a=666, b='qq', z='baidu')
2.2.7、*与**大总结
形参中带***是汇总操作
实参中带***是打散操作

def index(x, y):
    print(x, y)
    
def wrapper(*args, **kwargs):  # args=(1,2,3,4,5)  kwargs={"a":1,"b":2,"c":3}
    index(*args, **kwargs)

# 直接调用的是wrapper,然后通过wrapper间接调用index
wrapper(1, 2, 3, 4, 5, a=1, b=2, c=3) # 报错 看似是在为wrapper传参,实际是在给index传参,参数必须符合index函数的要求
wrapper(1, 2)
wrapper(y=2, x=1)
wrapper(1, y=2)
# 原格式--->汇总--->打回原形
3、函数对象
3.1、函数对象的具体操作
在python函数是第一类对象,简称函数对象
函数对象指的就是可以把函数当做变量一样去使用

1、可以赋值
def foo():
    print('from foo')
print(foo)
f=foo
print(f is foo)  # True
f()

2、可以当做参数传给另外一个函数
def func(aaa): # aaa=函数foo的内存地址
    print(aaa)
    aaa()
    
def foo():
    print('from foo')
    
func(foo)

3、可以当做函数的返回值
def func(aaa): # aaa=函数foo的内存地址
    return aaa # 返回的是函数foo的内存地址

def foo():
    print('from foo')

res=func(foo)
print(res)
res()

4、可以当做容器类型的元素
def foo():
    print('from foo')

l=[foo]
print(l)
l[0]()

# 应用场景:
# 利用函数对象可以取代if...elif...else的多分支问题
def login():
    print('登录功能...')

def transfer():
    print('转账功能...')

def withdraw():
    print('提现功能...')

def recharge():
    print('充值功能...')

func_dict = {
    '1': [login, '登录'],
    '2': [transfer, '转账'],
    '3': [withdraw, '提现'],
    '4': [recharge, '充值']
}

while True:
    print('0    退出')
    for k, v in func_dict.items():
        print('%s    %s' % (k, v[1]))
    choice = input('请输入指令: ').strip()
    if choice == '0':
        print('退出成功')
        break
    if choice in func_dict:
        func_dict[choice][0]()
    else:
        print('指令不存在...')
4、函数嵌套
4.1、定义与基本使用
函数嵌套定义:在一个函数内又定义了另外一个函数
实例一:
def f1():
    def f2():
        print('from f2')
    f2()
    x = 11111111
    return x
res = f1()
print(res)

实例二:
def f1():
    def f2():
        print('from f2')
    return f2
res = f1()
print(res)  # <function f1.<locals>.f2 at 0x00000142A6B1CC80>
res()  # from f2
4.2、利用函数嵌套计算圆的周长和面积
import math
def circle(radius,mode):
    def perimeter(radius):
        return 2 * math.pi * radius
    def area(radius):
        return math.pi * (radius ** 2)
    # 利用标志位判断
    if mode == 1:
        return perimeter(radius)
    elif mode == 2:
        return area(radius)
print(circle(18,1))
print(circle(18,2))
4.3、函数嵌套调用
函数嵌套调用:在调用一个函数的过程中又调用了其他函数

# 比较最大值
# # 思路: 两两比较 大问题拆解成小问题
def max2(x, y):
    if x > y:
        return x
    else:
        return y

def max4(a, b, c, d):
    res1 = max2(a, b)
    res2 = max2(res1, c)
    res3 = max2(res2, d)
    return res3

print(max4(1, 2, 3, 4))
5、名称空间
5.1、名称空间的定义与分类
名称空间就是存放名字的地方
分类:
1、内置名称空间:存放的是python解释器自带的名字,如lenprintinput
生命周期:解释器启动则创建,解释器关闭就销毁

2、全局名称空间:内置以及函数内的名字之外的名字都存放于全局名称空间中
生命周期:运行顶级代码/主流水线则创建,顶级代码/主流水线结束则销毁
x = 10 # 全局名称空间的名字
y = 12 # 全局名称空间的名字
# foo = 内存地址
def foo():  # foo本身也是全局的
    z = 111
    # f1=内存地址
    def f1():
        pass

if True:
    aaa = 33  # aaa是全局名称空间的名字
    
3、局部名称空间:函数内的名字
生命周期:函数调用时则创建,函数调用完毕则立即销毁
def f1(x, y):
    z = 3
    def f2():
        m=444
        n=5555
    f2()
f1(1, 2)
5.2、名称空间名字的查找顺序
从当前位置往外查找,如果当前是在局部: 局部名称空间 ——> 全局名称空间 ——> 内置名称空间
从当前位置往外查找,如果当前是在全部: 全局名称空间 ——> 内置名称空间

# 站在全局进行查找
input = 111
def f1():
    input = 222
f1()
print(input) # 站在全局找是不会去局部空间找的

# 站在局部进行查找
def f1():
    # input = 222
    print(input) # 111

input = 111
f1()

强调:名称空间的"嵌套关系"是函数定义阶段、也就是检测语法的时候就确定的,与调用为无关
实例一:
x = 111
def f1():
    print(x)  # 111
def f2():
    x = 222
    f1()
f2()

实例二:
def f3():
    print(x)
    x = 111
x=222
f3()  # 报错 UnboundLocalError: local variable 'x' referenced before assignment
6、作用域
6.1、作用域的分类
全局范围/全局作用域: 内置名称空间、全局名称空间
特点: 全局存活,全局有效

局部范围/局部作用域: 局部名称空间
特点: 临时存活,局部有效 
6.2、函数的参数传递
函数的参数传递都是值拷贝
1、对全局定义的不可变类型,不可以在函数内直接修改
x = 10
def func(a):  # a=10的内存地址
    a = 123
func(x)  # x=10的内存地址
print(x)  # 10

2、对全局定义的可变类型,可以在函数内直接修改
x = []
def func(a): # a=列表的内存地址
    a.append(11)
func(x) # x=列表的内存地址
print(x)  # [11]

3、如果在局部想要修改全局的名字对应的值(不可变类型),需要用global
x = 10
l = []
def func():
    global x  # # 声明x这个名字是全局的名字,不要再造新的名字了
    x = 22
    l.append(11)
func()
print(x)  # 22
print(l)  # [11]

4nonlocal:修改函数外层函数包含的名字对应的值(不可变类型)
x=0
def f1():
    x=11
    def f2():
        nonlocal x
        x=22
    f2()
    print('f1内的x:',x)  # f1内的x:22
f1()
7、闭包函数
7.1、闭包函数定义与使用
闭包函数=名称空间与作用域+函数嵌套+函数对象
核心点:名字的查找关系是以函数定义阶段为准
"闭"函数指的该函数是内嵌函数
"包"函数指的该函数包含对外层函数作用域名字的引用(不是对全局作用域),内函数引用了外函数的变量
def outter():
    x = 1111
    def inner():
        print(x)
    return inner  # 千万不要加括号

f = outter()
# print(f)

def f3():
    x = 222222
    f()
f3()
7.2、为函数体代码传参的两种方式
方式一:直接通过参数的方式传入(直接把函数体需要的参数定义成形参)
def func(x):
    print(x)
func(1)
func(2)
func(3)

方式二:通过闭包函数传递参数
def outter(x):
    def func():
        print(x)
    return func  

f1 = outter(1)
f2 = outter(2)
f3 = outter(3)

f1()
f2()
f3()
7.3、针对函数体代码传参两种方式的案例
# 直接通过参数的方式传入
def get(url):
    response = requests.get(url)
    print(len(response.text))
get('https://blog.csdn.net/wcg920212/article/details/106542638')
get('https://blog.csdn.net/wcg920212/article/details/106543644')
get('https://blog.csdn.net/wcg920212/article/details/106543933')

# 通过闭包函数传递参数
def outter(url):
    def get():
        response = requests.get(url)
        print(len(response.text))
    return get

article1 = outter('https://blog.csdn.net/wcg920212/article/details/106542638')
article1()

article2 = outter('https://blog.csdn.net/wcg920212/article/details/106543644')
article2 ()

article3 = outter('https://blog.csdn.net/wcg920212/article/details/106543933')
article3()
8、偏函数
Python 偏函数是通过 functools 模块被用户调用
偏函数 partial 应用
函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用

偏函数是将所要承载的函数作为partial()函数的第一个参数,原函数的各个参数依次作为partial()函数后续的参数,除非使用关键字参数

import functools

def create_pen(c_color, c_type):
    print('创建了{},颜色是{}'.format(c_type, c_color))

penFunc = functools.partial(create_pen, c_type='钢笔')
penFunc('红色')  # 创建了钢笔,颜色是红色
penFunc('绿色')  # 创建了钢笔,颜色是绿色
penFunc('紫色')  # 创建了钢笔,颜色是紫色

pencilFunc = functools.partial(create_pen, c_type='铅笔')
pencilFunc('红色')  # 创建了铅笔,颜色是红色
pencilFunc('绿色')  # 创建了铅笔,颜色是绿色
pencilFunc('紫色')  # 创建了铅笔,颜色是紫色
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值