函数

函数

首先试想一下 我们只有加法,没有乘法,我们是怎么做代替乘法的?

  • 比如 9个8 相加 那么我们是不是应该 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 = 72

  • 比如 4个1 相加 1+ 1 + 1 + 1 = 4

  • 有了乘法后就不用这么麻烦了 8 * 9 = 72 1 * 4 = 4

函数就是一种功能,从而使得代码复用,以达到减少重复劳动的效果。

代码复用的第一步是使用函数,它是命名的用于区分的代码段。函数可以接受任何数字或者其他类型的输入作为参数,并且返回数字或者其他类型的结果。

使用函数

你可以使用函数做以下两件事情:

  • 定义函数

  • 调用函数

为了定义 Python 函数,你可以依次输入 def、函数名、带有函数参数的圆括号,最后紧跟一个冒号(:)。函数命名规范和变量命名一样(必须使用字母或者下划线 _ 开头,仅能含有字母、数字和下划线)。

为了定义 Python 函数,你可以依次输入 def、函数名、带有函数参数的圆括号,最后紧跟
一个冒号(:)。函数命名规范和变量命名一样(必须使用字母或者下划线 _ 开头,仅能含
有字母、数字和下划线)。

我们先定义和调用一个没有参数的函数。下面的例子是最简单的 Python 函数:
>>> def do_nothing():
... pass

我们可以创建一个输出任意范围内 Fibonacci 数列的函数:

>>> def fib(n):    # write Fibonacci series up to n
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

重命名机制:

函数定义会把函数名引入当前的符号表中。函数名称的值具有解释器将其识别为用户定义函数的类型。这个值可以分配给另一个名称,该名称也可以作为一个函数使用。

>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89

它并不返回值。事实上,即使没有 [return]语句的函数也会返回一个值,尽管它是一个相当无聊的值。这个值称为 None (它是内置名称)。一般来说解释器不会打印出单独的返回值 None ,如果你真想看到它,你可以使用 print()

>>> fib(0)
>>> print(fib(0))
None

有用的None

None Python 中一个特殊的值,虽然它不表示任何数据,但仍然具有重要的作用。
虽然 None 作为布尔值和 False 是一样的,但是它和 False 有很多差别。下面是一个
例子:
>>> thing = None
>>> if thing:
... print("It's some thing")
... else:
... print("It's no thing")
...
It's no thing
为了区分 None 和布尔值 False , 使用 Python is 操作符:
>>> if thing is None:
... print("It's nothing")
... else:
... print("It's something")
...
It's nothing

这虽然是一个微妙的区别,但是对于 Python 来说是很重要的。你需要把 None 和不含
任何值的空数据结构区分开来。0 值的整型 / 浮点型、空字符串('')、空列表([])、
空元组((,))、空字典({})、空集合(set())都等价于 False,但是不等于 None

>>> print([] is None)
False

位置参数

Python 处理参数的方式要比其他语言更加灵活。其中,最熟悉的参数类型是位置参数,传

入参数的值是按照顺序依次复制过去的。

下面创建一个带有位置参数的函数,并且返回一个字典:
>>> def menu(wine, entree, dessert):
... return {'wine': wine, 'entree': entree, 'dessert': dessert}
...
>>> menu('chardonnay', 'chicken', 'cake')
{'dessert': 'cake', 'wine': 'chardonnay', 'entree': 'chicken'}

尽管这种方式很常见,但是位置参数的一个弊端是必须熟记每个位置的参数的含义。在调
用函数 menu() 时误把最后一个参数当作第一个参数,会得到完全不同的结果:
>>> menu('beef', 'bagel', 'bordeaux')
{'dessert': 'bordeaux', 'wine': 'beef', 'entree': 'bagel'}

关键字参数

为了避免位置参数带来的混乱,调用参数时可以指定对应参数的名字,甚至可以采用与函数定义不同的顺序调用:

>>> menu(entree='beef', dessert='bagel', wine='bordeaux')
{'dessert': 'bagel', 'wine': 'bordeaux', 'entree': 'beef'}

你也可以把位置参数和关键字参数混合起来。首先,实例化参数 wine,然后对参数 entree
dessert 使用关键字参数的方式:
>>> menu('frontenac', dessert='flan', entree='fish')
{'entree': 'fish', 'dessert': 'flan', 'wine': 'frontenac'}


如果同时出现两种参数形式,首先应该考虑的是位置参数

指定默认参数值

当调用方没有提供对应的参数值时,你可以指定默认参数值。这个听起来很普通的特性实际上特别有用,以之前的例子为例:

>>> def menu(wine, entree, dessert='pudding'):
... return {'wine': wine, 'entree': entree, 'dessert': dessert}

这一次调用不带 dessert 参数的函数 menu()
>>> menu('chardonnay', 'chicken')
{'dessert': 'pudding', 'wine': 'chardonnay', 'entree': 'chicken'}

如果你提供参数值,在调用时会代替默认值:
>>> menu('dunkelfelder', 'duck', 'doughnut')
{'dessert': 'doughnut', 'wine': 'dunkelfelder', 'entree': 'duck'}

默认参数值在函数被定义时已经计算出来,而不是在程序运行时。Python 程序员经常犯的一个错误是把可变的数据类型(例如列表或者字典)当作默认参数值。

函数 buggy() 在每次调用时,添加参数 arg 到一个空的列表 result,然
后打印输出一个单值列表。但是存在一个问题:只有在第一次调用时列表是空的,第二次
调用时就会存在之前调用的返回值:
>>> def buggy(arg, result=[]):
... result.append(arg)
... print(result)
...
>>> buggy('a')
['a']
>>> buggy('b') # expect ['b']
['a', 'b']

如果写成下面的样子就会解决刚才的问题:
>>> def works(arg):
... result = []
... result.append(arg)
... return result
...
>>> works('a')
['a']
>>> works('b')
['b']

这样的修改也为了表明是第一次调用跳过一些操作:
>>> def nonbuggy(arg, result=None):
... if result is None:
... result = []
... result.append(arg)
... print(result)
...
>>> nonbuggy('a')
['a']
>>> nonbuggy('b')
['b']

使用*收集位置参数

当参数被用在函数内部时,星号将一组可变数量的位置参数集合成参数值的元组。在下面的例子中 args 是传入到函数 print_args() 的参数值的元组

>>> def print_args(*args): # 习惯用args作为名字 *xxx也可以
... print('Positional argument tuple:', args)
...
什么都不传,则什么也不会返回:
>>> print_args()
Positional argument tuple: ()
   
给函数传入的所有参数都会以元组的形式返回输出:
>>> print_args(3, 2, 1, 'wait!', 'uh...')
Positional argument tuple: (3, 2, 1, 'wait!', 'uh...')
   
如果你的函数同时有限定的位置参数,那么 *args 会收集剩下的参数:
>>> def print_more(required1, required2, *args):
... print('Need this one:', required1)
... print('Need this one too:', required2)
... print('All the rest:', args)
...
>>> print_more('cap', 'gloves', 'scarf', 'monocle', 'mustache wax')
Need this one: cap
Need this one too: gloves
All the rest: ('scarf', 'monocle', 'mustache wax')

使用**收集关键字参数

和*args类似

使用两个星号可以将参数收集到一个字典中,参数的名字是字典的键,对应参数的值是字典的值。下面的例子定义了函数 print_kwargs(),然后打印输出它的关键字参数:

>>> def print_kwargs(**kwargs):
... print('Keyword arguments:', kwargs)
...
现在,使用一些关键字参数调用函数:
>>> print_kwargs(wine='merlot', entree='mutton', dessert='macaroon')
Keyword arguments: {'dessert': 'macaroon', 'wine': 'merlot', 'entree': 'mutton'}
   
在函数内部,kwargs 是一个字典。
如果你把带有 *args **kwargs 的位置参数混合起来,它们会按照顺序解析。和 args
样,调用时不需要参数 kwargs,这也是常见用法。

文档字符串 函数的注释

正如《Python 之禅》(the Zen of Python)中提到的,程序的可读性很重要。建议在函数体

开始的部分附上函数定义说明的文档,这就是函数的文档字符串:

>>> def echo(anything):
... 'echo returns its input argument'
... return anything

调用 Python 函数 help() 可以打印输出一个函数的文档字符串。把函数名传入函数 help()
就会得到参数列表和规范的文档:
>>> help(echo)
Help on function echo in module __main__:
echo(anything)
echo returns its input argument
   
如果仅仅想得到文档字符串:
>>> print(echo.__doc__)
echo returns its input argument

以下是有关文档字符串的内容和格式的一些约定。

第一行应该是对象目的的简要概述。为简洁起见,它不应显式声明对象的名称或类型,因为这些可通过其他方式获得(除非名称恰好是描述函数操作的动词)。这一行应以大写字母开头,以句点结尾。

如果文档字符串中有更多行,则第二行应为空白,从而在视觉上将摘要与其余描述分开。后面几行应该是一个或多个段落,描述对象的调用约定,它的副作用等。

Python 解析器不会从 Python 中删除多行字符串文字的缩进,因此处理文档的工具必须在需要时删除缩进。 这是使用以下约定完成的。 文档字符串第一行 之后 的第一个非空行确定整个文档字符串的缩进量。(我们不能使用第一行,因为它通常与字符串的开头引号相邻,因此它的缩进在字符串文字中不明显。)然后从字符串的所有行的开头剥离与该缩进 "等效" 的空格。 缩进更少的行不应该出现,但是如果它们出现,则应该剥离它们的所有前导空格。 应在转化制表符为空格后测试空格的等效性(通常转化为8个空格)。

下面是一个多行文档字符串的例子:

  • 以下是有关文档字符串的内容和格式的一些约定。

    第一行应该是对象目的的简要概述。为简洁起见,它不应显式声明对象的名称或类型,因为这些可通过其他方式获得(除非名称恰好是描述函数操作的动词)。这一行应以大写字母开头,以句点结尾。

    如果文档字符串中有更多行,则第二行应为空白,从而在视觉上将摘要与其余描述分开。后面几行应该是一个或多个段落,描述对象的调用约定,它的副作用等。

    Python 解析器不会从 Python 中删除多行字符串文字的缩进,因此处理文档的工具必须在需要时删除缩进。 这是使用以下约定完成的。 文档字符串第一行 之后 的第一个非空行确定整个文档字符串的缩进量。(我们不能使用第一行,因为它通常与字符串的开头引号相邻,因此它的缩进在字符串文字中不明显。)然后从字符串的所有行的开头剥离与该缩进 "等效" 的空格。 缩进更少的行不应该出现,但是如果它们出现,则应该剥离它们的所有前导空格。 应在转化制表符为空格后测试空格的等效性(通常转化为8个空格)。

    下面是一个多行文档字符串的例子:

    >>> def my_function():
    ...     """Do nothing, but document it.
    ...
    ...     No, really, it doesn't do anything.
    ...     """
    ...     pass
    ...
    >>> print(my_function.__doc__)
    Do nothing, but document it.

      No, really, it doesn't do anything.

函数标注

函数标注 是关于用户自定义函数中使用的类型的完全可选元数据信息

以字典的形式存放在函数的 __annotations__ 属性中,并且不会影响函数的任何其他部分。

形参标注的定义方式是在形参名称后加上冒号,后面跟一个表达式,该表达式会被求值为标注的值。

返回值标注的定义方式是加上一个组合符号 ->,后面跟一个表达式,该标注位于形参列表和表示 [def] 语句结束的冒号之间。 下面的示例有一个位置参数,一个关键字参数以及返回值带有相应标注:

其实就是一种提示,就像冬天要穿外套,你不穿也行,就是可能会报错,感冒。

>>> def f(ham: str, eggs: str = 'eggs') -> str: # 提示ham的参数应该是字符串(不是也不报错) 返回值的形式也是str 
...     print("Annotations:", f.__annotations__)
...     print("Arguments:", ham, eggs)
...     return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'

匿名函数:lambda()函数

Python 中,lambda 函数是用一个语句表达的匿名函数。可以用它来代替小的函数。

其实就是 lambda 参数 :表达式

这个函数返回两个参数的和: lambda a, b: a+b 。Lambda函数可以在需要函数对象的任何地方使用。它们在语法上限于单个表达式。从语义上来说,它们只是正常函数定义的语法糖。与嵌套函数定义一样,lambda函数可以引用所包含域的变量:

>>>
>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

上面的例子使用一个lambda表达式来返回一个函数。另一个用法是传递一个小函数作为参数:

>>>
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

 

一等公民 函数

函数是Python 中的一等公民,可以把它们(返回值)赋给变量,可以作为参数被其他函数调用,也可以从其他函数中返回值。

闭包函数

  • 1 定义在函数内部

  • 2 对外部作用域有引用

def wrapper():
x = 10
   def inner():
       print(x)  # 引用外部的x
   return inner # 当作值返回 一等公民

 

posted @ 2020-08-23 10:34  阿伟啊啊啊啊  阅读(274)  评论(0编辑  收藏  举报