VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • 深入 Python 流程控制(2)

[1] [1, 2] [1, 2, 3]

如果你不想让默认值在后续调用中累积,你可以像下面一样定义函数:

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

4.7.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) 以及三个可选参数 (stateaction, 和 type)。可以用以下的任一方法调用:

parrot(1000)                                          # 1 positional argument
parrot(voltage=1000)                                  # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

不过以下几种调用是无效的:

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 的参数时,它接收一个字典(参见 Mapping Types — dict ),该字典包含了所有未出现在形式参数列表中的关键字参数。这里可能还会组合使用一个形如 *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() 方法的结果进行排序,生成了关键字参数名的列表;如果不这样做,打印出来的参数的顺序是未定义的。

4.7.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.7.4. 参数列表的分拆

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

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

以同样的方式,可以使用 ** 操作符分拆关键字参数为字典:

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

4.7.5. Lambda 形式

出于实际需要,有几种通常在函数式编程语言例如 Lisp 中出现的功能加入到了 Python。通过 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')]

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

4.7.7. 函数注解

函数注解 是关于用户自定义的函数的完全可选的、随意的元数据信息。无论 Python 本身或者标准库中都没有使用函数注解;本节只是描述了语法。第三方的项目是自由地为文档,类型检查,以及其它用途选择函数注解。

注解是以字典形式存储在函数的 __annotations__ 属性中,对函数的其它部分没有任何影响。参数注解(Parameter annotations)是定义在参数名称的冒号后面,紧随着一个用来表示注解的值得表达式。返回注释(Return annotations)是定义在一个 -> 后面,紧随着一个表达式,在冒号与 ->之间。下面的示例包含一个位置参数,一个关键字参数,和没有意义的返回值注释:

>>> def f(ham: 42, eggs: int = 'spam') -> "Nothing to see here":
...     print("Annotations:", f.__annotations__)
...     print("Arguments:", ham, eggs)
...
>>> f('wonderful')
Annotations: {'eggs': <class 'int'>, 'return': 'Nothing to see here', 'ham': 42}
Arguments: wonderful spam

4.8. 插曲:编码风格

此时你已经可以写一些更长更复杂的 Python 程序,是时候讨论一下 编码风格 了。大多数语言可以写(或者更明白的说, 格式化 )作几种不同的风格。有些比其它的更好读。让你的代码对别人更易读是个好想法,养成良好的编码风格对此很有帮助。

对于 Python,PEP 8 引入了大多数项目遵循的风格指导。它给出了一个高度可读,视觉友好的编码风格。每个 Python 开发者都应该读一下,大多数要点都会对你有帮助:

Footnotes

[1] 实际上, 引用对象调用 描述的更为准确。如果传入一个可变对象,调用者会看到调用操作带来的任何变化(如子项插入到列表中)。


相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号