python学习笔记(三)函数

Love The Way You Lie 2023-07-02 03:17 37阅读 0赞

目录

(一)基本的函数定义

(二)多种函数参数

1)实参与形参

2)函数形参的定义方式

按位置给出形参

按形参名称传递实参

变长实参

(三)将可变对象用作函数实参

(四)局部变量、非局部变量和全局变量

(五)将函数赋给变量

(六)lambda表达式

(七)生成器函数

(八)装饰器


主要内容

  • 定义函数
  • 使用函数参数
  • 用可变对象作为参数
  • 理解局部变量和全局变量
  • 创建生成器函数
  • 创建和使用lambda表达式
  • 使用装饰器

(一)基本的函数定义

先简单举个例子,以阶乘计算为例。

以下代码定义了把阶乘的计算放到fact()函数中,这样只需要调用fact函数即可以得到阶乘值了:

文档字符串(docstring)用于描述函数对外表现出来的功能及所需的参数,可以通过fact.__doc__读取其值。文档字符串紧随在函数定义的第一行后面,通常用3重引号包围起来,以便能够跨越多行。

注释(comment)则是记录代码工作原理的内部信息。

  1. >>> def fact(n):
  2. """Return the factorial of the given number""" #文档字符串(docstring)
  3. r = 1
  4. while n > 0:
  5. r = r * n
  6. n = n - 1
  7. return r
  8. >>>
  9. >>> fact(4)
  10. 24
  11. >>> fact(10)
  12. 3628800
  13. # 读取文档字符串
  14. >>> fact.__doc__
  15. 'Return the factorial of the given number'

return语句之后的值将会返回给函数的调用者。

在某些编程语言中,无返回值的函数被称为“过程”。Python允许编写不含return语句的函数,如果执行了return arg语句,则值arg会被立即返回。return语句执行之后,函数体中的其余语句都不会执行。

因为Python没有真正的过程,所以均被称为“函数”。

(二)多种函数参数

大多数语言的函数都是需要参数的,python提供了三种给出参数的方式,在介绍之前,先了解一下形参和实参的区别。

1)实参与形参

实参(argument):全称为”实际参数”。是在调用时传递给函数的参数.。实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。 因此应预先用赋值、输入等办法使实参获得确定值。

形参(parameter):全称为”形式参数” 由于它不是实际存在变量,所以又称虚拟变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。也就是说,形参只存在于函数内。在调用函数时,实参将赋值给形参。因而,必须注意实参的个数,类型应与形参一一对应,并且实参必须要有确定的值。

2)函数形参的定义方式

按位置给出形参

在Python中,最简单的函数传递形参方式就是按位置给出。在函数的第一行中,可以为每个形参指定变量名称。当调用函数时,调用代码中给出的形参将按顺序与函数的形参变量逐一匹配。以下函数计算x的y次幂:

  1. >>> def power(x, y):
  2. r = 1
  3. while y > 0:
  4. r = r * x
  5. y = y - 1
  6. return r
  7. >>> power(3 , 3)
  8. 27

上述用法要求,调用代码使用的形参数量与函数定义时的形参数量应该完全匹配,否则会引发TypeError:

  1. >>> power(3)
  2. Traceback (most recent call last):
  3. File "<pyshell#17>", line 1, in <module>
  4. power(3)
  5. TypeError: power() missing 1 required positional argument: 'y'

函数的形参可以有默认值,可以在函数定义的第一行中给出该默认值,如下所示:

  1. def fun(arg1, arg2 = default2, arg3 = default3, ...)

可以为任何数量的形参给出默认值。带默认值的形参必须位于形参列表的末尾,因为与大多数编程语言一样,Python也是根据位置来把实参与形参匹配起来的。给函数的实参数量必须足够多,以便让形参列表中最后一个不带默认值的形参能获取到实参。

以下函数同样也会计算x的y次幂。但如果在函数调用时没有给出y,则会用默认值2,于是就成了计算平方的函数:

  1. >>> def power(x, y = 2):
  2. r = 1
  3. while y > 0:
  4. r = r * x
  5. y = y - 1
  6. return r
  7. >>> power(3, 3)
  8. 27
  9. >>> power(3)
  10. 9

按形参名称传递实参

也可以使用对应的函数形参的名称将实参传给函数,而不是按照形参的位置给出。继续上面的交互示例,可以键入:

  1. >>> power(3, 4)
  2. 81
  3. >>> power(y = 4, x = 3)
  4. 81

最后提交给power函数的实参带了名称,因此与顺序无关。实参与power函数定义中的同名形参关联起来,得到的是3^4的结果。这种实参传递方式被称为关键字传递(keyword passing)。

如果函数需要带有大量实参,并且大多数实参都有默认值,那么联合使用关键字传递和默认实参功能就非常有用了。

变长实参

python也可以定义为实参数量可变的形式,定义方式有两种。

  • 一种用于处理实参预期相对明了的情况,实参列表尾部数量不定的实参将会被放入一个例表中。
  • 另一种方式可将任意数量的关键字传递实参放入一个字典中,这些实参均是在函数形参列表中不存在同名形参的。

1.位置实参数量不定时的处理

当函数的最后一个形参名称带有*前缀时,在一个函数调用中所有多出来的非关键字传递实参(即这些按位置给出的实参未能赋给合适的形参)将会合并为一个元组赋给该形参。

下面用这种简单方式来实现一个求数字列表中最大值的函数

  1. def maximum(*numbers):
  2. if len(numbers) == 0:
  3. return None
  4. else:
  5. maxnum = numbers[0]
  6. for n in numbers[1:]:
  7. if n > maxnum:
  8. maxnum = n
  9. return maxnum
  10. print(maximum(3,5,2))
  11. print(maximum(1,5,9,-2,2))

输出:

  1. 5
  2. 9

2.关键字传递实参数量不定时的处理

按关键字传递的实参数量不定时,也能进行处理。如果形参咧白哦的最后一个形参前缀为“**”,那么所有多余的关键字传递实参将会被收入一个字典对象中。字典的键为多余实参的关键字(形参名称),字典的值为实参本身。这里的“多余”是指,传递实参的关键字匹配不到函数定义中的形参名称。

例如:

  1. def example_fun(x, y, **other):
  2. print("x:{0},y:{1},keys in 'other':{2}".format(x, y, list(other.keys())))
  3. other_total = 0
  4. for k in other.keys():
  5. other_total = other_total + other[k]
  6. print("The total of values in 'other' is {}".format(other_total))
  7. example_fun(2, y = "1",foo = 3, bar = 4)

输出:

  1. x:2,y:1,keys in 'other':['foo', 'bar']
  2. The total of values in 'other' is 7

(三)将可变对象用作函数实参

函数的实参传递的是对象的引用,形参则成为指向对象的新引用。对于不可变对象(如元组、字符串和数值),对形参的操作不会影响函数外部的代码。但是如果传入的是可变对象(如列表、字典或类的实例),则对该对象做出的任何改动都会改变该实参在函数外引用的值。

所以,在对一个列表需要引用多次的时候,往往会对列表进行复制后带作为函数的实参。

以下代码定义了deal_var()函数,实现打印功能。id()内置函数,返回对象内存地址。

  1. li = [0, 1, 2]
  2. def deal_var(var):
  3. #var.append(3)
  4. var = [0, 1, 2, 3]
  5. print("id(var) = {0}, {1}".format(var, id(var)))
  6. print("id(li) = {0}, {1}".format(li, id(li)))
  7. deal_var(li)
  8. print("id(li) = {0}, {1}".format(li, id(li)))

(四)局部变量、非局部变量和全局变量

我们需要理解python定义变量的范围,如果不理解变量的范围,就会导致变量访问出现问题。

我们前面提到的阶层函数中:

  1. >>> def fact(n):
  2. """Return the factorial of the given number""" #文档字符串(docstring)
  3. r = 1
  4. while n > 0:
  5. r = r * n
  6. n = n - 1
  7. return r

其中r和n都是函数fact()内的变量,是局部变量,如果在函数外面进行访问,python解释器就会报错。

如果我们想要访问函数内的变量,可以用global语句对变量声明,可以显式地使其成为全局变量,函数可以访问和修改全局变量。全局变量存在于函数之外。

如果想对函数之外的变量赋值,就必须将其显式声明为nonlocal或global。但是如果只是要访问函数外的变量,则不需要将其声明为nonlocal或global。

看以下例子:

  1. """
  2. 变量作用域
  3. """
  4. g1 = 'one'
  5. def func():
  6. g1 = 'one piece'
  7. global g2
  8. g2 = 'two piece'
  9. print("In func():", g1)
  10. print("In func():", g2)
  11. print("inside : id(g1) = %s" %(id(g1)))
  12. print("inside : id(g2) = %s" %(id(g2)))
  13. g2 = 'two'
  14. func()
  15. print("g1 = ", g1)
  16. print("g2 = ", g2)
  17. print("outside: id(g1) = %s" %(id(g1)))
  18. print("outside: id(g2) = %s" %(id(g2)))

运行结果:

  1. In func(): one piece
  2. In func(): two piece
  3. inside : id(g1) = 1985623671600
  4. inside : id(g2) = 1985623671344
  5. g1 = one
  6. g2 = two piece
  7. outside: id(g1) = 1985567139248
  8. outside: id(g2) = 1985623671344

(五)将函数赋给变量

与其他Python对象一样,函数也可以被赋值。

函数可以被放入列表、元组、或字典中。

通过字符串键值可以使用字典调用各个函数。在需要根据字符串值来选择不同函数的情况下,这种模式就很常用。

(六)lambda表达式

lambda表达式是匿名的小型函数,可以快速的在行内完成定义。通常小型函数是要被传给另一个函数的,例如,列表的排序方法用到的键函数。

lambda表达式:

  1. lambda parameter1, parameter2, ...: expression

看如下代码:

  1. add = lambda x, y: x + y
  2. print(add(1,5))

输出6。

注意,lambda表达式没有return语句,因为表达式的值将自动返回。

(七)生成器函数

生成器(generator)函数是一种特殊的函数,可用于定义自己的迭代器(iterator)。在定义生成器函数时,用关键字yield返回每一个迭代值。

  1. def four():
  2. x = 0
  3. while x < 4:
  4. print("in generator, x = ",x)
  5. yield x
  6. x += 1
  7. for i in four():
  8. print(i)

输出:

in generator, x = 0
0
in generator, x = 1
1
in generator, x = 2
2
in generator, x = 3
3

(八)装饰器

因为函数是Python的一级对象(first-class),所以能被赋给变量。函数也可以作为实参传递给其他函数,还可以作为其他函数的返回值回传。

  1. def foo():
  2. print("foo")
  3. def bar(func):
  4. func()
  5. bar(foo)

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

装饰器算是一个比较高级的内容,这里就先不介绍了,等我们未来调试项目的时候用到再来补充……

参考资料:

  1. IT_小默
  2. https://www.cnblogs.com/hf8051/p/8085424.html
  3. https://foofish.net/python-decorator.html

发表评论

表情:
评论列表 (有 0 条评论,37人围观)

还没有评论,来说两句吧...

相关阅读

    相关 python学习笔记函数

    python也有自己的函数,类似于java中的方法,他们也都是存在于自己对应的包下面,如果要使用这些函数,必须将该函数所对应的模块进行引入 1.我们要使用floor函数

    相关 Python 函数学习笔记

    1. 调用参数时的顺序 > 比如定义test(a,b)函数,在调用时,如果不显示的指定,那么第一个参数就传递给a,第二个参数就传递给b,但是也可以显示的指定比如test(b