Python进阶(函数)

一、什么是函数

函数是组织好的、实现单一功能或相关联功能的代码段。我们可以将函数视为一段有名字的代码,这类代码可以在需要的地方以“函数名()”的形式调用。

函数式编程具有以下优点:

       (1) 将程序结构化,即减少了代码冗余,又使结构更加清晰

        (2)提高了开发人员的效率

        (3)方便后期的维护和拓展

二、函数的定义和调用

1.定义函数:Python中使用关键字def来定义函数,其语法格式如下:

例如,定义一个计算两个数之和的函数,代码如下:

def add(a, b):
    result = a + b
    print(result)
 
print(add(10,20)) # 30
 

2.调用函数:函数在定义完成后不会立刻执行,直到被程序调用时才会执行。

 语法格式如下:函数名([参数列表])

(1)程序在调用函数的位置暂停执行。

(2)将数据传递给函数参数。

(3) 执行函数体中的语句。

(4)程序回到暂停处继续执行。

三、函数参数的传递

我们通常将定义函数时设置的参数称为形式参数(简称为形参),将调用函数时传入的参数称为实际参数(简称为实参)。函数的参数传递是指将实际参数传递给形式参数的过程。

函数参数的传递可以分为位置参数传递、关键字参数传递、默认参数传递、参数的打包与解包以及混合传递。

1.位置参数传递 

函数在被调用时会将实参按照相应的位置依次传递给形参,也就是说将第一个实参传递给第一个形参,将第二个实参传递给第二个形参,以此类推。

def get_max(a, b):
    if a > b:
        print(a,"是较大的值!")
    else:
        print(b,"是较大的值!")
 
get_max(8, 5) 
 

2.关键字参数传递

关键字参数的传递是通过“形参=实参”的格式将实参与形参相关联,将实参按照相应的关键字传递给形参。

def get_name(name,age):
    print(f"我叫{name},今年{age}岁了")
print(get_name(name = "rzm",age = 19))
问题:无论实参采用位置参数的方式传递,还是关键字参数的方式传递,每个形参都是有名称的,怎么区分用哪种方式传递呢? 

Python在3.8版本中新增了仅限位置形参的语法,使用符号“/”来限定部分形参只接收采用位置传递方式的实参。

def func(a, b, /, c):
    print(a, b, c)
 
# 错误的调用方式
# func(a=10, 20, 30)   
# func(10, b=20, 30)
# 正确的调用方式
func(10, 20, c=30)
 

3. 默认参数传递

函数在定义时可以指定形参的默认值,如此在被调用时可以选择是否给带有默认值的形参传值,若没有给带有默认值的形参传值,则直接使用该形参的默认值。

def get_name(name,age = 19):
   return f"我叫{name},今年{age}岁了"
print(get_name(name = "rzm")) # 我叫rzm,今年19岁了
print(get_name(name = "qwe",age = 20)) # 我叫qwe,今年20岁了
四、参数的打包与解包

1.打包:如果函数在定义时无法确定需要接收多少个数据,那么可以在定义函数时为形参添加“*”或“**”:

“*” —— 接收以元组形式打包的多个值

“**”—— 接收以字典形式打包的多个值

def test(*args):
    print(args)
 
print(test(11, 22, 33, 44, 55))
 
def test(**kwargs):
    print(kwargs)
 
test(a=11, b=22, c=33, d=44, e=55)
# {'a': 11, 'b': 22, 'c': 33, 'd': 44, 'e': 55}
关于打包的注意点:

虽然函数中添加“*”或“**”的形参可以是符合命名规范的任意名称,但这里建议使用*args和**kwargs。

若函数没有接收到任何数据,参数*args和**kwargs为空,即它们为空元组或空字典。

2.解包:

        实参是元组   →   可以使用“*”拆分成多个值  →  按位置参数传给形参

        实参是字典   →    可以使用“**” 拆分成多个键值对    →    按关键字参数传给形参

def test(a, b, c, d, e):
    print(a, b, c, d, e)
 
nums = (11, 22, 33, 44, 55)
print(test(*nums))
# {'a': 11, 'b': 22, 'c': 33 , 'd': 44, 'e': 55}
3.混合传递

在定义函数时:

(1) 带有默认值的参数必须位于普通参数之后。
(2) 带有“*”标识的参数必须位于带有默认值的参数之后。
(3) 带有“**”标识的参数必须位于带有“*”标识的参数之后。
 

def test(a, b, c=33, *args, **kwargs):
print(a, b, c, args, kwargs)
 
test(1, 2) # 1 2 33 () {}
test(1, 2, 3) # 1 2 3 () {}
test(1, 2, 3, 4) # 1 2 3 (4,) {}
test(1, 2, 3, 4, e=5) # 1 2 3 (4,) {'e': 5}
 
 
 

五、函数的返回值

函数中的return语句会在函数结束时将数据返回给程序,同时让程序回到函数被调用的位置继续执行。

def filter_sensitive_words(words):
    if "傻逼" in words:
        new_words = words.replace("傻逼", "**")
        return new_words
 
result = filter_sensitive_words("这个人好傻逼啊!")
print(result) # 这个人好**啊!
 
如果函数使用return语句返回了多个值,那么这些值将被保存到元组中。 

def move(x, y, step):
    nx = x + step
    ny = y - step
    return nx, ny     # 使用return语句返回多个值
 
result = move(100, 100, 60)      
print(result) # (160, 40)
 
六、 变量作用域

变量并非在程序的任意位置都可以被访问,其访问权限取决于变量定义的位置,其所处的有效范围称为变量的作用域。

根据作用域的不同,变量可以划分为局部变量和全局变量。

1.局部变量

        函数内部定义的变量,只能在函数内部被使用

        函数执行结束之后局部变量会被释放,此时无法再进行访问。

def test_one():
    number = 10  # 局部变量
    print(number) # 函数内部访问局部变量
 
test_one()
print(number) # 函数外部访问局部变量(此时编译会报错)
不同函数内部可以包含同名的局部变量,这些局部变量的关系类似于不同目录下同名文件的关系,它们相互独立,互不影响。

def test_one():
    number = 10      
    print(number) # 访问test_one()函数的局部变量number
def test_two():
    number = 20
    print(number) # 访问test_two()函数的局部变量number
test_one()
test_two()
2.全局变量

全局变量可以在整个程序的范围内起作用,它不会受到函数范围的影响。

number = 10  # 全局变量
def test_one():
    print(number) # 函数内部访问全局变量
test_one()
print(number) # 函数外部访问全局变量
全局变量在函数内部只能被访问,而无法直接修改。

# 定义全局变量
number = 10
def test_one():
    number += 1 # 编译报错
test_one()
print(number)
这是因为函数内部的变量number视为局部变量,而在执行“number+=1”这行代码之前并未声明过局部变量number。

拓展:LEGB原则

L-local:局部作用域:例如,局部变量和形参生效的区域。

E-enclosing:嵌套作用域:例如,嵌套定义的函数中外层函数声明的变量生效的区域。

G-global:全局作用域:例如,全局变量生效的区域。

B-built-in:内置作用域:例如,内置模块声明的变量生效的区域。

Python在搜索变量时会按照“L-E-G-B ”这个顺序依次在这四种区域中搜索变量:若搜索到变量则终止搜索,使用搜索到的变量;若搜索完L、E、G、B这四种区域仍无法找到变量,程序将抛出异常。

3.global和nonlocal关键字

函数内部无法直接修改全局变量或在嵌套函数的外层函数声明的变量,但可以使用global或nonlocal关键字修饰变量以间接修改以上变量。

使用global关键字可以将局部变量声明为全局变量,其使用方法如下:

number = 10  # 定义全局变量
def test_one():
    global number # 使用global声明变量number为全局变量
    number += 1
    print(number)
test_one()
print(number)
使用nonlocal关键字可以在局部作用域中修改嵌套作用域中定义的变量,其使用方法如下:

def test():
    number = 10
    def test_in():
        nonlocal number
        number = 20
    test_in()
    print(number)
test()
七、特殊形式的函数:

1.递归函数

函数在定义时可以直接或间接地调用其他函数。若函数内部调用了自身,则这个函数被称为递归函数。

递归函数在定义时需要满足两个基本条件:一个是递归公式,另一个是边界条件。其中:

(1) 递归公式是求解原问题或相似的子问题的结构;
(2) 边界条件是最小化的子问题,也是递归终止的条件
递归函数的执行可以分为以下两个阶段:

1.递推:递归本次的执行都基于上一次的运算结果。

2.回溯:遇到终止条件时,则沿着递推往回一级一级地把值返回来。

 递归函数的一般定义格式如下所示:

def函数名([参数列表]):

  if 边界条件:

          rerun 结果

  else:

          return 递归公式

递归例题(阶乘n!)

n! = 1 * 2 * 3 * … * n,可以分为以下两种情况:

1. 当n=1时,所得的结果为1。

2. 当n>1时,所得的结果为n*(n-1)!。

def func(num):
     if num == 1: 
         return 1
     else:
         return num * func(num - 1)
num = int(input("请输入一个整数:"))
result = func(num)
print("5!=%d"%result)
2.匿名函数

匿名函数与普通函数的主要区别如下:

(1) 普通函数在定义时有 名称 ,而匿名函数没有名称;
(2) 普通函数的函数体中包含有多条 语句 ,而匿名函数的函数体只能是一个 表达式 ;
(3) 普通函数可以实现比较 复杂 的功能,而匿名函数可实现的功能比较 简单 ;
(4) 普通函数 能被其他程序使用 ,而匿名函数 不能 被其他程序使用。
定义好的匿名函数不能直接使用,最好使用一个变量保存它,以便后期可以随时使用这个函数。

# 定义匿名函数,并将它返回的函数对象赋值给变量temp
temp = lambda x : pow(x, 2)
print(temp(10)) # 100
 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值