Python高级之函数介绍

函数的基本介绍

【一】函数的特点以及使用情形

【1】函数的特点:

  1. 封装(Encapsulation):
    • 函数封装了一段特定的代码,将其作为一个独立的单元。这有助于提高代码的可维护性和可读性。
  2. 重用性(Reusability):
    • 函数可以在程序中的不同位置被调用,实现了代码的重用。通过调用函数,可以避免重复编写相同的代码。
  3. 参数(Parameters):
    • 函数可以接受输入参数,这样可以在函数内部使用外部提供的数据。参数使得函数更加通用,适用于不同的输入。
  4. 返回值(Return Value):
    • 函数可以返回结果,这样调用者可以获取函数的输出。返回值是函数向外部提供结果的一种方式。
  5. 模块化(Modularity):
    • 函数可以作为模块,通过函数的组合可以构建复杂的程序。模块化使得代码结构更清晰,易于理解和维护。
  6. 局部变量(Local Variables):
    • 函数内部可以定义局部变量,这些变量的作用范围仅限于函数内部。这有助于避免命名冲突和提高代码的安全性。

【2】使用情形:

  1. 代码复用:
    • 当某段代码需要在程序的不同部分被重复使用时,将其封装成函数,可以通过函数调用来实现代码的复用。
  2. 抽象操作:
    • 函数提供了对某个操作的抽象,使得调用者无需关心操作的具体实现细节。这样可以提高代码的抽象层次,简化调用者的逻辑。
  3. 模块化设计:
    • 大型程序通常由许多小模块组成,每个模块可以是一个函数。通过模块化设计,可以更容易地理解和维护代码。
  4. 参数化操作:
    • 函数可以接受参数,使得操作变得更加通用。通过参数传递不同的值,可以在不同的上下文中使用同一个函数。
  5. 组织结构:
    • 函数有助于组织程序的结构。将不同功能的代码封装在不同的函数中,使得程序更加有序,易于理解。
  6. 提高可读性:
    • 使用函数可以将复杂的逻辑分解成简单的步骤,提高了代码的可读性。每个函数都承担特定的责任,使得代码更具清晰度。
  7. 测试和调试:
    • 函数可以更容易地进行单元测试,因为函数是独立的单元。在调试时,函数也提供了更容易定位问题的标志点。

【二】函数的定义与调用

【1】定义的语法

(引)基本概念

  • 定义函数的语法
def 函数名(参数1,参数2,...):
	"""文档描述"""
	函数体
	return 值
  • def: 定义函数的关键字;
  • 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
  • 括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
  • 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
  • """文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
  • 函数体:由语句和表达式组成;
  • return 值:定义函数的返回值,return是可有可无的。

(1)空函数

def 函数名():
    .../pass
  • 一般在开始结构构建阶段,作为标记使用

(2)无参无返回值

def 函数名():
    函数体   
'''例如'''
def add():
    print(1+1)
  • 一般是进行一些操作且不需要返回值时使用

(3)有参无返回值

def 函数名(参数1,参数2,...):
    函数体
'''例如'''
def add(x,y):
    print(x+y)

(4)有参有返回值

def 函数名(参数1,参数2,...):
    函数体
    return 数据
'''例如'''
def add(x,y):
    msg=x+y
    return msg

【2】函数的调用方式

(1)直接调用

def add(x,y):
    return x+y
print(add(1,2))
  • 通过函数名+()直接进行调用

(2)表达式调用

def add(x,y):
    return x+y
result = add   # add = add()   # add作为变量名,add()函数作为内存地址
print(result(1,2))   # result=add # 为result赋值add  # add作为add()的变量名其对应的值为函数的内存地址
# 故 result() == add()  # 所以给result传参执行的就是add()函数的操作,并返回1+2的值3

(3)函数作为参数

def add():
    return 1+9   # add()函数返回1+9的值10
def mix(x,y):
    return x*y
print(mix(x=add(),y=10))   # 100   # mix()需要的两个参数x,y  # x = add() = 10, y = 10

【三】函数的参数

【1】函数的参数类型[形参&实参]

(1)形参(形式参数)

  • 形参即在定义函数时,括号内声明的参数。
    • 形参本质就是一个变量名,用来接收外部传来的值。

(2)实参(实际参数)

  • 实参即在调用函数时,括号内传入的值
    • 值可以是常量、变量、表达式或三者的组合
def add(形参1,形参2):
    return 实参1+实参2
add(形参1=实参1,形参2=实参2)  # 实参为数值,形参为关键字

(3)参数类型弱警告

  • 可以为形参设定期望参数
    • 通过函数名(参数1:数据类型,参数2)实现
    • 弱警告并不会报错,只是警示开发人员,此处希望得到的数据类型是什么
# 可以通过在参数后加上冒号并指定参数类型
def add(x: int, y):
    return x + y


add('a', 2)

image-20231215171556923

【2】传参数

(1)位置传参

  • 在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参
  • 顺序错误将导致传递数值错误
  • 按位置传参数据数量必须一致,否则将导致报错
def add(a,b,c):
    return a+b+c

print(add('我','爱','你'))   # 我爱你
# 正常按位置传参数  输出内容为 我爱你
print(add('爱','我' '你'))   # 爱我你
# 位置与参数位置不一致,将导致输出内容错乱
print(add('我', '爱', '你','吗?'))    # add() takes 3 positional arguments but 4 were given
# 参数多了一个

(2)关键字传参

  • 在调用函数时,实参可以是key=value的形式,称为关键字参数

    • 凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
def add(a, b, c):
    return a + b + c

print(add(a='我', b='爱', c='你'))   # 我爱你
print(add(b='爱', a='我', c='你'))   # 我爱你
# 不管关键字参数位置怎么变,都是我爱你,爱你哟~

(3)位置参数和关键字参数混合使用

  • 需要注意,关键字参数不可以在位置参数前,否则将报错
def add(a, b, c):
    return a + b + c

print(add('I', b='love', c='you'))  # Iloveyou
# ’I'是位置参数,'love''you'是关键字参数
print(add(a='I', 'love', c='you'))   # SyntaxError: positional argument follows keyword argument
# 当关键字参数在前,后续就 不可以出现位置参数
# 'I'作为关键字参数传入后,'love'就必须指定关键字成为关键字参数,否则将报错

(4)默认参数

  • 在定义函数时,就已经为形参赋值,这类形参称之为默认参数
    • 当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。
# 默认参数类似于实参中的关键字参数,故需要放在参数的最后面
def add(a,c,b='love'):
    return a+b+c

print(add('我','你'))  # 我love你
# 这里需要先用位置传参,a='我',c='你',b作为默认参数,可以不用传参
# 默认参数'love'无需多言
print(add('我','你','爱'))   # 我爱你
# 默认参数同样是可以传参的,如果传参就将替换掉默认参数的值

# 其他情况
# 当设定了命名关键字参数时,'*'后的所有参数都需要使用命名关键字,而这时,默认参数的位置就不会限制在最后面了
def add(a, *, b='love', c):
    return a + b + c
print(add('I', b='love', c='you'))  # Iloveyou

【3】可变长参数

(1)可变长位置参数*args

  • 如果在最后一个形参名前加 * 号,那么在调用函数时,溢出的位置实参,都会被 * 接收,以元组的形式保存下来赋值给该形参
  • 既然时元组就可以进行解包赋值的操作
# 在最后一个形参名args前加*号
def foo(x, y, z=1, *args):
    print(x)
    print(y)
    print(z)
    print(args)


foo(1, 2, 3, 4, 5, 6, 7)
# 实参1、2、3按位置为形参x、y、z赋值
# 多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args
# 即args=(4, 5, 6,7)

# 可变长位置参数可以求多个值的和
def add(*args):
    res = 0
    for i in args:
        res += i
    return res


res = add(1, 2, 3, 4, 5)
print(res) # 15


# 解包
def foo(x, y, *args):
    print(x)
    print(y)
    print(args)


L = [3, 4, 5]
foo(1, 2, *L)
# *L就相当于位置参数3,4,5 ----> 解包
# 对应到函数中就是,foo(1,2,*L)就等同于foo(1,2,3,4,5)

(2)可变长关键字参数**kwargs

  • 如果在最后一个形参名前加**号,那么在调用函数时,溢出的关键字参数,都会被 ** 接收,以字典的形式保存下来赋值给该形参
def foo(x, **kwargs):  # 在最后一个参数kwargs前加**
    print(x)
    print(kwargs)


foo(y=2, x=1, z=3)
# 溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
# 1
# {'y': 2, 'z': 3}

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


dic = {'a': 1, 'b': 2}
foo(1, 2, **dic)
# **dic就相当于关键字参数a=1,b=2,foo(1,2,**dic)
# 等同foo(1,2,a=1,b=2)

# 1
# 2
# {'a': 1, 'b': 2}

【4】命名关键字参数*

  • 想要限定函数的调用者必须以key=value的形式传值

  • 需要在定义形参时,用 * 作为一个分隔符号,* 号之后的形参称为命名关键字参数。

  • 对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值

def register(name, age, *, sex, height):  # sex,height为命名关键字参数
    pass


# 正确使用
register('user', 18, sex='male', height='1.9m')

# TypeError:未使用关键字的形式为sex和height传值
register('user', 18, 'male', '1.9m')

# TypeError没有为命名关键字参数height传值。
register('user', 18, height='1.9m')

【5】混合使用

  • 综上所述所有参数可任意组合使用,但定义顺序必须是:
    • 位置参数默认参数*args命名关键字参数**kwargs
  • 可变参数*args 与关键字参数**kwargs通常是组合在一起使用的
  • 如果一个函数的形参为 **args**kwargs,那么代表该函数可以接收任何形式、任意长度的参数
def wrapper(*args,**kwargs):
    pass
  • 在该函数内部还可以把接收到的参数传给另外一个函数(这在装饰器的实现中大有用处)
def func(x, y, z):
    print(x, y, z)


def wrapper(*args, **kwargs):
    func(*args, **kwargs)


wrapper(1, z=3, y=2)
# 1 2 3
  • 提示: *args**kwargs 中的args和kwargs被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。
posted @ 2023-12-12 16:22  Lea4ning  阅读(24)  评论(0编辑  收藏  举报