Python函数 各种参数

limalove / 2023-09-01 / 原文

 

抽象

抽象是数学中非常常见的概念。举个例子:我们看到 符号∑ 就可以理解成求和,而不是还原成低级的加法运算。

借助抽象,我们才能不关心底层的具体计算过程,而直接在更高的层次上思考问题。

写计算机程序也是一样,函数就是最基本的一种代码抽象的方式。

 

 

 3,函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”。

 

 

 

 

二、定义函数

1,return返回值

一旦执行到return时,函数就执行完毕,并将结果返回。即使后面有return语句或其他语句也不再执行。

 如果没有return语句,返回结果为None。return None可以简写为return

3,返回多个值

函数可以同时返回多个值,以元组tuple的形式返回。多个变量可以同时接收一个tuple,按位置赋给对应的值。

 

 

三、函数的参数

1,默认参数

默认参数的好处是能降低调用函数的难度。

注意:必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);

定义默认参数要牢记一点:默认参数必须指向不变对象!

 

问题:为什么要设计str, none这样的不变对象呢?

     (1)因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。

     (2)由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。

因此,我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

 

2,可变参数

  • 可变参数就是传入的参数个数是可变的。
  • 在函数内部,参数numbers接收到的是一个tuple
  • *nums表示把nums这个list的所有元素作为可变参数传进去。

3,关键字参数

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

4,命名关键字参数

  • 命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。
  • 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
  • 命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错

5,强制位置参数 

What’s New In Python 3.8 — Python 3.11.5 documentation

Python3.8 新增了一个函数形参语法 / ,用来指明该符号" / "前面的形参必须使用位置参数,不能使用关键字参数形式。

在以下的例子中,形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参:

#形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参
def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)
    

#c为位置参数,d为关键字参数
result = f(10, 20, 30, d=40, e=50, f=60)  #运行OK

#c, d为关键字参数
result = f(10, 20, c = 30, d=40, e=50, f=60)  #运行OK

#c, d均为位置参数
result = f(10, 20,  30,  40, e=50, f = 60 )   #运行OK


f(10, b=20, c=30, d=40, e=50, f=60)   #报错, b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60)           #报错, e 必须使用关键字参数的形式

 

 

6,参数组合

参数定义的顺序必须是:必选参数(位置参数)、默认参数、可变参数、命名关键字参数和关键字参数。

 

 

 

 

 

 

小结

  • 默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
  • 要注意定义可变参数和关键字参数的语法:
  • *args是可变参数,args接收的是一个tuple;
  • **kw是关键字参数,kw接收的是一个dict。
  • 以及调用函数时如何传入可变参数和关键字参数的语法:
  • 可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3))
  • 关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})
  • 使用*args**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
  • 命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
  • 定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。

 

关键字参数和命名关键字参数的区别:

 

 

四、递归函数

  • 在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
  • 递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
  • 使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试fact(1000),一般最多是998次。
#阶乘

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

 

小结

  • 使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
  • 针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
  • Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

 

 

变量的作用域:全局变量和局部变量

局部变量

局部变量是指在函数内部定义并使用的变量,它只在函数内部有效。即函数内部的名字只在函数运行时才会创建,在函数运行之前或者运行完毕之后,所有的名字就都不存在了。所以,如果在函数外部使用函数内部定义的变量,就会出现抛出NameError异常。

全局变量

(1)如果一个变量在函数外定义,那么不仅可以在函数外可以访问到,在函数内也可以访问到。在函数体外定义的变量是全局变量。

(2)在函数体内定义,并且使用global关键字修饰后,该变量也就变为全局变量。

 

当实际参数为不可变对象时,进行的是值传递;当实际参数为可变对象时,进行的是引用传递。

 

 

 

 

 

 

 

 

这个讲eval(  )函数的例子好强大,尤其是函数这一块使用

 

 

 

 

 

 

 

因为sort修改原对象,所以元组不能使用sort方法排序,而sorted返回排序后的新对象,不会修改原数据,所以元组可以使用sorted方法排序