Python 编程摘要

  -杨武 2018

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

语言基础有了,要系统学习编程的数据结构与算法,http://interactivepython.org/runestone/static/pythonds/index.html 是一个非常好的资源

Jupiter Notebook tips

tab/shift-tab for code intelligence

%config IPCompleter.greedy=True