流程控制与定义函数
# 流程控制
# while
>>> a, b = 0, 1 # 多重赋值
>>> while a < 10:
... print(a)
... a, b = b, a+b
- 循环体 是 缩进的 :缩进是 Python 组织语句的方式。
- 在交互式命令行里,得为每个缩进的行输入空格(或制表符)。使用文本编辑器可以实现更复杂的输入方式;所有像样的文本编辑器都支持自动缩进。
- 交互式输入复合语句时,要在最后输入空白行表示完成(因为解析器不知道哪一行代码是代码块的最后一行)。注意,同一块语句的每一行的缩进相同。
# if
x = int(input("Please enter an integer: "))
if x < 0:
x = 0
print('Negative changed to zero')
elif x == 0:
print('Zero')
elif x == 1:
print('Single')
else:
print('More')
# for
# 遍历数组
# 度量一些字符串:
words = ['cat', 'window', 'defenestrate']
for w in words:
print(w, len(w))
# 次数遍历
for i in range(5):
print(i)
# 跳出语句
# break
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(f"{n} equals {x} * {n//x}")
break
# continue
for num in range(2, 10):
if num % 2 == 0:
print(f"Found an even number {num}")
continue
print(f"Found an odd number {num}")
# 循环的 else 子句
在 for 或 while 循环中 break 语句可能对应一个 else 子句。 如果循环在未执行 break 的情况下结束,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:
# 循环到底未找到一个因数
print(n, 'is a prime number')
# pass 语句
不会执行任何动作,多用于应付语法检测。
while True:
pass # 无限等待键盘中断 (Ctrl+C)
# match(switch)
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _: # _被作为通配符并必定会匹配成功,类似default
return "Something's wrong with the internet"
case 401 | 403 | 404:
return "Not allowed"
# point 是一个 (x, y) 元组
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")
# 定义函数
def fib2(n): # 返回斐波那契数组直到 n
"""Return a list containing the Fibonacci series up to n."""
result = []
a, b = 0, 1
while a < n:
result.append(a) # 见下
a, b = b, a+b
return result
f100 = fib2(100) # 调用它
f100 # 输出结果
- 定义函数使用关键字
def
- 默认返回值为
None
# 形参 - 默认值
跟 js 类似,可定义默认值
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
reply = input(prompt)
if reply in {'y', 'ye', 'yes'}:
return True
if reply in {'n', 'no', 'nop', 'nope'}:
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
- 本例还使用了关键字
in
(opens new window) ,用于确认序列中是否包含某个值。
重要警告: 默认值只计算一次。默认值为列表、字典或类实例等可变对象时,会产生与该规则不同的结果。例如,下面的函数会累积后续调用时传递的参数:
def f(a, L=[]):
L.append(a)
return L
print(f(1)) # [1]
print(f(2)) # [1, 2]
print(f(3)) # [1, 2, 3]
# 不想在后续调用之间共享默认值时,应以如下方式编写函数:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
# 调用 - 关键字
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 个关键字参数
# 形参 - 特殊参数
# **
- 最后一个形参为
**name
形式时,接收一个字典,该字典包含与函数中已定义形参对应之外的所有关键字参数。 **name
形参可以与*name
形参组合使用(*name
必须在**name
前面),*name
形参接收一个 元组 (opens new window),该元组包含形参列表之外的位置参数。例如,可以定义下面这样的函数:
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
# /、*
函数可使用 /
和 *
来限制调用者传递参数的形式。
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| 位置或关键字 |
| - 仅限关键字
-- 仅限位置
/
前仅限位置,调用者只能通过位置传递/
后*
前可位置或关键字*
后仅限关键字
# 任意实参列表(*name)
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
# *name通常在最后,如果在前面,后面就只能通过关键字传参
def concat(*args, sep="/"):
return sep.join(args)
concat("earth", "mars", "venus")
concat("earth", "mars", "venus", sep=".")
# 解包实参列表
列表
list(range(3, 6)) # 附带两个参数的正常调用
args = [3, 6]
list(range(*args)) # 附带从一个列表解包的参数的调用
字典
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)
# Lambda 表达(匿名函数)
def make_incrementor(n):
return lambda x: x + n # 定义匿名函数 (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]) # (pair) => pair[1]
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
# 文档字符串(注释)
以下是文档字符串内容和格式的约定。
第一行应为对象用途的简短摘要。为保持简洁,不要在这里显式说明对象名或类型,因为可通过其他方式获取这些信息(除非该名称碰巧是描述函数操作的动词)。这一行应以大写字母开头,以句点结尾。
文档字符串为多行时,第二行应为空白行,在视觉上将摘要与其余描述分开。后面的行可包含若干段落,描述对象的调用约定、副作用等。
Python 解析器不会删除 Python 中多行字符串字面值的缩进,因此,文档处理工具应在必要时删除缩进。这项操作遵循以下约定:文档字符串第一行 之后 的第一个非空行决定了整个文档字符串的缩进量(第一行通常与字符串开头的引号相邻,其缩进在字符串中并不明显,因此,不能用第一行的缩进),然后,删除字符串中所有行开头处与此缩进 “等价” 的空白符。不能有比此缩进更少的行,但如果出现了缩进更少的行,应删除这些行的所有前导空白符。转化制表符后(通常为 8 个空格),应测试空白符的等效性。
def my_function():
"""Do nothing, but document it.
No, really, it doesn't do anything.
"""
pass
print(my_function.__doc__)
# 代码规范
缩进,用 4 个空格,不要用制表符。
4 个空格是小缩进(更深嵌套)和大缩进(更易阅读)之间的折中方案。制表符会引起混乱,最好别用。
换行,一行不超过 79 个字符。
这样换行的小屏阅读体验更好,还便于在大屏显示器上并排阅读多个代码文件。
用空行分隔函数和类,及函数内较大的代码块。
最好把注释放到单独一行。
使用文档字符串。
运算符前后、逗号后要用空格,但不要直接在括号内使用:
a = f(1, 2) + g(3, 4)
。类和函数的命名要一致;按惯例,命名类用
UpperCamelCase
,命名函数与方法用lowercase_with_underscores
。命名方法中第一个参数总是用self
(类和方法详见 初探类 (opens new window))。编写用于国际多语环境的代码时,不要用生僻的编码。Python 默认的 UTF-8 或纯 ASCII 可以胜任各种情况。
同理,就算多语阅读、维护代码的可能再小,也不要在标识符中使用非 ASCII 字符。