4. 更多控制流工具
除了刚才介绍的while语句之外,Python还使用了通常的 从其他语言中已知的流控制语句,有一些曲折。
4.1. 声明if
也许最著名的语句类型是 if 语句。为 例:
>>>
x = int(input("Please enter an integer: "))
Please enter an integer: 42
if x < 0:
x = 0
print('Negative changed to zero')
elif x == 0:
print('Zero')
elif x == 1:
print('Single')
else:
print('More')
More
可以有零个或多个 elif 部分,其他部分是 自选。关键字“”是“else if”的缩写,很有用 以避免过度缩进。一。。。。。。。。。序列是其他语言中的 OR 语句的替代品。elififelifelifswitchcase
如果要将同一值与多个常量进行比较,或者检查特定类型或 属性,您可能还会发现该语句很有用。欲了解更多信息 详细信息请参阅匹配语句。match
4.2. 声明for
Python 中的 for 语句与你可能使用的语句略有不同 到 C 或 Pascal。而不是总是迭代算术级数 数字(如在 Pascal 中),或让用户能够定义两者 迭代步骤和停止条件(如C),Python的语句 循环访问任何序列(列表或字符串)的项,顺序为 它们按顺序显示。例如(没有双关语):for
>>>
# Measure some strings:
words = ['cat', 'window', 'defenestrate']
for w in words:
print(w, len(w))
cat 3
window 6
defenestrate 12
在循环访问同一集合时修改集合的代码可以 要做对很棘手。相反,循环通常更直接 或创建新集合:
# Create a sample collection
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}
# Strategy: Iterate over a copy
for user, status in users.copy().items():
if status == 'inactive':
del users[user]
# Strategy: Create a new collection
active_users = {}
for user, status in users.items():
if status == 'active':
active_users[user] = status
4.3. 范围() 函数
如果您确实需要迭代数字序列,内置函数 range() 会派上用场。它生成算术级数:
>>>
for i in range(5):
print(i)
0
1
2
3
4
给定的终点永远不会是生成的序列的一部分; 生成 10 个值,长度为 10 的序列的项目的合法索引。它 可以让范围从另一个数字开始,或指定不同的 增量(甚至是负数;有时这称为“步骤”):range(10)
>>>
list(range(5, 10))
[5, 6, 7, 8, 9]
list(range(0, 10, 3))
[0, 3, 6, 9]
list(range(-10, -100, -30))
[-10, -40, -70]
要遍历序列的索引,可以按如下方式组合 range() 和 len():
>>>
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
print(i, a[i])
0 Mary
1 had
2 a
3 little
4 lamb
但是,在大多数情况下,使用 enumerate() 函数很方便,请参阅循环技术。
如果您只打印一个范围,就会发生奇怪的事情:
>>>
range(10)
range(0, 10)
在许多方面,range() 返回的对象的行为就像一个列表, 但事实并非如此。它是一个返回 当您迭代它时所需的序列,但它并没有真正使 列表,从而节省空间。
我们说这样的对象是可迭代的,也就是说,适合作为目标 函数和构造,期望从中可以得到的东西 获取连续物品,直到供应耗尽。我们已经看到 for 语句就是这样的构造,而函数的示例 需要迭代对象的是 sum():
>>>
sum(range(4)) # 0 + 1 + 2 + 3
6
稍后我们将看到更多返回可迭代对象并将可迭代对象作为 参数。在数据结构一章中,我们将更详细地讨论 list()。
4.4. 和语句,以及循环上的子句breakcontinueelse
与 C 语言中一样,break 语句脱离了最内层的封闭 for 或 while 循环。
循环语句可以有一个子句;当循环时执行 通过耗尽可迭代对象(带 for)或当 条件变为假(使用 while),但当循环为 由中断语句终止。例如 以下循环,用于搜索质数:else
>>>
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, '*', n//x)
break
else:
# loop fell through without finding a factor
print(n, 'is a prime number')
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(是的,这是正确的代码。仔细看:该条款属于 for 循环,而不是 if 语句。else
当与循环一起使用时,该子句与 try 语句的子句比与 if 语句的子句有更多的共同点:try 语句的子句运行 当没有发生异常时,循环的子句在未发生时运行。有关语句和异常的详细信息,请参阅处理异常。elseelseelseelsebreaktry
继续语句,也是从 C 借来的,继续下一个 循环迭代:
>>>
for num in range(2, 10):
if num % 2 == 0:
print("Found an even number", num)
continue
print("Found an odd number", num)
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9
4.5. 声明pass
pass 语句不执行任何操作。当语句 语法上是必需的,但程序不需要任何操作。例如:
>>>
while True:
pass # Busy-wait for keyboard interrupt (Ctrl+C)
这通常用于创建最小类:
>>>
class MyEmptyClass:
pass
另一个地方通行证可以用作功能或 处理新代码时的条件体,让您继续思考 在更抽象的层面上。被静默忽略:pass
>>>
def initlog(*args):
pass # Remember to implement this!
4.6. 声明match
match 语句采用表达式并将其值与连续 模式作为一个或多个案例块给出。这是表面上的 类似于 C、Java 或 JavaScript 中的 switch 语句(以及许多 其他语言),但它更类似于模式匹配 像 Rust 或 Haskell 这样的语言。仅匹配的第一个模式 执行,它还可以提取组件(序列元素) 或对象属性)从值转换为变量。
最简单的形式是将主题值与一个或多个文本进行比较:
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"
注意最后一个块:“变量名”充当通配符和 永远不会失败匹配。如果没有大小写匹配,则不执行任何分支。_
您可以使用 (“or”) 将多个文本组合到单个模式中:|
case 401 | 403 | 404:
return "Not allowed"
模式可以看起来像解包分配,并可用于绑定 变量:
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
仔细研究那个!第一个模式有两个文字,并且可以 被认为是上面显示的文字模式的扩展。但 接下来的两个模式组合了一个文本和一个变量,并且 变量绑定来自主题 () 的值。第四 模式捕获两个值,这使得它在概念上类似于 解包作业 .point(x, y) = point
如果使用类来构建数据 您可以使用类名后跟类似于 构造函数,但能够将属性捕获到变量中:
class Point:
x: int
y: int
def where_is(point):
match point:
case Point(x=0, y=0):
print("Origin")
case Point(x=0, y=y):
print(f"Y={y}")
case Point(x=x, y=0):
print(f"X={x}")
case Point():
print("Somewhere else")
case _:
print("Not a point")
您可以将位置参数与一些内置类一起使用,这些类提供 对其属性(例如数据类)进行排序。您还可以定义特定的 通过设置特殊属性在模式中的位置 属性。如果设置为 (“x”、“y”),则以下模式均为 等效(并且全部将属性绑定到变量):__match_args__yvar
Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)
阅读模式的推荐方法是将它们视为您的扩展形式 将放在赋值的左侧,以了解哪些变量将设置为 什么。 只有独立名称(如上所述)由 match 语句分配给。 虚线名称(如)、属性名称(及以上)或类名称 (像上面一样由它们旁边的“(…)”识别)永远不会被分配到。varfoo.barx=y=Point
模式可以任意嵌套。例如,如果我们有一个短 点列表,我们可以像这样匹配它:
match points:
case []:
print("No points")
case [Point(0, 0)]:
print("The origin")
case [Point(x, y)]:
print(f"Single point {x}, {y}")
case [Point(0, y1), Point(0, y2)]:
print(f"Two on the Y axis at {y1}, {y2}")
case _:
print("Something else")
我们可以在模式中添加一个子句,称为“守卫”。如果 守卫是假的,继续尝试下一个案例块。注意 该值捕获发生在评估防护之前:ifmatch
match point:
case Point(x, y) if x == y:
print(f"Y=X at {x}")
case Point(x, y):
print(f"Not on the diagonal")
该声明的其他几个关键特征:
与解包作业一样,元组和列表模式恰好具有 相同的含义,实际上匹配任意序列。重要 例外情况是它们不匹配迭代器或字符串。
Sequence patterns support extended unpacking: and work similar to unpacking assignments. The name after may also be , so matches a sequence of at least two items without binding the remaining items.[x, y, *rest](x, y, *rest)*_(x, y, *_)
Mapping patterns: captures the and values from a dictionary. Unlike sequence patterns, extra keys are ignored. An unpacking like is also supported. (But would be redundant, so it is not allowed.){"bandwidth": b, "latency": l}"bandwidth""latency"**rest**_
Subpatterns may be captured using the keyword:as
case (Point(x1, y1), Point(x2, y2) as p2): ...
will capture the second element of the input as (as long as the input is a sequence of two points)p2
Most literals are compared by equality, however the singletons , and are compared by identity.TrueFalseNone
Patterns may use named constants. These must be dotted names to prevent them from being interpreted as capture variable:
from enum import Enum
class Color(Enum):
RED = 'red'
GREEN = 'green'
BLUE = 'blue'
color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))
match color:
case Color.RED:
print("I see red!")
case Color.GREEN:
print("Grass is green")
case Color.BLUE:
print("I'm feeling the blues :(")
有关更详细的解释和其他示例,您可以查看以教程格式编写的 PEP 636。
4.7. 定义函数
我们可以创建一个函数,将斐波那契级数写入任意 边界:
>>>
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 引入了函数定义。它必须是 后跟函数名称和形式参数的括号列表。 构成函数主体的语句从下一行开始,并且 必须缩进。
函数体的第一个语句可以选择是字符串文本; 此字符串文本是函数的文档字符串或文档字符串。 (有关文档字符串的更多信息,请参阅文档字符串部分。 有些工具使用文档字符串自动生成在线或打印 文档,或让用户以交互方式浏览代码;很好 练习在你编写的代码中包含文档字符串,所以养成习惯。
函数的执行引入了用于本地的新符号表 函数的变量。更准确地说,所有变量赋值 函数将值存储在本地符号表中;而变量引用 首先查看本地符号表,然后查看本地符号表 将函数括起来,然后在全局符号表中,最后在表中 的内置名称。因此,全局变量和封闭函数的变量 不能在函数中直接赋值(除非,对于全局 变量,在全局语句中命名,或者,用于封闭变量 函数,在非本地语句中命名),尽管它们可能是 引用。
函数调用的实际参数(参数)在本地引入 被调用函数时被调用函数的符号表;因此,参数是 使用按值调用传递(其中值始终是对象引用, 而不是对象的值)。1 当一个函数调用另一个函数时, 或者递归地称自己为一个新的 将为该调用创建本地符号表。
函数定义将函数名称与 中的函数对象相关联 当前符号表。解释器识别 该名称作为用户定义的函数。其他名称也可以指向相同的名称 函数对象,也可用于访问函数:
>>>
fib
<function fib at 10042ed0>
f = fib
f(100)
0 1 1 2 3 5 8 13 21 34 55 89
来自其他语言,您可能会反对这不是一个函数,而是 一个过程,因为它不返回值。事实上,即使是没有 return 语句的函数也会返回一个值,尽管这是一个相当无聊的值。这 值称为(它是一个内置名称)。写入值是 通常由解释器抑制,如果它是写入的唯一值。 如果你真的想使用 print(),你可以看到它:fibNoneNone
>>>
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 功能:
return 语句返回函数中的值。 没有表达式参数返回 .脱落 函数的结尾也返回 。returnNoneNone
该语句调用列表对象的方法。方法是一个“属于”对象的函数,被命名为 ,其中是某个对象(这可能是一个表达式), 和 是由对象的类型定义的方法的名称。 不同的类型定义不同的方法。不同类型的方法可能有 相同的名称而不会引起歧义。(可以定义自己的 对象类型和方法,使用类,请参阅类) 示例中所示的方法是为列表对象定义的;它 在列表末尾添加一个新元素。在此示例中,它等效于 ,但效率更高。result.append(a)resultobj.methodnameobjmethodnameappend()result = result + [a]
4.8. 更多关于定义函数的信息
也可以定义具有可变参数数量的函数。 有三种形式,可以组合。
4.8.1. 默认参数值
最有用的形式是为一个或多个参数指定默认值。 这将创建一个函数,该函数可以使用比实际更少的参数调用 定义为允许。例如:
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)
可以通过多种方式调用此函数:
仅给出强制性参数: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!’)
此示例还引入了 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
4.8.2. 关键字参数
也可以使用形式的关键字参数调用函数。例如,以下函数:
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, "!")
接受一个必需参数 () 和三个可选参数 (、 和 )。可以在任何 以下方式:voltagestateactiontype
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
在函数调用中,关键字参数必须跟在位置参数后面。 传递的所有关键字参数必须与其中一个参数匹配 被函数接受(例如 不是函数的有效参数),它们的顺序并不重要。这还包括 非可选参数(例如 也是有效的)。 任何参数都不能多次接收值。 下面是由于此限制而失败的示例:actorparrotparrot(voltage=1000)
>>>
def function(a):
pass
function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() got multiple values for argument 'a'
当表单的最终形式参数存在时,它会收到一个 字典(请参阅映射类型 — 字典),包含除 对应于形式参数的参数。这可以与正式的 表单的参数(在下一小节中描述),其中 接收包含位置的元组 形式参数列表之外的参数。( 必须发生 之前。例如,如果我们定义一个这样的函数:namenamenamename
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)
for kw in keywords:
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.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch
请注意,关键字参数的打印顺序是有保证的 以匹配在函数调用中提供它们的顺序。
4.8.3. 特殊参数
默认情况下,参数可以通过位置传递给 Python 函数 或按关键字明确。为了可读性和性能,有必要 限制参数的传递方式,以便开发人员只需查看 在函数定义中确定项目是否按位置传递,由 位置或关键字,或按关键字。
函数定义可能如下所示:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
其中 和 是可选的。如果使用,这些符号表示 参数通过如何将参数传递给函数: 仅位置、位置或关键字和仅关键字。关键字参数 也称为命名参数。/*
4.8.3.1. 位置或关键字参数
如果函数定义中不存在 并且不存在,则参数可以 按位置或关键字传递给函数。/*
4.8.3.2. 仅位置参数
更详细地查看这一点,可以标记某些参数 作为仅位置。如果仅定位,则参数的顺序很重要,并且 参数不能通过关键字传递。仅位置参数为 放在 (正斜杠)之前。用于逻辑 将仅位置参数与其余参数分开。 如果函数定义中没有,则没有仅位置 参数。///
后面的参数可以是位置或关键字或仅关键字。/
4.8.3.3. 仅关键字参数
要将参数标记为仅关键字,指示必须传递参数 通过关键字参数,在参数列表中将 an 放在第一个仅关键字参数之前。*
4.8.3.4. 函数示例
考虑以下示例函数定义,密切关注 标记和 :/*
>>>
def standard_arg(arg):
print(arg)
def pos_only_arg(arg, /):
print(arg)
def kwd_only_arg(*, arg):
print(arg)
def combined_example(pos_only, /, standard, *, kwd_only):
print(pos_only, standard, kwd_only)
第一个函数定义,最熟悉的形式, 对调用约定和参数没有限制 按位置或关键字传递:standard_arg
>>>
standard_arg(2)
2
standard_arg(arg=2)
2
第二个功能仅限于使用位置 函数定义中存在参数:pos_only_arg/
>>>
pos_only_arg(1)
1
pos_only_arg(arg=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'
第三个函数只允许指示的关键字参数 在函数定义中通过 a:kwd_only_args*
>>>
kwd_only_arg(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
kwd_only_arg(arg=3)
3
最后一个在同一函数中使用所有三个调用约定 定义:
>>>
combined_example(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: combined_example() takes 2 positional arguments but 3 were given
combined_example(1, 2, kwd_only=3)
1 2 3
combined_example(1, standard=2, kwd_only=3)
1 2 3
combined_example(pos_only=1, standard=2, kwd_only=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'
最后,考虑这个函数定义,它在位置参数之间有潜在的冲突,并且具有以下键:name**kwdsname
def foo(name, **kwds):
return ‘name’ in kwds
没有可能的调用会使其返回,因为关键字将始终绑定到第一个参数。例如:True’name’
>>>
foo(1, **{'name': 2})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'name'
>>>
但是使用(仅位置参数),这是可能的,因为它允许作为位置参数和关键字参数中的键:/name’name’
>>>
def foo(name, /, **kwds):
return 'name' in kwds
foo(1, **{'name': 2})
True
换句话说,仅位置参数的名称可以毫无歧义地使用。**kwds
4.8.3.5. 回顾
用例将确定在函数定义中使用哪些参数:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
作为指导:
如果希望参数的名称不是 可供用户使用。当参数名称没有实际数时,这很有用 意思是,如果你想在函数时强制执行参数的顺序 被调用,或者如果你需要采取一些位置参数和任意 关键字。
当名称具有含义且函数定义为 通过明确名称或您想要阻止更容易理解 依赖于所传递参数位置的用户。
对于 API,请使用仅位置以防止中断 API 更改 如果将来修改了参数的名称。
4.8.4. 任意参数列表
最后,最不常用的选项是指定函数可以是 使用任意数量的参数调用。这些参数将被包装 在元组中向上(请参阅元组和序列)。在可变数量的参数之前, 可能会出现零个或多个正常参数。
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.8.5. 解压缩参数列表
当参数已在列表或元组中时,会发生相反的情况 但需要为需要单独位置的函数调用解压缩 参数。例如,内置的 range() 函数需要单独的开始和停止参数。如果它们不能单独提供,请编写 使用 -运算符调用函数以将参数从列表中解压缩 或元组:*
>>>
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.8.6. lambda 表达式
可以使用 lambda 关键字创建小型匿名函数。 此函数返回其两个参数的总和:。 Lambda 函数可以在需要函数对象的任何位置使用。他们是 语法上仅限于单个表达式。从语义上讲,它们只是 用于正常函数定义的语法糖。像嵌套函数 定义,lambda 函数可以从包含 范围:lambda a, b: a+b
>>>
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.8.7. 文档字符串
以下是有关文档内容和格式的一些约定 字符串。
第一行应始终是对象的简短摘要 目的。为简洁起见,它不应显式声明对象的名称或类型, 由于这些可以通过其他方式获得(除非名称恰好是 描述函数操作的动词)。此行应以大写字母开头 字母并以句点结尾。
如果文档字符串中有更多行,则第二行应为 空白,在视觉上将摘要与描述的其余部分分开。这 以下行应该是描述对象调用的一个或多个段落 约定、副作用等。
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.
4.8.8. 函数注释
函数注释是完全可选的元数据 有关用户定义函数使用的类型的信息(有关详细信息,请参阅 PEP 3107 和 PEP 484)。
批注作为字典存储在函数的属性中,对 功能。参数注释由参数名称后面的冒号定义,后跟 通过计算到注释值的表达式。返回注释是 由参数之间的文本 定义,后跟表达式 列表和表示 def 语句结尾的冒号。这 以下示例有一个必需参数、一个可选参数和返回 值注释:annotations->
>>>
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'
4.9. 间奏曲:编码风格
现在你即将编写更长、更复杂的 Python 片段,它是一个 谈论编码风格的好时机。大多数语言都可以编写(或更多) 简洁,格式)不同风格;有些比其他的更具可读性。 让其他人轻松阅读您的代码始终是一个好主意,并且采用 一个好的编码风格对此有很大帮助。
对于 Python 来说,PEP 8 已经成为大多数项目遵循的风格指南; 它促进了非常可读和赏心悦目的编码风格。每条蟒蛇 开发人员应该在某个时候阅读它;以下是最重要的一点 为您提取:
使用 4 空格缩进,不使用制表符。
4 个空格是小缩进之间的良好折衷(允许更大的 嵌套深度)和大缩进(更易于阅读)。选项卡介绍 混乱,最好被排除在外。
换行,使其不超过 79 个字符。
这有助于使用小型显示器的用户,并可以拥有多个 代码文件在较大的显示器上并排显示。
使用空行分隔函数和类,以及较大的 函数中的代码。
如果可能,将评论放在自己的一行上。
使用文档字符串。
在运算符周围和逗号后使用空格,但不要直接在内部使用 括号构造:。a = f(1, 2) + g(3, 4)
一致地命名您的类和函数;约定用于类和函数 和方法。始终用作第一个方法参数的名称 (有关类和方法的更多信息,请参阅类的初探)。UpperCamelCaselowercase_with_underscoresself
如果您的代码打算用于国际 环境。Python 的默认值、UTF-8 甚至普通 ASCII 在任何 箱。
同样,如果只有 说不同语言的人阅读或维持的可能性很小 代码。