Python官方教程学习笔记
2.1 命令行传入参数
import sys
print(sys.argv[0], sys.argv[1])
# 0 是文件名,与c接收命令行参数相同
2.2 文件编码及脚本
python源码文件默认是utf-8,也可以规定编码格式。Unix中python脚本可以直接运行,像shell脚本一样。这两个必须放在开头,且顺序固定。
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
3.1 运算符
/
浮点除法,结果一定是浮点数
//
向下取整除法,结果一定是整数(C是向0取整)
**
乘方
交互模式下,_
表示上一个表达式的值
type()
可以查看一个变量的类型
3.2 字符串
如果不希望前置 \
的字符转义成特殊字符,可以使用 原始字符串,在引号前添加 r
即可:print(r"c:\some\name")
字符串字面值可以实现跨行连续输入。实现方式是用三引号:"""..."""
或 '''...'''
,字符串行尾会自动加上回车换行,如果不需要回车换行,在行尾添加 \
即可。示例如下:
print("""\
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
""")
字符串的合并,+
和 *
可以用于变量,空格
只能用于字符串常量。空格
和+
都可以跨行使用。
'py' + 'thon' # 'python'
'py' * 2 + 'thon' # 'pypython'
'py' 'thon' # 'python' 可以跨行使用
字符串支持下标索引(从0开始),python中没有单字符类型,都是用字符串。下标支持负数(从-1开始),负数是从右边开始索引。
字符串支持切片,切片区间左闭右开,并且会自动处理越界索引。
word = "python"
word[1 : -1] # 'ytho'
word[-1 : 1] # ''
word[2 : 1] # ''
word[1 : 100] # 'ython'
word[-100 : 2] # 'py'
python的字符串是immutable
的。要生成不同的字符串,应当新建一个:
>>> 'J' + word[1:]
'Jython'
>>> word[:2] + 'py'
'Pypy'
3.3 列表
支持多种 复合 数据类型,可将不同值组合在一起。支持切片操作。
Python 的赋值语句不复制对象,而是创建目标和对象的绑定关系,相当于指针。
a = [1, 2, 3]
b = a # a b 指向同一个对象
而如果想浅拷贝和深拷贝,要使用copy模块。浅拷贝只拷贝一层,
# copy.copy(x)
# copy.deepcopy(x[, memo])
import copy
x = [1, 2]
y = [3, 4]
a = [x, y]
b = copy.copy(a) # 浅拷贝
b = copy.deepcopy(a) # 深拷贝
浅拷贝时,a中两个元素,分别指向x和y,b中两个元素也分别指向x和y。
b[0] = 'abc' # b : ['abc', y] 更改了b[0]的指向,此时a还是[x, y]
深拷贝使用deepcopy()
时,在拷贝过程中会有一个备忘录memo字典来记录哪些对象是已经拷贝过的;并且可以用户自定义拷贝行为。
想要自定义类的拷贝行为,可以通过定义特殊方法 __copy__()
和 __deepcopy__()
。 前者以实现浅层拷贝操作,该方法不必传入额外参数。 后者以实现深层拷贝操作,应传入一个参数,即 memo
字典。 如果 __deepcopy__()
的实现需要创建一个组件的深层拷贝,它应当调用 deepcopy()
函数并以该组件作为第一个参数而以该 memo 字典作为第二个参数。
list的下面切片操作会返回浅拷贝:
a[:]
list支持合并、添加元素,+=
只能作用与两个list上,作用与append相同:
>>> a + [20, 30]
[[1, 2], [3, 4], 20, 30]
>>> a.append(200)
>>> a += [300]
可以给list切片赋值来改变或者清空list:
a[2:5] = ['abc']
a[2:5] = [] # remove
a[:] = [] # clear the list by repalcing all the elements with an empty listaa
4.1 流程控制语句
- if
if condition :
do_something()
elif condition2 :
do_something()
else
do_something()
- for
python的for语句不像C一样给予用户定义迭代步骤和暂停条件的能力,而是迭代列表或字符串等任意序列,元素的迭代顺序与在序列中出现的顺序一致。
words = ['cat', 'window', 'defenestrate']
for w in words :
print(w, len(w))
要在遍历时修改集合的内容,应该遍历该集合的副本或创建新的集合:
# 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
- range()
用于生成数字序列[a, b),可以设定步长c,默认为1。如果a>b且c>0则生成空序列。
list(range(4)) # [0, 1, 2, 3]
list(range(0, 10, 3)) # [0, 3, 6, 9]
list(range(-10, -100, -30)) # [-10, -40, -70]
range()和len()组合起来可以按索引迭代序列,不过推荐使用enumerate()。
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
for i, v in enumerate(['tic', 'tac', 'toe']):
print(i, v)
# 0 tic
# 1 tac
# 2 toe
- else
循环语句支持 else
子句;for
循环中,可迭代对象中的元素全部循环完毕时,或 while
循环的条件为假时,执行该子句;break
语句终止循环时,不执行该子句。
- pass
pass
语句不执行任何操作,属于占位符。
def initlog(*args):
pass # TODO
- match 模式匹配
python 3.10开始支持。
4.2 函数
函数也可以直接"赋值",类似函数指针:
def f(n):
pass
g = f # g(10) == f(10)
函数也可以接受可变数目的参数,有以下三种方法:
4.2.1 默认值
参数的默认值是在定义过程中,在函数定义处计算的:
i = 5
def f(arg = i)
printf(arg)
i = 6
f() # 5
默认值只会执行一次:
def f(a, L = [])
L.append(a)
return L
print(f(1)) # [1]
print(f(2)) # [1, 2]
如果不想共享默认值,可以这样写:
def (a, L=None):
if L is None:
L = []
L.append(a)
return L
4.2.2 关键字参数
有多个默认值时,可以通过关键字来指定赋值:
def foo(v, state='s', action='v', t='N'):
return None
foo(1)
foo(v = 100)
foo(action = 'VOOM', v = 100)
参数v是位置固定的,叫做positional argument,其他的叫keyword arugment。position argu必须有,但是位置可以不固定(只要通过关键字的方式指出就行)。
4.2.3 特殊参数
上两节的参数可以通过位置或者关键字的方法传递给函数,而下面的方法会让参数只能以位置或者关键字的方法传递。
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
4.2.4 元组做参数
这种方法用的最少,元组做参数,可以接受任何数量的参数。元组后面的形参必须以关键字形式传递。
def concat(*args, sep="/"):
return sep.join(args)
concat("earth", "mars", "venus")
# 'earth/mars/venus'
concat("earth", "mars", "venus", sep=".")
# 'earth.mars.venus'
4.2.5 解包
用*
来解包list
或tuple
,可将容器中元素解包成为位置参数。如果想把元素按关键字方式传递给参数,可以用**
解包dict
。解包list
和tuple
必须容器里的元素个数和函数形参个数相同;解包dict必须key
与函数形参对应。
def foo(a, b, c):
print(a, b, c)
a = [1, 2, 3]
foo(*a) # 1 2 3
a = {
'b':2,
'a':1,
'c':3,
}
foo(**a) # 1 2 3
4.2.6 lambda表达式
是单个表达式,匿名函数的语法糖。形式:lambda parameters : return_value
。
def foo(n): # return a lambda function
return lambda x : x + n
foo(10)(4) # foo(10) return a function; foo(10)(4) return 14
f = foo(20)
f(5) # 25
5.1 List
python中有几种数据结构:List, Tuple, Set, Dict
List用[]
赋值,可以改变其中元素;Tuple用()
赋值,是immutable
的。
List常用操作:list.append(val), list.remove(val), list.sort(), list.count(val), list.reverse(), list.copy(), +=
+=
和 append
作用与字符串时结果不同,+= str
是把str拆成单个字符加到列表尾部,append(str)
是直接把字符串加到列表尾部。
remove(val)
是删除第一个等于val的元素。
sort()
能工作的前提是list中的东西可以比较(是同一个类型)。
元组和列表都可以解包:
a = [1, 2, 3]
b = (1, 2, 3)
x, y, z = a
x, y, z = b
5.1.1 栈
List可以作为栈使用,仅在列表尾部操作,效率很高。
stack = [3, 4, 5]
stack.append(6)
stack.pop() # return 6; stack is [3, 4, 5]
5.1.2 队列
List可以作为队列使用,但在列表头部插入删除都需要移动整个列表里的元素,效率低下。若想使用队列,可以使用 collections.deque
。
from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry") # Terry arrives
queue.append("Graham") # Graham arrives
queue.popleft() # The first to arrive now leaves
# 'Eric'
5.1.3 列表推导式
推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。共有三种推导式:
- 列表(list)推导式
variable = [out_exp_res for out_exp in input_list if out_exp == 2]
- 字典(dict)推导式
{ key_expr: value_expr for value in collection if condition }
- 集合(set)推导式
{ expr for value in collection if condition }
推导式也可以嵌套:
m = [[ i for i in range(x, x + 4)] for x in range(1, 12, 4)]
# [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
# 转置矩阵
mt = [[row[i] for row in m] for i in range(4)]
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
推导式中,元组必须加上括号。
5.2 del语句
del
可以删除一个序列、切片、从索引删除单个元素。
del a[0]
del a[2:4]
del a[:]
5.3 Tuple
元组中的值由逗号隔开,并且可以省略括号。元组是immutable
的,但是可以包含mutable
元素。
t = 1, 2, "hello" # equals to : t = (1, 2, "hello")
t = [1, 2], [3, 4]
t[0][0] = 10
t # ([10, 2], [3, 4])
元组可以解包,常见的例子就是交换元素:
a, b = b, a # actually is a, b = (b, a)
trick: 创建0/1个元素的元素
empty = ()
single = 'hello', # equals to single = ("hello")
5.4 Set
集合是由不重复元素组成的无序的集。它的基本用法包括成员检测和消除重复元素。集合对象也支持像 联合,交集,差集,对称差分等数学运算。
花括号或 set()
函数可以用来创建集合。创建一个空集合只能用 set()
而不能用 {}
,因为后者是创建一个空字典。
a = [1, 2, 1, 4, 2]
a = list(set(a)) # 去重
b = [*{*a}] # 和上面一样
a = set('abracadabra') # {'a', 'r', 'b', 'c', 'd'}
5.5 Dict
和C++中map一样。 list(d)
将返回包含该字典中所有键的列表,按插入次序排列 (如需其他排序,则要使用 sorted(d)
)。要检查字典中是否存在一个特定键,可使用 in
关键字。
dict()
构造函数可以直接从键值对序列里创建字典。
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
# {'sape': 4139, 'guido': 4127, 'jack': 4098}
字典推导式可以从任意的键值表达式中创建字典
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
当关键字是简单字符串时,有时直接通过关键字参数来指定键值对更方便
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
5.6 循环技巧
当在字典中循环时,用 items()
方法可将关键字和对应的值同时取出
for i, j in d.items():
print(i, j)
sape 4139
guido 4127
jack 2098
当在序列中循环时,用 enumerate()
函数可以将索引位置和其对应的值同时取出
for i, j in enumerate(a): # a is a list
print(i, j)
当同时在两个或更多序列中循环时,可以用 zip()
函数将其内元素一一匹配。
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
如果要逆向循环一个序列,可以先正向定位序列,然后调用 reversed()
函数
for i in reversed(range(1, 10, 2)):
想在循环时修改列表内容,一般来说改为创建一个新列表是比较简单且安全的
5.7 条件控制
while
和 if
条件句中可以使用任意操作,而不仅仅是比较操作。
比较操作符 in
和 not in
校验一个值是否在(或不在)一个序列里。操作符 is
和 is not
比较两个对象是不是同一个对象,这只对像列表这样的可变对象比较重要。所有的比较操作符都有相同的优先级,且这个优先级比数值运算符低。
比较操作可以传递。例如 a < b == c
会校验是否 a
小于 b
并且 b
等于 c
。
比较操作可以通过布尔运算符 and
和 or
来组合,并且比较操作(或其他任何布尔运算)的结果都可以用 not
来取反。这些操作符的优先级低于比较操作符;在它们之中,not
优先级最高, or
优先级最低,因此 A and not B or C
等价于 (A and (not B)) or C
。
布尔运算符 and
和 or
也由短路规则。
也可以把比较操作或者逻辑表达式的结果赋值给一个变量,例如
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
请注意 Python 与 C 不同,在表达式内部赋值必须显式地使用 海象运算符 :=
来完成。 这避免了 C 程序中常见的一种问题:想要在表达式中写 ==
时却写成了 =
。
is
和 == 的区别:
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True
a is b # False
6 module
在当前文件夹下有一个module1文件夹,这个文件夹里面有一个fib.py文件,文件里有两个函数fib,fib2。module1文件夹就称为“一个包”。
.
└── module1
└── fib.py
# fib.py
def fib1(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
现在要使用fib和fib2这两个函数,有以下几种方法:
from module1 import fib # 导入整个模块
fib.fib1(10) # 模块名.函数名()
fib.fib2(10)
from module1.fib import fib1, fib2 # 导入单个函数
fib1(10) # 直接使用函数名
fib2(10)
from module1.fib import * # 导入所有函数
from module1.fib import fib as fib3, fib2 # 当多个模块中的函数有重名时,可以定义函数别名
fib1(10) # err
fib2(10)
fib3(10)
编译过的python文件
为了加速模块载入,Python在 __pycache__
目录里缓存了每个模块的编译后版本,名称为 module.*version*.pyc
,其中名称中的版本字段对编译文件的格式进行编码; 它一般使用Python版本号。例如,在CPython版本3.3中,spam.py的编译版本将被缓存为 __pycache__/spam.cpython-33.pyc
。
$ tree .
.
└── module1
├── __pycache__
│ └── fib.cpython-38.pyc
└── fib.py
7 输入输出
7.1 字符串
格式化字符串字面值:在字符串前面加f或F。在 ':'
后传递一个整数可以让该字段成为最小字符宽度。这在使列对齐时很有用。
>>> year = 2016
>>> event = 'Referendum'
>>> f'Results of the {year} {event}'
'Results of the 2016 Referendum'
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print(f'{name:10} ==> {phone:10d}')
...
Sjoerd ==> 4127
Jack ==> 4098
Dcab ==> 7678
python还可以使用旧式的printf字符串格式化方法,而且不止可以用在print里,还可以用在任何字符串定义里。方法:'string' % (values)
s = '%04d %.3f %s' % (32, pi, "hello")
# '0032 3.142 hello'
7.2 读写文件
open(filename, mode)
。mode 可以是 'r'(default) 'w' 'a' 'r+'(read+write)
,但只能选一个。这种方式要手动关闭文件。
通常文件是以 text mode 打开的,这意味着从文件中读取或写入字符串时,都会以指定的编码方式进行编码。如果未指定编码格式,默认值与平台相关 (参见 open()
)。在mode 中追加的 'b'
则以 binary mode 打开文件:现在数据是以字节对象的形式进行读写的。这个模式应该用于所有不包含文本的文件。
在文本模式下读取时,默认会把平台特定的行结束符 (Unix 上的 \n
, Windows 上的 \r\n
) 转换为 \n
。在文本模式下写入时,默认会把出现的 \n
转换回平台特定的结束符。这样在幕后修改文件数据对文本文件来说没有问题,但是会破坏二进制数据例如 JPEG
或 EXE
文件中的数据。请一定要注意在读写此类文件时应使用二进制模式。
最好使用with
的方式来读写文件。
>>> with open('workfile') as f:
... read_data = f.read()
>>> # We can check that the file has been automatically closed.
>>> f.closed
True
API:
# 读取时返回空串''说明读取完毕
f.read() # 默认读取所有文件内容
f.read(size) # 返回字符串(文本模式)或字节串对象(二进制模式)
f.readline() # 读取一行,换行符(\n)留在字符串的末尾
# 文件对象f是可按行迭代的
for line in f:
print(line, end='') # 读取的时候就读进了\n
# 以列表形式读取所有行,2种方式
list(f)
f.readlines()
# f.write(string) 会把 string 的内容写入到文件中,并返回写入的字符数。
f.write('this is a line\n') # return 15
# 写入其他类型时,要先转化为字符串(文本模式)或字节对象(二进制模式)
f.write(str(value))
f.seek(offset, whence) # whence : 0 文件开头; 1 当前文件位置; 2 文件末尾. 默认值为0
8 exception
语法:
try: 可能有异常的语句;
except 异常类型 as 异常变量名 : exception接收所有异常,发生异常时执行
else: 不发生异常时执行
finally: 不管是否发生异常,都执行
In [5]: def divide(x, y):
...: try:
...: result = x / y
...: except Exception as e:
...: print("division by zero")
...: else:
...: print(f"resule is {result}")
...: finally:
...: print("executing finally clause")
9 class
类中所有成员函数的参数都要有self。
In [32]: class foo:
...: def __init__(self, x = 1, y = 2):
...: self.a = [x, y]
...: def time(self):
...: for i in range(len(self.a)):
...: self.a[i] *= 2
类对象支持两种操作:属性(类中变量和函数)引用和实例化。
属性引用 使用 Python 中所有属性引用所使用的标准语法: obj.name
;实例化就是创建一个类的对象,实例对象的唯一操作是属性引用。
class foo:
i = 10
def f(self):
return "hello"
foo.i # 10
foo.f # "hello"
x = foo() # 实例化
类中的变量有两种:类变量和实例变量,实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法,属性名称同时出现在实例和类中,则属性查找会优先选择实例。
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
python比较随意,也可以在生成对象之后再增加成员变量。
a = foo()
a.a # 10
a.x = 20 # 增加一个变量
python中没有“私有变量”,不过python代码遵循一个约定:下划线开头的属性应当被视为“私有”。
python支持多重继承,在子类中如果想调用父类的属性,使用super().name
。子类实例化时,并不会实例化父类。
class faa(foo):
def __init__(self):
print('faa')
super().__init__() # 手动调用父类构造函数