函数

1 函数的意义

函数就相当于具备某一功能的工具

函数可以有效提高代码的复用性,可读性,维护性,可拓展性

函数的使用必须遵循一个原则:
先定义, 后调用

2 函数的语法

函数语法中必要的是def,函数名,():,函数体

可选的是参数,文档描述,return

def 函数名(参数1,参数2,...):
    """文档描述"""
    # 函数体
    return "值"

函数可以有两种产物,一是函数体的执行产物,二是return返回的值

可以使用变量接受return的值

3 函数的定义方式

定义函数发生的事情
1、申请内存空间保存函数体代码
2、将上述内存地址绑定函数名
3、定义函数不会执行函数体代码,但是会检测函数体语法

3.1 无参函数

顾名思义,无参函数就是没有参数的函数,它不需要传入任何参数就能运行

def func():
    print('哈哈哈')
    print('哈哈哈')
    print('哈哈哈')
def interactive():
    name=input('username>>: ')
    age=input('age>>: ')
    gender=input('gender>>: ')
    msg='名字:{} 年龄:{} 性别{}'.format(name,age,gender)
    print(msg)

interactive()
interactive()
interactive()
interactive()

3.2 有参函数

顾名思义,有参函数就是有参数的函数,它必须输入对应的参数才能执行

def func(x,y): # x=1  y=2
    print(x,y)
func(1,2)
def add(x,y):
    res=x + y
    return res 
res=add(20,30)
print(res)

3.3 空函数

空函数函数体代码为pass

空函数一般用于快速搭建框架

def auth_user():
    """user authentication function"""
    pass

def download_file():
    """download file function"""
    pass

def upload_file():
    """upload file function"""
    pass

def ls():
    """list contents function"""
    pass

def cd():
    """change directory"""
    pass

4 函数的调用方式

调用函数发生的事情
1、通过函数名找到函数的内存地址
2、然后加括号就是在触发函数体代码的执行

def func():
    pass
print(func)
# <function func at 0x000002326850DF28>
func()

4.1 只加括号调用函数的形式

interactive()
add(1,2)

这种函数调用形式只有代码执行的产物,没有接收返回值

4.2 表达式形式:

赋值表达式和数学表达式都属于这个类型

这个类型主要是为了接收返回值,并将返回值输出或继续进行别的操作

def add(x,y): # 参数->原材料
    res=x + y
    return res # 返回值->产品
# 赋值表达式
res=add(1,2)
print(res)
# 数学表达式
res=add(1,2)*10
print(res)

4.3 函数调用可以当做参数

这个类型主要是为了用返回值进行别的操作

res=add(add(1,2),10)
print(res)

5 定义与调用

5.1 示例1

示范1代码的执行为

1.定义bar

2.定义foo

3.调用foo,执行print(bar)

4.foo调用bar------------->执行print('from bar')

5.继续执行foo,执行print('from foo')

6.代码结束

# 示范1
def bar(): # bar=函数的内存地址
    print('from bar')
def foo():
    print(bar)
    bar()
    print('from foo')
foo()

# <function bar at 0x00000265609FDF28>
# from bar
# from foo

5.2 示例2

示范2代码的执行为

1.定义foo

2.定义bar

3.调用foo,执行print(bar)

4.foo调用bar------------->执行print('from bar')

5.继续执行foo,执行print('from foo')

6.代码结束

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

def bar():  # bar=函数的内存地址
    print('from bar')

foo()
# <function bar at 0x00000265609FDF28>
# from bar
# from foo
# <function func at 0x00000265609FDEA0>

5.3 示例3

示范3代码的执行为

1.定义foo

2.调用foo,执行print(bar)

3.由于未定义bar,报错

4.定义bar-------未执行

# 示范3
def foo():
    print(bar)
    bar()
    print('from foo')
foo()
def bar():  # bar=函数的内存地址
    print('from bar')

# NameError: name 'bar' is not defined

6 函数返回值

return是函数结束的标志,即函数体代码一旦运行到return会立刻
终止函数的运行,并且会将return后的值当做本次运行的结果返回:

6.1 返回None:

返回None的三种情况:

  1. 函数体内没有return

  2. return

  3. return None

6.2 返回一个值:return 值

def func():
    return 10

res=func()
print(res)

6.3 返回多个值:

用逗号分隔开多个值,会被return返回成元组

def func():
    return 10, 'aa', [1, 2]

res = func()

print(res, type(res))
# (10, 'aa', [1, 2]) <class 'tuple'>

7 函数的参数

7.1 参数的类型

形参:在函数定义阶段定义的参数称之为形式参数,简称形参,相当于变量名

def func(x, y):  # 定义阶段中的x,y全都是形参
    print(x, y)  # 定义阶段中的x,y全都是形参

实参:在调用函数阶段传入的值称之为实际参数,简称实参,相当于变量值
func(1,2)

实参: 调用函数时传入的值,但值可以是以下形式

# 形式一:直接传值
func(1,2)

# 形式二:传入变量值
a=1
b=2
func(a,b)

# 形式三:传入函数的结果
func(int('1'),2)
func(func1(1,2,),func2(2,3),333)

形参与实参的关系:
1、在调用阶段,实参(变量值)会绑定给形参(变量名)
2、这种绑定关系只能在函数体内使用
3、实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系

7.2 形参

形参是在函数定义阶段定义的参数称之为形式参数,相当于变量名

可以分为位置参数,关键字参数,默认参数,不定长参数

7.2.1 位置参数

位置参数:在函数定义阶段,按照从左到右的顺序依次定义的参数称之为位置参数

特点:每个位置参数都必须被传值,多一个不行少一个也不行

def func(x,y):
    print(x,y)
func(1,2)	# 1,2
func(1,2,3) # 报错
func(1,)    # 报错

7.2.2 默认参数

默认形参:在定义函数阶段,就已经被赋值的形参,称之为默认参数
特点:在定义阶段就已经被赋值,在调用阶段可以不用为其传参

def func(x,y=3):
    print(x,y)
    
func(x=1)
# 1,3
func(x=1,y=44444)
# 1,44444

# 默认参数的运用场景: 
# 某个参数一般都为同一个值,很少改变时可以用默认参数减少调用时的传参数
def register(name,age,gender='男'):
    print(name,age,gender)

register('三炮',18)
register('二炮',19)
register('大炮',19)
register('没炮',19,'女')

7.2.3 位置形参与默认形参混用

位置形参与默认形参混用时必须遵守的规则:

1、位置形参必须在默认形参的左边

def func(y=2,x):
    pass
# 报错

2、默认参数的值是在函数定义阶段被赋值的,准确地说被赋予的是值的内存地址

# 示范1:
m=2
def func(x,y=m):
    print(x,y)
# 默认参数y此时指向2的内存地址,而不是m的值
m=3333
func(1)
# 输出1,2

# 示范2:
m = [1, ]
def func(x, y=m): 
    print(x, y)
# 默认参数y此时指向[111111, ]的内存地址
m.append(3)
# 在该列表中增加值,未改变内存地址
func(1)
# 输出1,[1,3]

3、默认参数不使用可变数据类型,如有需求可以将可变数据类型写入函数体中

虽然默认值指定为任意数据类型不会报错,但是不推荐使用可变类型

函数最理想的状态:

函数的调用只跟函数本身有关系,不外界代码的影响

使用可变数据类型可能导致意外情况出现

# 如有需求可以将可变数据类型写入函数体中
def func(x,y,z,l=None):
    if l is None:
        l=[]
    l.append(x)
    l.append(y)
    l.append(z)
    print(l)

func(1,2,3)
# [1,2,3]

new_l=[111,222]
func(1,2,3,new_l)
# [111,222,1,2,3]

7.2.4 可变长度的形参(*args和**kwargs)

可变长度的形参可以接收所有传入的多余的实参

其中*args用来接收所有传入的多余的位置参数

**kwargs用来接收所有传入的多余的关键字参数

7.2.4.1 可变长度的位置参数

*+形参名:用来接收溢出的位置实参,*会把溢出的位置实参保存成元组的格式然后传给紧跟其后的形参名

在调用函数的过程中,溢出的位置实参会被*保存成元组的格式然后传给紧跟其后的形参名
*后跟的可以是任意名字,但是约定俗成应该是args,即*args

def func(x,y,*z): # z =(3,4,5,6)
    print(x,y,z)
func(1,2,3,4,5,6)
# 1,2,(3,4,5,6)

def my_sum(*args):
    res=0
    for item in args:
        res+=item
    return res

res=my_sum(1,2,3,4)
print(res)
# 10

*可以用在实参中,实参中带*,会先把*后的值打散成实参

如把列表,字符串,元组变成多个独立的元素按位置传入参数

def func(x,y,z):
    print(x,y,z)

func(*[11,22,33]) # func(11,22,33)
func(*[11,22]) # func(11,22)

l=[11,22,33]
func(*l)

形参与实参中都使用*的情况

def func(x,y,*args): # args=(3,4,5,6)
    print(x,y,args)

func(1,2,[3,4,5,6])	 	 # 此时为传入3个位置参数,分别为1,2,列表[3,4,5,6]
# 1 2 ([3, 4, 5, 6],)	 # 这是不打散,打包

func(1,2,*[3,4,5,6]) 	 # 此时为传入6个位置参数,func(1,2,3,4,5,6)
# 1 2 (3, 4, 5, 6)	 	 # 这是打散,再打包

func(*'hello') 		 	 # 传入5个位置参数func('h','e','l','l','o')
# h e ('l', 'l', 'o')	 # 这是打散,再打包

7.2.4.2 可变长度的关键字参数

**+形参名:用来接收溢出的关键字实参,**会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名
**后跟的可以是任意名字,但是约定俗成应该是kwargs,即**kwargs

def func(x,y,**kwargs):
    print(x,y,kwargs)

func(1,y=2,a=1,b=2,c=3)
# 1 2 {'a': 1, 'b': 2, 'c': 3}

**可以用在实参中(**后跟的只能是字典),实参中带**,会把**后的字典打散成独立的关键字实参,即key=value的形式

def func(x,y,z):
    print(x,y,z)

func(*{'x':1,'y':2,'z':3}) # func('x','y','z')
func(**{'x':1,'y':2,'z':3}) # func(x=1,y=2,z=3)

# 错误演示
func(**{'x':1,'y':2,}) # func(x=1,y=2)
func(**{'x':1,'a':2,'z':3}) # func(x=1,a=2,z=3)

形参与实参中都使用**的情况:

def func(x,y,**kwargs):
    print(x,y,kwargs)

func(y=222,x=111,a=333,b=444)	
# 传入了4个关键字参数,其中ab为多余的关键字参数,打包成字典
# 111 222 {"a": 333, "b": 444}

func(**{'y':222,'x':111,'a':333,'b':4444}) 
# 传入了4个关键字参数,其中ab为多余的关键字参数,打包成字典
# 先打散,再打包
# 111 222 {"a": 333, "b": 4444}

7.2.4.2 可变长度的参数混用

混用*与**时必须遵守的规则:

*args必须在**kwargs之前

def func(a,*args,**kwargs):
    print(args)
    print(kwargs)

func(1,2,3,4,5,6,7,8,x=1,y=2,z=3) 
# 传入8个位置参数,其中7个为多余的,打包成元组(2,3,4,5,6,7,8)
# 传入3个关键字参数,全为多余的,打包成字典{"x":1,"y":2,"z":3}
# 运行结果:
# (2,3,4,5,6,7,8)
# {"x":1,"y":2,"z":3}
def index(x,y,z):
    print('index=>>> ',x,y,z)

def wrapper(*args,**kwargs): #args=(1,) kwargs={'z':3,'y':2}
    index(*args,**kwargs)
    # index(*(1,),**{'z':3,'y':2})
    # index(1,z=3,y=2)

wrapper(1,z=3,y=2) 
# 为wrapper传递的参数是给index用的
# 传入wrapper的参数都为多余的,打包成(1,)和{"z":3,"y":2}
# 执行index(*args,**kwargs)时为实参,即*将实参元组打散,**将字典打散,即得1,z=3,y=2
# 传入index 得到结果1,2,3
# 原格式--->打包---->打散
# 原格式--->汇总---->打回原形

7.2.5 命名关键字参数

命名关键字参数:

在定义函数时,*后定义的参数,称之为命名关键字参数

特点:
1、必须按照key=value的形式才能为命名关键字参数传值,按照位置传参数时全会被*拦截,不可能传参数到命名关键字参数

2、传参时不要求顺序

3、不设定默认值时必须传值

def func(x,y,*,a,b): # 其中,a和b称之为命名关键字参数
    print(x,y)
    print(a,b)

func(1,2,b=222,a=111)
# 1,2
# 111,222

可以用key=value的形式为命名关键字参数设定默认值

设定默认值后可以不用传参数

def func(x,y,*,a=11111,b):
    print(x,y)
    print(a,b)

func(1,2,b=22222)
# 1,2
# 11111,22222

7.2.6 所有形式参数混用

形参混用的顺序:

位置新参,默认形参, *args, 命名关键字形参,**kwargs

def func(x,y=111,*args,z,**kwargs):
    print(x,y,args)
    print(z,kwargs)

7.3 实参

实参是调用函数时传入的值

默认参数可以不传入参数

实参可以通过以下方法传参:

1.按位置传参

2.按关键字传参

3.位置,关键字混合传参

7.3.1 按位置传参

按位置传参: 按照从左到右的顺序依次传入参数
特点:必须按照顺序,与形参一一对应

def fun(a,b):
    print(a,b)
func(1,2)
# 1-->a ,   2-->b
# 1,2
func(2,1)
# 2-->a ,   1-->b
# 2,1

7.3.2 按关键字传参

按关键字传参:按照key=value的形式传入参数
特点:指名道姓给某个形参传值,可以完全不参照顺序

def func(x,y):
    print(x,y)

func(y=2,x=1)
# 2-->y ,   1-->x
# 1,2

7.3.3 位置传参与关键字传参混用

位置传参与关键字传参混用必须遵守的规则:

1、位置实参必须放在关键字实参前

def func(x,y):
    print(x,y)
func(1,y=2)		# 正确示范 
# 1,2
func(y=2,1)		# 错误示范
# 报错

2、不能能为同一个形参重复传值

def func(x,y):
    print(x,y)
func(1,y=2)		# 正确示范 
# 1,2 
func(1,y=2,x=3) # 错误示范
# 报错

7.3.3 所有传参方式混用

只要保证按位置传的参数写在按关键字传的参数之前就行

*args (打散)可以看作多个按位置传的参数

**kwargs (打散)可以看作多个按关键字传的参数

def func(x,y,z,a,b,c):
	print(x,y,z,a,b,c)

func(111,y=222,*[333,444],**{'b':555,'c':666})	
# 错误

func(111,y=222,333,444,b=555,c=666)
# 错误

func(111,*[333,444],a=222,**{'b':555,'c':666})
# 正确

func(111,333,444,a=222,b=555,c=66)
# 正确

func(111,*[333,444],**{'b':555,'c':666},a=222,)
# 正确

8 函数对象

函数也是一个对象,可以把函数当成变量去用

8.1 可以赋值

# func=内存地址
def func():
    print('from func')

f=func
print(f,func)
f()

8.2 可以当做函数当做参数传给另外一个函数

# func=内存地址
def func():
    print('from func')

def foo(x): # x = func的内存地址
    # print(x)
    x()

foo(func) # foo(func的内存地址)

8.3 可以当做函数当做另外一个函数的返回值

def foo(x): # x=func的内存地址
    return x # return func的内存地址

res=foo(func) # foo(func的内存地址)
print(res) # res=func的内存地址

res()

8.4 可以当做容器类型的一个元素

l=[func,]
# print(l)
l[0]()

dic={'k1':func}
print(dic)
dic['k1']()

8.5 函数对象应用示范

示范1:

def login():
    print('登录功能')


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


def check_banlance():
    print('查询余额')

def withdraw():
    print('提现')


def register():
    print('注册')

func_dic={
    '1':login,
    '2':transfer,
    '3':check_banlance,
    '4':withdraw,
    '5':register
}


while True:
    print("""
    0 退出
    1 登录
    2 转账
    3 查询余额
    4 提现
    5 注册
    """)
    choice = input('请输入命令编号:').strip()
    if not choice.isdigit():
        print('必须输入编号,傻叉')
        continue

    if choice == '0':
        break


    if choice in func_dic:
        func_dic[choice]()
    else:
        print('输入的指令不存在')



示范2:

def login():
    print('登录功能')


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


def check_banlance():
    print('查询余额')


def withdraw():
    print('提现')


def register():
    print('注册')


func_dic = {
    '0': ['退出', None],
    '1': ['登录', login],
    '2': ['转账', transfer],
    '3': ['查询余额', check_banlance],
    '4': ['提现', withdraw],
    '5': ['注册', register]
}



while True:
    for k in func_dic:
        print(k, func_dic[k][0])

    choice = input('请输入命令编号:').strip()
    if not choice.isdigit():
        print('必须输入编号,傻叉')
        continue

    if choice == '0':
        break

    # choice='1'
    if choice in func_dic:
        func_dic[choice][1]()
    else:
        print('输入的指令不存在')

9 函数的嵌套

函数嵌套有两种:

调用的嵌套 和 定义的嵌套

9.1 函数的嵌套调用

函数的嵌套调用就是在调用一个函数的过程中又调用其他函数

def max2(x,y):
    if x > y:
        return x
    else:
        return y
# 比较大小,返回较大值
def max2(x,y):
    if x > y:
        return x
    else:
        return y
# 比较大小,返回较大值

# 嵌套调用
def max4(a,b,c,d):
    # 第一步:比较a,b得到res1
    res1=max2(a,b)
    # 第二步:比较res1,c得到res2
    res2=max2(res1,c)
    # 第三步:比较res2,d得到res3
    res3=max2(res2,d)
    return res3

res=max4(1,2,3,4)
print(res)

9.2 函数的嵌套定义

函数的嵌套定义就是在函数内定义其他函数

def f1():
    def f2():
        pass
# 圆形
# 求圆形的求周长:2*pi*radius
# radius 半径
# pi=π
def circle(radius,action=0):
    from math import pi
    def perimiter(radius):
        return 2*pi*radius

    # 求圆形的求面积:pi*(radius**2)
    def area(radius):
        return pi*(radius**2)

    if action == 0:
        return 2*pi*radius

    elif action == 1:
        return area(radius)

circle(33,action=0)

1

 posted on 2020-03-17 20:23  wwwpy  阅读(786)  评论(0编辑  收藏  举报