创建一个用来生成指定边界的斐波那契数列的函数:

>>> 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

该关键字def引入了一个函数定义。在其后必须跟随函数名称和形式参数的括号列表。形成函数体的语句从下一行开始,必须缩进。函数体的第一行语句可以是可选的字符串文本,这个字符串文字是函数的文档字符串或者docstring。有些工具通过docstrings自动生成在线的或可打印的文档,可以让用户以交互方式浏览代码;在编写代码中包含docstrings是一个好的实践。

函数调用会为函数局部变量生成一个新的符号表。更准确地说,所有函数中的变量赋值都是将值存储在局部符号表;而变量引用首先在局部符号表中查找,然后是包含函数的局部符号表,之后是全局符号表,最后是内置名称表。因此,全局变量不能在函数中直接赋值(除非用global语句命名),尽管它们可能被引用。

函数调用的实际参数在被调用函数的局部符号表中被引入,因此,使用call的值传递参数(其值始终是对象引用,而不是对象的值)。【实际上, 引用对象调用 描述的更为准确。如果传入一个可变对象,调用者会看到调用操作带来的任何变化(如子项插入到列表中)。】一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建。

函数定义在当前符号表中引入函数名称。函数名指代的值(即函数体)有一个被Python解释器认定为用户自定义函数的类型。该值可以分配给另一个名称,然后可以将其用作函数。这是一般的重命名机制:

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

如果使用过其他语言的开发者,可能会反对这fib不是一个函数,而是一个过程,因为它不返回一个值。事实上,即使没有return声明的函数也会返回一个值,虽然这是一个相当令人厌烦的值(指None)。这个值被称为None (这是一个内建名称)。如果它是写入的唯一值。你可以看到它,如果确实想看到这个值的输出内容,请使用print() 函数:

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

定义一个返回斐波那契数列数字列表的函数,而不是打印:

>>> def fib2(n): # return Fibonacci series up to n
...     """Return a list containing the Fibonacci series up to n."""
...     result = []
...     a, b = 0, 1
...     while a < n:
... result.append(a)    # see below
... a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # call it
>>> f100# write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

这个例子和往常一样,演示了一些新的Python功能:

1)return语句从函数中返回一个值,不带表达式的return返回None,过程结束后也会返回 None。

2)语句result.append(b)称为链表对象result的一个方法。方法是一个“属于”某个对象的函数,它被命名为 obj.methodename,其中obj有一些对象(这可能是一个表达式),并且methodname是由对象的类型定义的方法的名称。不同的类型定义不同的方法。不同类型可能有同样名字的方法,但不会混淆。(当定义自己的对象类型和方法时,可能会出现这种情况)。示例中演示的append() 方法由链表对象定义,它向链表中加入一个新元素。在示例中它等同于result = result + [a],不过效率更高。

深入Python函数定义

在Python3中,也可以定义包含若干参数的函数。这里有三种可用的形式,也可以组合使用。

1、默认参数值

最常用的一种形式是为一个或多个参数指定默认值。创建一个可以使用比定义时允许的参数更少的参数调用的函数,示例:

def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
    return True
if ok in ('n', 'no', 'nop', 'nope'):
    return False
retries = retries - 1
if retries < 0:
    raise OSError('uncooperative user')
print(complaint)

这个函数可以通过几种不同的方式调用:

1)只给出必要参数:ask_ok('Do you really want to quit?')

2)给出一个可选参数:ask_ok('OK to overwrite the file?', 2)

给出所有的参数:ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

此示例还介绍了in关键字。这测试序列是否包含某个值。

默认值在函数定义作用域被解析,如下所示:

i = 5
def f(arg=i):
    print(arg)
i = 6
f()

将会输出 5。

注意事项:默认值只被赋值一次。这使得当默认值是可变对象时会有所不同,比如列表、字典或者大多数类的实例。例如,下面的函数在后续调用过程中会累积传给它的参数:

def f(a, L=[]):
    L.append(a)
    return L
print(f(1))
print(f(2))
print(f(3))
输出结果为:
[1]
[1, 2] 
[1, 2, 3]

如果你不想让默认值在后续调用中共享默认值,则可以改写此功能:

def f(a, L=None):
    if L is None:
L = []
    L.append(a)
    return L


2、关键字参数

函数可以通过关键字参数的形式来调用,如:keyword = value。以下的函数示例:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")

接受一个必选参数 (voltage) 以及三个可选参数 (state, action, 和 type)。可以用以下的任一方法调用此函数:

parrot(1000)  #1位置参数
parrot(voltage=1000)  #1位置参数
parrot(voltage=1000000, action='VOOOOOM')     #2位置参数
parrot(action='VOOOOOM', voltage=1000000)     #2位置参数
parrot('a million', 'bereft of life', 'jump') #3位置参数
parrot('a thousand', state='pushing up the daisies')  #1位置,1关键字

注意以下几种调用是无效的:

parrot()     # required argument missing
parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
parrot(110, voltage=220)     # duplicate value for the same argument
parrot(actor='John Cleese')  # unknown keyword argument

在函数调用中,关键字参数必须跟随在位置参数的后面。传递的所有关键字参数必须与函数接受的某个参数相匹配 (例如actor不 parrot函数的有效参数),它们的顺序并不重要。这也包括非可选参数(例如parrot(voltage=1000) 也是有效的)。任何参数都不可以多次赋值。以下是由于此限制而失败的示例:

>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

引入一个类似**name的参数时,它接收一个字典,该字典包含了所有未出现在形式参数列表中的关键字参数。这里可能还会组合使用一个类似 *name的形式参数,它接收一个元组,包含了所有没有出现在形式参数列表中的参数值(*name必须在 **name之前出现)。例如,我们这样定义一个函数:

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
print(arg)
    print("-" * 40)
    keys = sorted(keywords.keys())
    for kw in keys:
print(kw, ":", keywords[kw])

它可以像这样调用:

cheeseshop("Limburger", "It's very runny, sir.",
   "It's really very, VERY runny, sir.",
   shopkeeper="Michael Palin",
   client="John Cleese",
   sketch="Cheese Shop Sketch")

当然它会按如下内容打印:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

注意在打印关键字参数之前,通过对关键字字典keys() 方法的结果进行排序,生成了关键字参数名的列表;如果不这样做,打印出来的参数的顺序是未定义的。

3、可变参数列表

最常用的选项是指定可以使用任意数量的参数来调用函数。这些参数被包装进一个元组。在这些可变个数的参数之前,可能会出现零个或多个正常参数。

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

通常,这些可变参数在形式参数列表中最后出现,因为它们会遍历传递给函数的所有剩余输入参数。在*args 参数之后发生的任何形式参数都是“仅关键字”参数,这意味着它们只能用作关键字而不是位置参数。

>>> def concat(*args, sep="/"):
...    return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

4、参数列表的分拆

另有一种相反的情况: 当你要传递的参数已经是一个列表,但要调用的函数却接受分开一个个的参数值。这时候要把已有的列表拆开。例如内建函数range() 需要要独立的start,stop参数。可以在调用函数时加一个*操作符来自动把参数列表拆开:

>>> list(range(3, 6))    # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args))    # call with arguments unpacked from a list
[3, 4, 5]

以同样的方式,字典可以使用**-operator 提供关键字参数:

>>> def parrot(voltage, state='a stiff', action='voom'):
...     print("-- This parrot wouldn't", action, end=' ')
...     print("if you put", voltage, "volts through it.", end=' ')
...     print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

5、Lambda 形式

使用lambda关键字创建小型匿名函数。此函数返回它的两个参数的和:lambda a, b: a+bLambda形式可以用于任何需要的函数对象,它们在语法上仅限于单个表达式。语义上讲,它们只是普通函数定义中的一个语法技巧。类似于嵌套函数定义,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')]

6、文档字符串

这里介绍的文档字符串的概念和格式。第一行应该是对象目的的简短总结。为简洁起见,它不应显式地声明对象的名称或类型,因为这些可以通过其他方式使用(除非名称恰好是描述函数操作的动词)。这条线应该以大写字母开头,以句号结尾。如果文档字符串中有更多行,则第二行应为空白,可视地将摘要与其余描述区分开。接下来的文档应该有一或多段描述对象的调用约定、边界效应等。

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.

8、函数注解

函数注解是用户定义函数使用类型的完全可选的元数据信息。

注解__annotations__作为字典存储在函数的属性中,对函数的任何其他部分没有影响。参数注解(Parameter annotations)是定义在参数名称的冒号后面,后跟一个用来表示注解的值得表达式。返回注解(Return annotations)由一个文字定义->,后跟一个表达式,在参数列表和冒号之间,表示def语句的结尾。以下示例具有位置参数,关键字参数和注解的返回值:

>>> def f(ham: str, eggs: str = 'eggs') -> 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'

关注下方微信公众号“Java精选”(w_z90110),回复关键词领取资料:如Mysql、Hadoop、Dubbo、Spring Boot等,免费领取视频教程、资料文档和项目源码。

Java精选专注程序员推送一些Java开发知识,包括基础知识、各大流行框架(Mybatis、Spring、Spring Boot等)、大数据技术(Storm、Hadoop、MapReduce、Spark等)、数据库(Mysql、Oracle、NoSQL等)、算法与数据结构、面试专题、面试技巧经验、职业规划以及优质开源项目等。其中一部分由小编总结整理,另一部分来源于网络上优质资源,希望对大家的学习和工作有所帮助。

评论

  1. #1

    MT (2019/12/10 22:13:38)回复
    python3函数找了半天终于找到了感谢博主。

分享:

支付宝

微信