Python文档学习笔记(4)--定义函数
定义函数
关键字 def
引入函数的定义。其后必须跟有函数名和以括号标明的形式参数列表。组成函数体的语句从下一行开始,且必须缩进。
执行 一个函数会引入一个用于函数的局部变量的新符号表。
因此,在函数内部无法给一个全局变量直接赋值(除非在一个 global
语句中命名),虽然可以引用它们。
return
语句可以从函数中携带着返回值返回。return
语句不携带任何表达式参数时返回 None
。如果一直执行到整个函数结束还没有碰到 return 语句,也返回 None
。
a = 0 b = 0 def A(a): #定义函数 print("函数内引用a:{}".format(a)) #可以引用 a = 1 global b #全局声明后可以修改 b = 1 print("函数内a:{}".format(a)) print("函数内b:{}".format(b)) return a #return 函数的返回值,没有return返回None r = A print("函数外a:{}".format(a)) print("函数外b:{}".format(b)) print("return:{}".format(r(a))) ################ 函数内引用a:0 函数内a:1 函数内b:1 函数外a:0 函数外b:1 return:1
默认参数值
这个函数可以通过几种方式调用:
- 只提供必须的参数:
ask_ok('Do you really want to quit?')
- 提供可选参数中的一个:
ask_ok('OK to overwrite the file?', 2)
- 或者提供所有参数:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
def ask_ok(prompt, retries=4, reminder='Please try again!'): 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 ValueError('invalid user response') print(reminder)
默认值在函数定义的时刻,在定义的作用域中计算(函数内定义域)
i = 5 def f(arg=i): print(arg) i = 6 f()
默认值只初始化一次。当默认值是一个可变对象(如列表,字典或大多数类的实例)时,默认值会不同。例如,下面的函数在后续调用过程中会累积传给它的参数:
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
关键字参数
函数还可以用kwarg=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, "!")
在函数调用中,关键字参数必须写在位置参数之后。传递的所有关键字参数必须匹配函数接收的参数中的一个(例如,actor
不是parrot
函数的合法参数),它们的顺序并不重要。这同样适用于非可选的参数(例如,parrot(voltage=1000)
也是合法的)。任何参数都不可以多次赋值。
接受一个必需的参数 (voltage
) 和三个可选参数 (state
, action
, and 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
如果在最后存在一个**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
*arguments:"It's really very, VERY runny, sir." **keywords : shopkeeper="Michael Palin", client="John Cleese", sketch="Cheese Shop Sketch"
任意参数的列表
某个函数可以被可变个数的参数调用。这些参数将被封装在一个元组中(参见元组和序列)。在可变数量的参数之前,可能出现零个或多个正常参数。
通常,这些可变的
参数将位于形式参数列表的最后面,因为它们将剩下的传递给函数的所有输入参数都包含进去。出现在*args
之后的任何形式参数都是“非关键字不可”的参数,意味着它们只能用作关键字参数而不能是位置参数。
>>> def concat(*args, sep="/"): ... return sep.join(args) ... >>> concat("earth", "mars", "venus") 'earth/mars/venus' >>> concat("earth", "mars", "venus", sep=".") 'earth.mars.venus'
解包参数列表
当传递的参数已经是一个列表或元组时,情况与之前相反,你要分拆这些参数,因为函数调用要求独立的位置参数。例如,内建的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]
同样的风格,字典可以通过**
操作传递关键字参数:
>>> 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 !
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 >>> 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 解释器不会从多行的文档字符串中去除缩进,所以必要的时候处理文档字符串的工具应当自己清除缩进。使用以下约定即可,第一行 之后 的第一个非空行字符串确定整个文档字符串的缩进的量。(我们不用第一行是因为它通常紧靠着字符串起始的引号,其缩进格式不明晰。)所有行起始的等于缩进量的空格都将被过滤掉。不应该存在缩进更少的行,但如果确实存在,应去除所有其前导空白。应该在展开制表符之后(展开后通常为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.
函数注解
函数注解是关于用户定义的函数使用的类型的元数据信息,它们是完全可选的(更多信息参见PEP 484)。
注解以字典形式存储在函数的__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'
代码风格
若要编写更长更复杂的 Python 代码,是时候谈一谈 编码风格了 。大部分语言都可以有多种(比如更简洁,更格式化)写法,有些写法可以更易读。让你的代码更具可读性,而良好的编码风格对此有很大的帮助。
对Python, PEP 8 已经成为多数项目遵循的代码风格指南;它推动了一种非常易于阅读且赏心悦目的编码风格。每个Python开发者都应该找个时间读一下; 以下是从中提取出来的最重要的一些点:
-
使用 4 个空格的缩进,不要使用制表符。
4 个空格是小缩进(允许更深的嵌套)和大缩进(易于阅读)之间很好的折衷。制表符会引起混乱,最好弃用。
-
折行以确保其不会超过 79 个字符。
这有助于小显示器用户阅读,也可以让大显示器能并排显示几个代码文件。
-
使用空行分隔函数和类,以及函数内的大块代码。
-
如果可能,注释单独成行。
-
使用文档字符串。
-
在操作符两边和逗号之后加空格, 但不要直接在左括号后和右括号前加:
a = f(1, 2) + g(3, 4)
. -
类和函数的命名风格要一致;传统上使用
CamelCase
(驼峰风格)命名类 而用lower_case_with_underscores
(小写字母加下划线)命名函数和方法。方法的第一个参数名称应为self
(查看 初识类 以获得更多有关类和方法的规则)。 -
如果您的代码要在国际环境中使用,不要使用花哨的编码。Python 默认的 UTF-8 或者 ASCII 在任何时候都是最好的选择。
-
同样,只要存在哪怕一丁点可能性有使用另一种不同语言的人会来阅读或维护你的代码,就不要在标识符中使用非 ASCII 字符。