Python 编程摘要
-杨武 2018
- 背景: Guido van Rossum 1989年圣诞假期闲得没事儿干而为 Unix/C 背景的程序员设计的脚本语言,1991 年正式公开发行。
- 环境:学习语言可以考虑 Thonny IDE,Python 发行版中应该都自带有idle集成环境。工业界比较流行 PyCharm 和 Jupyter Notebook.
目前 Python 有 2.7 和 3.x 两个主要的发行版,3.x 2009 年开始发行,对 2.x 不完全兼容。如果工作上并不依赖还没有适配 3.x 的库,建议学习、使用 3.x 。
python 解释器都有交互模式,可以直接在命令行上使用。交互模式下,最后一个表达式的结果会保存在特殊变量 _ 里
编写纯文本的程序,然后 python xxx.py 也可以执行。
Unix/Linux 风格的脚本,可使用 #!/usr/bin/env python 这样的 unix shebang - 安装:不管 Windows/Mac/Linux ,初学者均推荐 Anaconda, Mac 自带,但还是推荐 anaconda 或 virtualenv 避免环境污染
- 可以使用国内镜像加速包安装,如清华大学镜像(版权原因该镜像已停)
- conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
- conda 用 —remove, pip 用 unset 移除镜像渠道
- 如果碰到 ssl 错误
- 出错信息:Could not connect to https://repo.continuum.io/pkgs/free/osx-64/....tar.bz2 Error: Connection error: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590): https://repo.continuum.io/pkgs/free/osx-64/....tar.bz2
- 解决办法
- 找到证书存储位置,然后:conda config --set ssl_verify <pathToYourFile>.crt
- 禁用 ssl 校验(不安全):conda config --set ssl_verify false
- 语言
- 文法
- 一般语句不能跨逻辑行,物理行可以用末尾的 \ 字符连接成逻辑行
- 缩进表达逻辑结构,必须严格一致
- 行首空白的tab(制表符)将视多个空格,空格个数为将行首空格数补全为8的倍数
- 行首空白不要混用tab(制表符)和space(空格)
- 注释:#字符后面的是注释,程序执行时将被忽略,可以是单行也可以接在语句后面
- 源文件编码:Python3 默认为 utf-8,探测到 BOM 则强制 utf-8,可使用编码注释申明
- # -*- coding: utf-8 -*-
- 这个格式 GNU Emacs 也认识
- # vim:fileencoding=utf-8
- 这个格式 VIM 也认识
- 字符编码背景知识 中文编码小知识 - 知乎
- 标识符
- Python 2.x 标识符只能使用 ASCII 集中的大小写字母、_和数字
- Python 3.x 则支持 Unicode 集中的字母、数字和部分符号,完整清单见 https://www.dcl.hpi.uni-potsdam.de/home/loewis/table-3131.html
- 特殊意义的名字
- _开头的符号在 import * 时会忽略
- __id__ 是系统定义的名字
- __id 用于类私有名字,这种类属性会进行名字粉碎(name mangling)以防止冲突
- 命令行参数
import sys;
sys.argv 数组就是命令行参数 - 变量定义
- 赋值即定义:name = value
- 动态类型:赋值时推导类型,可以在不同时间保存不同类型的值
- 强类型:表达式的类型需要显式转换,不支持隐式转换
- 使用目标类型名为函数:int(var)
- 作用域
- 变量在申明的块内有效,不在任何块内的是全局变量
- 局部可以引用全局量作为右值
- 定义在局部块的名字屏蔽全局相同的名字
- 显式引入全局变量:global name
- 如果要修改全局变量,必须显式引入
- 否则是定义了一个新的同名局部变量
- 如果在定义时右值部分也引用到该名字,此时这个变量还没有赋值,会报错
- UnboundLocalError: local variable ‘name’ referenced before assignment
- 基本类型
- 数字:int, float, Decimal, Fraction, complex number (加 j 或 J 后缀)
- 字面量
- 9, 9.2, .1, 2.
- 2/8/十六进制:0b110 0O76 0xFE
- 字面量分段用_,可任意加,求值时忽略: 1_000_000
- 科学计数法格式:31.4e-2
- 算术运算:+ - * / **指数 //整除 %取余
- 比较运算:== != > < >= <=
- 赋值运算:= += -= *= /= %= **= //=
- 位运算:& | ^ ~ << >>
- 字符串(string):
- 定义
- 字面量用单引号或双引号包起来
- 转义序列:\ ,字符串加r前缀表示原始串不进行转义 r’c:\name’
- 二进制串:b’xyz’
- 只能使用ASCII字符
- 多行: 用一对 """或 ''' 包起来的多行文本
- 字面量是不可变的 (immutable)
- 运算
- 连接:字符串可以+ ,连续的字符串字面量会合并
- 重复并拼接:"#" * 8 得到 "########"
- 索引:name[idx],idx为负数时从末尾向前数,注意-0就是0 并不能取最后一个字符(最后一个字符是[-1])
- 切片:name[from:to], [from]~[to-1] 的子串,省略from时从0开始,省略to时到末尾结束。方便起见from/to可视为指向字符之间,比较容易搞清楚切片范围。
- .replace(fromStr, toStr)
- 逻辑(布尔)类型
- 字面量:True, False
- 运算:
- and or not
- 成员运算:in, not in
- if var in (‘a’, ‘b’, ‘c’)
- 身份运算:is, is not
- 引用的是否是同一个对象
- 区分:== 是比较引用变量的值是否相等,但引用的可能2个对象
- 复合类型
- tuple 多元数
- 由逗号分隔的若干个值
- 定义
- t = 1, 2, “a”
- ( v1, v2, v3, … )
- 可嵌套
- 解包赋值:x,y,z = t
- tuple(expr):每个list成员或string字符转化为一元
- 运算:
- tuple+tuple, tuple*int
- len(tuple)
- del tuple #执行之后该变量就未定义了
- cmp(t1,t2), 返回值:0-等长,1-t1长,-1-t1短
- max(t), min(t):取出元组中最大/小的元素
- 列表 (list)
- 字面量:[ 1, 2, 3 ] 方括号内由逗号分隔的一系列值
- 跟字符串一样可以索引、切片
- 列表字面量是可变的 (mutable)
- list[idx] = newValue
- list.append( value )
- 列表可嵌套,即某个元素是另一个列表
- 操作:
- .append(x), .extend(itr), .insert(i, x), .remove(x), .pop([i]), .clear(),
- .popleft() 是 deque 特有的
- .index(x[,start[,end]]), #在范围内找x出现的位置
- .count(x), #数x出现的次数
- .sort(key=None,reverse=False),
- .reverse(), .copy()
- del 语句:del list[idx] 删除该元素
- List Comprehension (列表推导式/列表解析式/列表生成式)
- 用来构造列表的简洁表达
- [expr for name in range]
- 用 for 语句遍历 range 范围取得 name 的每一个值,传进 expr里对name的引用 ,expr 的值做为元素追加到结果列表里
- 可嵌套:expr 部分可以是另一个生成式
- 执行时,最右的生成式相当于循环的最外层
- 集合 (set)
- 字面量:{ val1, val2, … }
- 空集要用 set() 构造,{} 是空字典
- 同列表一样支持生成式
- 定义:s = set()
- 操作:
- s[index]
- .add(one), .update(collection)
- .remove(e), .discard(e) # e不存在前者会异常
- .pop() 弹出最早加入的成员
- 字典 (dictionary)
- 字面量:{ key:value, key:value, … }
- tuple 也可以做为 key,如果其中只包含strings, numbers, or tuples
- 操作
- .keys()
- d[key]=value
- in, not in:作用于 key
- 同样支持生成式
- 语句
- Python 使用缩进决定代码的层次结构
- if 语句
- if bool_expr:
statements
elif expr:
statements
else:
statements
- for 语句
- for var in expr:
expr - range expr
range( from, to, step ) - 流程控制子句
- break;
- continue;
- for-else: 循环结束且没有 break 过时执行
- while bool_expr: 当 bool_expr 计算结果为 True 时重复执行下属的语句块
- pass 显式说明这里啥也不干
- with
- with xxx: 为下级语句块定义一个作用域, xxx 部分返回 ContextManager
- 进入作用域时自动调用 contextManager.__enter__(),
- 作用域结束时自动调用其 __exit__()
- 系统库的许多方法返回对象都是 ContextManager
- File, zipfile.ZipFile, subprocess.Popen, tarfile.TarFile, telnetlib.Telnet, pathlib.Path
- contextLib
- @contextmanager decorator
- 所修饰生成器(generator function)的代码行 yield 之前是 __enter__(), 之后是 __exit__()
- contextlib.ContextDecorator
- 继承该类定义,重载 __enter__(self), __exit__(self, *exc)
- 获得自定义修饰器 @myclass()
- 标准 I/O
- input(prompt)
- print(arg… end=‘’)
- str() 可转换任何类型为可读字符串
- repr() 可转换任何类型为解释器可读字符串
- 文件读写
- f = open(path, mode) #r,w,a,+,b
- 推荐的安全写法:
with open(path) as f:
read_data = f.read()
即使有异常,离开 with block 时文件也会安全关闭
- f.read(), f.readline(), f.write(expr), f.tell(),
- f.seek(ofs, from): from 0=开头向后 1=当前位置向后 2=末尾向前
- f.isatty(), f.truncate()
- 行遍历:
for line in f:
print(line)
- 表达式
- Lambda 表达式
- 定义:lambda param: expr
- 相当于匿名函数,但只能包含一个表达式,不能包含语句和注解(annotation)
- 所含表达式执行时求出的值就是lambda的值
- 运算优先级由低到高:逻辑->成员->身份->赋值->比较->位->算术
- 序列比较:短的小,同长度逐个元素比较字典序(lexicographical)
- 代码单元
- 函数 (function)
- def funcName(formalParameters):
""" 可选的多行 docstring """
statements - 参数
- 定义时
- 参数列表用逗号分隔
- 默认值: param1=expr,提供了默认值的参数在调用时可以不指定值
- 带星参数(starred parameter):表示调用时这一个形参名将接收后续的若干参数值
- *name:从这个位置往传入的参数值组成一个 tuple
- 实现可变参数列表
- **name:形参末尾的这个参数将会收到一个dictionary,其中存有所有调用时用keyword arguments方式传入且不对应其它形参的参数值
- *name 必须出现在 **name 之前
- 注解:函数参数和返回值都可以加注解
- 参数:在形参名后加 : expr,用 paramName.__annotations__ 读
- 返回值:在) 和 : 之间加 -> expr,用 funcName.__annotations__读
- 调用时
- 参数值(arguments)是传值的 (call-by-value)
- 参数都是对象引用,所以传递的是对象引用的值,而不是对象本身的值
- 可用形参名做为关键字指定要传给特定参数的值(keyword arguments):print(‘abc’, end=‘;’)
- 展开参数列表:*list_expr 会将list中的元素展开并依次传给函数参数
- 返回值
- return value;
- 省略 value 则返回特殊值 None
- 即使不写 return 也有返回值 None
- 函数本身也是对象,函数名也是一个变量
- first-class-function
- 函数名本身的值是对函数这个对象的引用
- 引用值可以赋给任意变量,该变量即可做为函数调用
- 匿名函数
- 即lambda表达式
- 函数式编程范式
- 高阶函数: 接收其他函数作为参数的函数
- 内置 map()/reduce()/filter()
- 闭包 closure
- 在函数内可以定义函数,并可作为返回值
- 内部函数可以引用外部函数的变量
- 引用的是符号,而不是当前值
- 所引符号会在闭包执行时,取那一刻的当前值
- 如果引用循环变量,只会取到相同的循环结束时的值
- 柯理化 currying
- 协程 coroutine:Python 3.5 实现了PEP492
- 异步函数: async def fname…,内部可以使用await 表达式、async for 和 async with 语句
- await 表达式:await expr,expr 通常是对协程的调用
- async for 语句
- async with 语句
- 类型提示 : Python 3.7 实现了 PEP484
- def func(arg: type) -> returnType
- 类型别名:alias = type 例如Url = str
- 使用 None:-> None 相当于 ->type(None)
- Callable 类型:用来提示只接受特定的函数签名
- Callable[arg1Type, arg2Type...]
- 范型 generic
- from typing import Mapping, Set
- 提示特定元素类型的容器类型:Set[elemType], Mapping[keyType, valueType]
- TypeVar factory:typing模块新加了这个工厂方法
- T = TypeVar('name') : 接受任意类型
- def first(Sequence[T]) -> T 返回类型为容器元素类型
- T = TypeVar('name', Text, bytes) : 接受指定的基本类型,基本类型也就是不能是参数化的
- Any 可以用来表示任意类型
- 类 (Class)
- 定义
class Name:
”””可选的 docstring ”””
statements
- 变量赋值语句:即定义属性并初始化
- 定义函数: 即定义方法,第一个参数应为 self
- __init__() 是实例初始化方法,在每个实例创建时被调用
- __init(self, …) 可以接收参数
- __new__(cls, *args, **kargs) 重载可控制实例构造过程
- 使用特定名字的函数实现操作符重载
- + __add__(self, other), - __sub__
- [idx] __getitem__(self,idx)/__setitem__(self,idx,val)
- 完整清单: https://docs.python.org/3/reference/datamodel.html#special-method-names
- 可见性:Python 没有可见性控制,惯例 _ 开头的成员是内部接口, 外部不应使用
- 实例化
- obj = ClassName()
- 对象模型
- 类上的属性:所有实例共享的类属性
- __init__() 里 self.name 定义的属性:实例私有的属性
- 实例上可以随时添加属性
- 继承
- class Name(BaseClassName):
- 检查:isinstance(obj, cls), issubclass(sub, base)
- 多重继承:class Name(B1, B2, B3):
- 迭代器支持
- for-in 会对序列容器对象调用 iter(c) 获取迭代器
- 然后调用 i.__next__() 取下一个
- 可用内建函数 next(it) 来调用 it.__next__()
- 遍历超出后会抛出 StopIteration
- 生成器 (generator)
- 定义跟函数一样,内部使用 yield 语句返回值
- 解释器会记录 yield 点
- 并在 g.next() 调用时,从 yield 点之后继续执行
- 实现
- 本质上还是个迭代器
- 解释器会自动生成 __iter__() 和 __next__(),自动在完成后抛出 StopIteration
- 局部变量在调用之间会自动保存
- Generator Expression
- 跟 List Comprehensions 类似,不过不是[ ]里而是在 ( ) 间
- 例如: sum(i*i for i in range(10))
- 模块
- 定义模块:任意.py文件
- __name__
- import 时为模块文件名
- 通过python做为程序执行时,__name__ 会是 “__main__”
- 顶层代码为初始化代码,在引入时会执行
- 每个模块的符号表是私有的,定义全局变量不会冲突,使用者需要用 module.var 引用
- 使用模块
- import name 引入到当前模块中
- from module import symbol_list 可将指定符号加到当前模块中
- 符号由逗号分隔
- * 表示全部符号
- 以上是全局查找,相对路径引用见下面6.2/6.3
- from __future__ import xxx
- 这个称为 future statement,不是常规模块
- https://docs.python.org/3/reference/simple_stmts.html#future-statements
- 用以引进未来版本的新特性,在编译时特殊处理,可以改变关键词的解释
- 需要写在在程序文件最开始
- import __future__ [as name] 只是普通模块引用语句
- 模块查找路径
- built-in
- sys.path
- input script所在的目录
- PYTHONPATH 环境变量
- 安装路径中的默认位置
- 程序可以修改这个变量,影响当前进程内的查找过程!
- 编译和缓存
- 默认会在 __pycache__ 目录按 module.pythonversion.pyc 文件名保存编译过的模块
- compile all 模块可以为目录下的所有模块生成 .pyc 文件
- pyc文件可以脱离源码发行,没有源码时 python 不会使用缓存
- 标准模块
- sys 模块内建在每个python解释器里
- sys.ps1, sys.ps2, sys.path
- dir() 内建函数:可列出模块中定义的名字
- 为指定模块时,则列出当前有定义的所有名字
- package 包管理机制
- 定义
- package 是一个目录,里边有 __init__.py 文件
- __init__.py 可以为空,也可以用 __all__ 列表发布所有的名字
- from m1.m2 import * 时,如果 __all__ 则引入其中所有的名字
- 否则只引入模块里的全部名字,不会递归处理子模块
- 模块的名字空间可以嵌套,用 A.B 来访问
- import sound.effects.echo 对应 sound/effects/echo
- 包间引用
- 绝对引用:子模块需要引用其它模块时,可以始终用完整模块名字空间路径
- 相对引用:但也可以用相对路径指示对应的模块
- from . import m
- from .. import m
- from ..filters.abc import m
- 常用模块
- dir(module);
- help(module);
- import json;
- json.dumps(o[, f]);
- json.load(f)
- import os;
- os.getcwd();
- os.chdir(path);
- os.system(‘cmd’);
- import shutil;
- shutil.copyfile(fromPath, toPath);
- shutil.move(fat);
- import glob;
- glob.glob(‘*.py’)
- import sys;
- sys.argv .stdin, .stdout, .stderr
- import re;
- re.findall(r’regexpr’, str);
- re.sub(r’regexpr’, r’repo’, str)
- math.cos(n), random.random()
- statistics
- .mean(d), .median(d), .variance(d)
- from urllib.request import urlopen;
- with urlopen(url) as resp:
for line in response
line = line.decode(‘utf-8’)
- import smtplib
- server = smtplib.SMTP(host)
- server.sendmail(…)
- server.quit()
- datetime.date.tody()
- import zlib
- t = zlib.compress(text)
- len(str)
- .decompress(zs)
- .crc32(s)
- from timeit import Timer
- Timer(stmt_literals).timeit()
- 环境管理
- 虚拟环境(virtual env)
- python3 自带 venv 模块
- python3 -m venv name
- source name/bin/activate
- name\Scripts\activate.bat for Windows
- pip 包管理
- 默认从 https://pypi.python.org/pypi 安装包
- pip search name
- pip install [--upgrade] name[==version]
- pip uninstall name
- show name; list; freeze > requires.txt
- pip install -r requires.txt 可在别处装全这些依赖
- 使用 Anaconda 发行版时,尽量避免混用conda install 和 pip install,只有 conda 不提供的包才 pip 安装
- 异常处理
- 区分异常与语法错误:异常是语法正确但运行时有问题。
- 异常处理语法
- try:
statements
except ValueError as ename: #
statements
raise #再抛出
except (Err1,Err2):
pass
finally: #clean-up actions
statements
- 抛出异常:raise ValueError(msg)
语言基础有了,要系统学习编程的数据结构与算法,http://interactivepython.org/runestone/static/pythonds/index.html 是一个非常好的资源
Jupiter Notebook tips
tab/shift-tab for code intelligence
%config IPCompleter.greedy=True