Python 基础

参考官方文档The Python Tutorial

An Informal Introduction to Python

除法运算 / 总是返回浮点数,使用 // 向下取整,Java 中的整数除法是向零取整。使用 ** 计算幂,** 的优先级比 - 更高。交互模式下,上次输出的表达式值被赋给变量 _,最好将其视为只读类型,显式为其赋值会创建同名的局部变量,屏蔽内置变量的行为。

1
2
>>> 15 / 3, 11 / 4, 11 // 4, -(3**2), (-3) ** 2
(5.0, 2.75, 2, -9, 9)

字符串使用单引号 '...' 或双引号 "..." 表示,两者没有区别。可以使用三重引号跨行编写字符串,换行符会被包含在字符串中,可以在行尾添加 \ 避免该行为。字符串可以使用 + 合并,也可以使用 * 重复,相邻字符串字面量会自动合并。字符串可以使用索引访问,索引越界会报错,但是切片会自动处理索引越界。字符串是不可变的,要生成不同的字符串应该创建字符串。使用内置函数 len() 获取字符串的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> # 3 times 'un', followed by 'ium'
>>> 3 * 'un' + 'ium'
'unununium'
>>> 'Py' 'thon'
'Python'
>>> text = ('Put several strings within parentheses '
... 'to have them joined together.')
>>> text
'Put several strings within parentheses to have them joined together.'
>>> word = 'Python'
>>> word[42]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>> word[4:42]
'on'
>>> word[0] = 'J'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> 'J' + word[1:]
'Jython'
>>> len(word)
6

列表可以使用 + 合并,列表是可变的可以使用 list.append() 方法添加元素。使用 = 将列表赋值给变量是浅拷贝,该变量引用该列表。列表允许包含不同类型的元素,且可以相互嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
>>> squares = [1, 4, 9, 16, 25]
>>> squares + [36, 49, 64, 81, 100]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> rgb = ["Red", "Green", "Blue"]
>>> rgba = rgb
>>> id(rgb) == id(rgba)
True
>>> a = ['a', 'b', 'c']
>>> n = [1, 2, 3]
>>> x = [a, n]
>>> x
[['a', 'b', 'c'], [1, 2, 3]]

Python 使用缩进组织语句,同一块语句要使用相同大小的缩进,可以使用空格或制表符。

1
2
3
4
5
6
>>> a, b = 0, 1
>>> while a < 1000:
... print(a, end=',')
... a, b = b, a+b
...
0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,

More Control Flow Tools

if 语句由 ifelifelse 组成。for 语句由 forin 组成,使用 range() 函数生成等差数列。组合使用 range()len() 实现按索引迭代序列,或者使用 enumerate() 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 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
1
2
3
4
5
6
>>> 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]
1
2
3
4
5
6
7
8
9
>>> 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

forwhile 循环可以有对应的 else 子句,如果循环在未执行 break 的情况下结束,则会执行该 else 子句。pass 语句不执行任何操作,当语法上需要一个语句,而程序不需要执行任何操作时,可以使用该语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> 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
1
2
3
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...

match 语句用于模式匹配,使用 _ 通配符匹配剩余情况,使用 | 将多个字面值组合到一个模式中。模式之后可以使用 if 子句,如果判断结果为假,则 match 会继续尝试匹配下一个 case 块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Point:
__match_args__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y

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")
1
2
3
4
5
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")

使用 def 定义函数,如果函数内第一个语句是字符串,则该字符串是文档字符串。函数中的所有变量赋值,都会将值存储在局部符号表中。函数中查找变量引用的顺序依次是,当前函数的局部符号表,外层函数的局部符号表,全局符号表,内置名称表。所以尽管可以引用全局变量和外层函数变量,但是赋值会创建本地变量,除非使用 global/nonlocal 语句定义该变量。函数参数是值传递的,值是对象的引用(地址)。

1
2
3
4
5
6
7
8
9
10
11
12
>>> def fib(n):    # write Fibonacci series less than n
... """Print a Fibonacci series less than 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

函数定义将函数名和函数对象关联,存储在当前符号表中。可以使用其他名称指向相同函数对象。没有 return 语句的函数返回 None,通常解释器会隐藏单个 None 值,可以使用 print() 函数打印。

1
2
3
4
5
>>> fib
<function fib at 0x000001B7F0AA1120>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
1
2
3
>>> fib(0)
>>> print(fib(0))
None

可以为函数参数指定默认值,默认值在函数定义时求值。默认值只计算一次,当默认值是对象时,多次调用之间会共享该对象。如果是可变对象,则之后的调用会被之前的影响,如果不想共享默认值则需要显式处理。

1
2
3
4
5
6
7
8
9
>>> i = 5
...
... def f(arg=i):
... print(arg)
...
... i = 6
... f()
...
5
1
2
3
4
5
6
7
8
9
10
11
>>> def f(a, L=[]):
... L.append(a)
... return L
...
... print(f(1))
... print(f(2))
... print(f(3))
...
[1]
[1, 2]
[1, 2, 3]
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def f(a, L=None):
... if L is None:
... L = []
... L.append(a)
... return L
...
... print(f(1))
... print(f(2))
... print(f(3))
...
[1]
[2]
[3]

可以使用 *args 表示可变参数,该参数是元组类型。可以使用 *** 执行解包。

1
2
3
4
5
>>> 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]
1
2
3
4
5
6
7
8
9
>>> 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 表达式创建匿名函数。可以为函数添加注解,注解被存储在函数的 __annotations__ 属性中。

1
2
3
4
5
6
7
8
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
1
2
3
4
5
>>> f = make_incrementor(42)
>>> 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')]
1
2
3
4
5
6
7
8
9
10
>>> 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'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
Arguments: spam eggs
'spam and eggs'

Data Structures

列表推导式的方括号 [] 中包含以下内容:一个表达式,一个 for 子句,之后是零个或多个 for/if 子句。使用 zip() 函数在多个迭代器上并行迭代,每个迭代器返回一个元素组成元组。使用 del 语句删除列表元素。

1
2
3
4
5
6
7
8
9
>>> combs = []
... for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
... combs
...
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
1
2
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
1
2
3
4
5
6
7
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

元组由逗号分隔的值组成,元组是不可变的,但可以包含可变对象,此时可以修改该可变对象。空元组使用 () 创建,单元素元组使用该元素之后跟 , 逗号创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
>>> u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
>>> t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
>>> v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
1
2
3
4
5
6
7
8
>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

可以使用花括号或 set() 函数创建集合,创建空集合只能使用 set() 而不能使用 {}{} 创建的是空字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # fast membership testing
True
>>> 'crabgrass' in basket
False

>>> # Demonstrate set operations on unique letters from two words
>>>
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # letters in both a and b
{'a', 'c'}
>>> a ^ b # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}

字典的键必须是不可变类型,使用 {} 创建空字典,获取不存在键的值会报错。使用 list(d) 获取键列表,使用 sorted(d) 获取排序之后的键列表,使用 innot in 判断键是否存在,使用 dict() 函数创建字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

遍历字典的键值对使用 items() 方法,遍历序列的索引和元素使用 enumerate() 函数,并行遍历使用 zip() 函数,反向遍历使用 reversed() 函数,排序遍历使用 sorted() 函数。

1
2
3
4
5
6
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
... for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
1
2
3
4
5
6
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
1
2
3
4
5
6
7
8
>>> questions = ['name', 'quest', 'favorite color']
... answers = ['lancelot', 'the holy grail', 'blue']
... for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.

使用 isis not 比较两个对象是否是同一个对象。比较运算支持链式操作,a < b == ca < b and b == c 等价。andornot 表示与或非,相当于 Java 中的 &&||!。布尔值有 TrueFalse,使用大写字母开头。在表达式内部赋值需要使用 :=,避免误用 =。布尔运算中使用普通值而不是布尔值时,短路运算的返回值是最后一个已求值的参数。

1
2
3
4
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> def scope_test():
... def do_local():
... spam = "local spam"
...
... def do_nonlocal():
... nonlocal spam
... spam = "nonlocal spam"
...
... def do_global():
... global spam
... spam = "global spam"
...
... spam = "test spam"
... do_local()
... print("After local assignment:", spam)
... do_nonlocal()
... print("After nonlocal assignment:", spam)
... do_global()
... print("After global assignment:", spam)
...
... scope_test()
... print("In global scope:", spam)
...
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

可以为类定义 __init__() 方法,在创建对象之后自动执行。实例对象作为第一个参数隐式传递给方法,obj_name.foo() 相当于 ClassName.foo(obj_name),该参数通常被命名为 self,也可以被命名为其他名称,self 没有特殊含义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Dog:

kind = 'canine' # class variable shared by all instances

def __init__(self, name):
self.name = name # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'

可以动态给对象添加属性或方法,可以在类之外定义函数,然后将其赋值给类属性,毕竟 self 没有特殊含义。在 Python 中,所有值都是对象,都有相应的类型,类型信息存储在 object.__class__ 中。

1
2
3
4
5
6
7
8
9
10
11
>>> class Warehouse:
... purpose = 'storage'
... region = 'west'
...
>>> w1 = Warehouse()
>>> print(w1.purpose, w1.region)
storage west
>>> w2 = Warehouse()
>>> w2.region = 'east'
>>> print(w2.purpose, w2.region)
storage east
1
2
3
4
5
6
7
8
9
10
11
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)

class C:
f = f1

def g(self):
return 'hello world'

h = g

Python 中的方法都是 virtual 方法(动态绑定),属性默认是公有的。不存在私有变量,不过约定以单下划线 _ 开头的变量应该被视为私有的,不论是函数、方法或属性。在类定义中,名称改写(name mangling)会将至少带有两个前缀下划线的标识符 __update 替换为 _Mapping_update,避免子类重写方法影响父类中相应方法的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)

def update(self, iterable):
for item in iterable:
self.items_list.append(item)

__update = update # private copy of original update() method

class MappingSubclass(Mapping):

def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)