写这篇笔记的缘由,首先想系统地学习一下 python,之前也片段地学了下,每次用起的时候,都不太想得起具体的用法,要搜索很久。同时,个人感觉教程内容有点杂,于是总结了下方便查阅,也加深一下印象。
Python 基础
输入和输出
输出
- print(),加引号输出字符串,不加输出变量或计算结果
遇到,
输出一个空格,print('The quick brown fox','jumps over','the lazy dog')
输入
- input(),可在加上输入提示参数
数据类型和变量,基本运算
数据类型
-
整数
-
浮点数
-
字符串
r''
表示''
内的字符默认不转义
'''...'''
表示多行内容,直接换行,不用\n
-
布尔值: True 和 False
-
空值:None,不能理解为0
变量
- 必须由大小写字母,数字和下划线组成,且不能以数字开头
基本运算
/
除法,精确的,10 / 3 = 3.333333333//
地板除,两个整数的地板除仍为整数, 10 // 3 = 3
字符串和编码
Python字符串
-
Python 3 字符串以 Unicode 编码
-
ord()
获取字符的整数表示,chr()
将***编码***转换成对应字符 -
网络或磁盘上传输的是字节流类型 bytes
encode()
将字符串转换成指定编码的 bytes'中文'.encode('utf-8')
decode()
将 bytes 转换成指定编码的数据,传入errors='ignore'
忽略无法解码的字节b'\xe4\xb8\xad\xff'.decode('utf-8',errors='ignore')
-
len()
计算字符数,如果传入 bytes ,计算字节数 -
使 Python 解释器以指定编码读取源代码
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
格式化
-
%
对应%d, %f, %s, %x(十六进制整数)
'Hi, %s,you have $%d' % ('Michael', 10000)
;输出单个%
用%%
-
format()
'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.25)
-
f-strings
list 和 tuple
list 列表
-
[]
包裹,classmates = ['Michael', 'Bob', 'Tracy']
-
len()
获取 list 元素个数 -
使用索引访问元素,
-1
获取最后一个元素 -
append()
,将元素将元素追加到末尾 -
insert()
插到索引号为 1 的位置:classmates.insert(1, 'Jack')
-
pop(i)
删除指定位置的元素 -
直接给元素赋值替换元素
tuple 元组
- 一旦初始化就不能改变
- 使用索引访问元素
- 无
append()
,insert()
方法 - 只有一个元素时添加
,
消除歧义t = (1,)
list 和 tuple 区别
- list 可变,tuple 不可变
条件判断
-
if
,else
后面都有:
-
else if
可用elif
替代
循环
-
for … in 循环,
for name in names:
-
range()
生成整数序列 -
while 循环
-
continue,break
dict 和 set
dict 字典
-
{}
包裹,names = {'Michael': 95, 'Bob': 70, 'Tracy': 85}
-
判断 key 存在
1、in
判断是否存在;'Thomas' in names
2、get()
方法,不存在返回None
或者指定的 value ;names.get('Thomas', -1)
-
pop(key)
删除 key ,对应 value 也删除;names.pop('Bob')
-
存放顺序和 key 放入的顺序无关,用空间换取时间
-
作为 key 的对象不可变,如字符串,整数
set
- 和 dict 类似,key 的集合,不存储 value,没有重复的 key
- 创建 set,需提供一个 list 作为输入集合;
s = set([1, 2, 3])
- 重复元素自动被过滤
add(key)
添加元素,重复添加没有效果;s.add(4)
remove(key)
删除元素;s.remove()4
- set 可做交集,并集操作;
s1 & s2
,s1 | s2
不可变对象
- 调用自身任意方法,也不会改变对象自身的内容
函数
调用函数
-
查看函数的名称和参数
python 官方文档:https://docs.python.org/3/library/functions.html
通过help()
查看;help(abs)
-
参数数量,类型不对,会报
TypeError
错误
定义函数
-
def + 函数名 + 参数名 + 冒号;
def my_abs(x):
-
定义空函数:pass
def nop():
pass
函数参数
-
位置参数:普通参数,调用函数时按照位置顺序依次赋值
-
默认参数
在函数中设定默认参数值,调用时可以不写默认参数的值:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
power(5) #25
power(5, 3) #125
-
必选参数在前,默认参数在后,默认参数中变化大的参数在前,变化小的在后
-
多个默认参数时,改变其中某个参数,指明参数名
def student(name, gender, age=6, city='Beijing'):
print('name:', name)
print('gender:', gender)
print('age:', age)
print('city:', city)
student('Adam', 'M', city='Tianjin')
-
默认参数大坑,须指向不变对象
-
可变参数:
*param
传入参数,得到 list
函数定义:
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
调用函数:
calc(1, 2, 3)
传入一个 list 或 tuple(元组),前加 *
,*nums
表示把 nums
这个 list 的所有元素作为可变参数传进去:
calc(*nums)
- 关键字参数:
**kw
传入参数,得到 dict
函数定义:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
调用函数:
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
传入一个 dict,前加 **
,**extra
表示把 extra
这个 dict 的所有 key-value 传进去
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
- 命名关键字参数
限制关键字参数的名字,添加分隔符*
,*
之后的为命名关键字参数
函数定义:
def person(name, age, *, city, job)
print(name, age, city, job)
函数调用
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
已有可变参数,后面命名关键字参数不需要特殊分隔符:
def person(name, age, *args, city, job):
print(name, age, args, city, job)
命名关键字参数可以有默认值
递归函数
- 函数里调用函数自身
高级特征
切片
-
L[0:3]
表示从索引 0 开始,直到索引 3 结束,不包括索引 3 -
索引是 0 可以省略,
L[:3]
-
'abcdef'[1::2]
从索引 1 开始每隔 2 个取 1 个 -
负索引也生效
迭代
-
for ... in
-
迭代 dict ,d 是 dict,
for key in d
迭代的是 key 。若要迭代 value ,for value in d.values()
。同时迭代 key 和 value,for d,v in d.items()
-
判断是否可迭代对象,通过 collections 模块的 iterable 类型判断
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
enumerate()
函数同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
列表生成式
-
List Conprehensions
生成的列表中的内容 + for 迭代 -
生成[1x1, 2x2, 3x3, …, 10x10]
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
也可采用循环方式
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- 加上判断,筛选出偶数的平方
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
- 两层循环,生成全排列
>>> [m + n for m in 'ABC' for n in 'DEF']
['AD', 'AE', 'AF', 'BD', 'BE', 'BF', 'CD', 'CE', 'CF']
- 可调用列表生成内容的方法
把 list 中所有字符串转换成小写
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
生成器(generator)
-
节省空间,一边循环一边计算
-
第一种方法,将列表生成器中
[]
改成()
>>> L = [x * x for x in range(1, 11)]
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> g = (x * x for x in range(1, 11))
>>> g
<generator object <genexpr> at 0x0512BE68>
next()
函数获取 generator 的下一个返回值,没有更多元素时,抛出 StopIteration 错误,一般采用迭代的方式获取
>>> next(g)
1
>>> next(g)
4
- geneterator 生成斐波拉契数列
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
函数遇到 return 返回,变成 generator 的函数,每次调用 next()
的时候执行,遇到 yield
返回,再次执行从上次返回的 yield
语句继续执行。
迭代器
nop
函数式编程
nop
模块
-
一个 .py 文件为一个模块
-
将函数分组到模块中,提高代码可维护性
-
通过包名解决模块名冲突的问题
包名.模块名
mycompany.abc
使用模块
- 模块示列
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
第四行为模块文档注释,任何模块第一个字符串都被视为文档注释
第六行用 __author__
变量写入作者名字
if __name__=='__main__'
用于测试模块功能
- 作用域
正常函数和变量名是公开(public)的,如:abc
,x123
__xxx__
为特殊变量,可以被引用,有特殊函数
_xxx
和__xxx
为非公开的(private)
安装第三方模块
python -m pip install xxx
面向对象编程
类和实例
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
-
类名后括号内为继承的类,默认为 object
-
构造函数
def __init__(self, name, score)
第一个参数为self
操作类的属性self.name
-
内部函数
def print_score(self)
第一个参数为self
-
实例化对象直接调用构造函数,不需要 new
访问限制
-
__
两个下划线开头,私有变量,不能访问,实际名字改变,如__name
改成了_Student__name
,不同 python 版本处理方式不同 -
_
单下划线开头看做私有变量 -
私有变量采用 get 和 set 方法 可检查传入参数
继承和多态
-
被继承的 class 称为基类,父类,或超类(Base class,Super class),继承的 class 称为 子类 (subclass)
-
用
isinstance()
判断变量是否某个类型
>>> isinstance(a, list)
True
- 子类计时父类型又是子类型,父类不是子类型
获取对象信息
-
type()
获取对象类型 -
isinstance()
判断对象是否属于某个类型 -
dir()
获取对象的所有属性和方法,__xxx__
的是特殊的属性和方法 -
getattr()
获取属性或方法,setattr()
设置属性或方法,hasattr()
判断属性或方法是否存在
实例属性和类属性
- 实例属性优先级高于类属性
错误,调试和测试
错误处理
-
使用返回错误代码的方式,繁琐,调试不方便
-
try...except...finally
错误处理机制,try 可能出错的代码
try:
print('try...')
r = 10/0
print('result=', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
执行 r = 10/0
捕获到 ZeroDivisionError
,不再执行 try
中之后语句
执行 except
语句
执行 finally
语句
继续执行剩下语句
-
设置多个
except
语句块捕获多种不同类型的语句
错误类型也是 class ,存在子类父类,except 父类错误时也将子类’一网打尽’了
所有错误从BaseException
类派生
常见错误类型和错误关系:https://docs.python.org/3/library/exceptions.html#exception-hierarchy
-
如果没有捕获到错误可加一个
else
语句,没有错误发生,执行 else 中语句 -
finally
语句不管错误是否发生,最后都会执行 -
调用栈:错误的跟踪信息,异常栈
$ python3 err.py
Traceback (most recent call last):
File "err.py", line 11, in <module>
main()
File "err.py", line 9, in main
bar('0')
File "err.py", line 6, in bar
return foo(s) * 2
File "err.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
-
使用
logging
记录错误信息 -
raise
抛出错误
调试
- 断言:
assert
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
assert 断言失败,抛出 AssertionError
,python 解释器参数 -o
关闭 assert
- logging
控制语句输出到不同的地方,比如 console 和文件
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
-
pdb
python -m pdb err.py
1
查看代码
n
单布执行代码
p 变量名
查看变量
q
结束调试
程序中pdb.set_trace()
设置断点,命令c
继续执行 -
IDE调试
pycharm
单元测试
nop
文档测试
nop
IO编程
数据的输入(input)输出(output)
分为同步和异步,区别为是否等待IO执行的结果,等待的为同步IO
文件读写
-
现代操作系统不允许普通的程序直接操作磁盘,读写文件请求操作系统打开一个文件对象
-
读文件
1.open()
函数传入文件名和标识符f = open('test.txt', 'r')
2.read()
一次读取文件的全部内容;read(size)
每次最多读取 size 个字节的内容;readline()
每次读取一行内容;readlines()
读取所有内容并返回 list
3.close()
文件使用完后必须关闭
4.使用try..finally
确保使用完正确关闭文件,使用with
语句自动调用close()
方法
with open('test.txt', 'r') as f:
print(f.read())
5.二进制文件采用标志符 rb
打开
6.encoding
设置读取文件的编码方式,errors
设置遇到编码错误后的处理方式
f = open('text.txt', 'r', encoding='gbk', errors='ignore')
-
写文件
1.传入标识符'w'
,'wb'
写文本文件或二进制文件
2.wtite()
写入文件
3.务必使用colose()
关闭文件,确保数据全部写入磁盘。写文件时,不是立即写入磁盘,而是在内存中缓存起来,空闲的时候再写。
4.'w'
如果文件已存在,会直接覆盖,'a'
以追加的模式写入
标识符含义:https://docs.python.org/3/library/functions.html#open
-
fire-like object
有read()
方法
StringIO 和 BytesIO
在内存中读写 str 和 byte
getvalue()
获取写入后的值
StringIO:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!
BytesIO:
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
操作文件和目录
-
os.name
获取操作系统类型,os.uname()
获取系统详细信息(uname 在 windows 下不支持)
posix
: Linux、Unix 或 Mac OS X
nt
: Windows -
os.environ
操作系统中的环境变量
os.environ.get('path')
获取 path 变量的值 -
操作文件和目录的函数一部分在
os
,一部分在os.path
os.rname('text.txt', 'text.py')
文件重命名
os.remove('test.py')
删除文件
os.path.abspath('.')
绝对路径
os.mkdir('/Users/michael/testdir')
创建目录
os.rmdir('/Users/michael/testdir')
删除目录
合成路径使用os.path.join()
能正确处理不同操作系统的分隔符
shutil
模块的copyfile()
函数复制文件
os.path.listdir('.')
列出该文件夹下的所有子一级文件夹和文件
实现 dir -l
功能:
import os,time
# 时间戳格式转换
def timenum2str(timenum):
# print('timenum = ', timenum)
t = time.localtime(timenum)
# print('t = ', t)
return time.strftime('%Y-%m-%d %H:%M', t)
def getSize(filepath):
size = str(os.path.getsize(filepath))
return size
def getTime(filepath):
time = os.path.getmtime(filepath)
return timenum2str(time)
if __name__ == '__main__':
for dir_name in [x for x in os.listdir('.')]:
print(getSize(dir_name), '\t', getTime(dir_name), '\t', dir_name)
序列化
pickle
模块实现序列化
1.pick.dumps()
将对象序列化成 bytes 对象,或将对象序列化成 bytes 后写入文件
d = dict(name='Bob', age=20, score=88)
with open('dump.txt', 'wb') as f:
pickle.load(d, f)
2.pick.load()
从 bytes 中反序列化出对象,或者直接从 file-like obgect 中反序列
with open('dump.txt', 'rb') as f:
d = pickle.load(f)
print(d)
- JSON 和 python 内置对象的转换
dumps()
将 python 对象变成 JSON ,dump()
直接将 JSON 写入file-like object
loads()
将 JSON 字符串反序列化,load()
从file-like object
中读取并序列化
进程和线程
对于操作系统来说,一个任务就是一个进程(Process)
进程内的‘子任务’称为线程(Thread),线程是最小的执行单元,每个进程至少由一个线程组成。
多进程
nop
- Unix/Linux 下,
fork()
调用多进程 - windows 使用跨平台
multiprocessing
模块实现多进程 Queue
,Pipes
实现进程间通信
正则表达式
-
\d
一个数字
\w
一个数字或者字母
.
任意字符
\s
一个空格(包含 Tab 等空白符) -
*
任意个数
+
至少一个
?
0个或者一个
{n}
n 个字符
{n,m}
n-m 个字符 -
^
开头,$
结尾
A|B
匹配 A 或 B
[]
表示范围;[0-9a-zA-Z\_]
特殊字符需用\
转义,比如下划线_
-
re 模块
直接用match()
方法
re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
预编译之后再用 match()
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')
()
包裹的为要返回的分组(Group),group()
返回某一个,或 groups()
返回全部
group(0)
匹配的原始字符串,group(1)
第一个 ()
内
常用内建模块
datetime
nop
collections
nop
base64
-
用 64 个字符表示任意二进制数据的编码
-
3 个字节一组,划分为 4 个 6 bit,查表替换
-
少于 3 字节的用
\00
补足,编码末尾加上一个或者两个=
,表示补了多少字节 -
编码后的长度一定是 4 的倍数,补足用
=
补足 -
编码
base64.b64encode(b'abc')
-
解码
base64.b64decode(b'YWJjZA==')
struct
解决 bytes 和其他二进制数据类型的转换
halib
摘要算法:将任意长度的数据转换成固定长度的数据串
- md5
生成固定 128 bit,用 32 个十六进制数表示
数据块很大,可以分块调用update()
,结果一样
hexdigest()
十六进制摘要
使用加盐(salt)的方法加强保护
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 '.encode('utf-8'))
md5.update('in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
- sha1
生成固定 160 bit,用 40 个十六进制数表示
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())
- sha256,sha512
hmac(Keyed-Hashing for Message Authentication)
hash 加盐
itertools
操作迭代函数
图形界面
支持的图形界面的第三方库
TK,wxWidgets,Qt,GTK
#网络编程
TCP/IP 简介
-
IP 协议
标识互联网上计算机
按块发送,不保证到达,也不保证按顺序到达
IPv4,32 位整数:192.168.0.1
IPV6,128 位整数:2001:0db8:85a3:0042:1000:8a2e:0370:7334
(8 个 4 位 16进制,:
隔开) -
TCP
建立在 IP 之上
可靠连接,保证顺序到达
HTTP 协议, SMTP 协议建立在 TCP 之上 -
端口
用于进程间通信
小于 1024 的是 internet 标准服务的端口
常见端口号:
80
Web 服务
21
FTP
25
SMTP
TCP编程
socket 通常表示打开了一个网络连接,打开一个 socket 需要知道目标地址的 ip,端口号,再指定协议类型
客户端
-
导入 socket 库
import socket
-
创建基于 TCP 的 socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
AF_INET
IPV4,AF_INET6
IPV6
SOCK_STREAM
使用面向流的协议 TCP -
连接
s.connect(('www.sina.com', 80))
使用 tuple(元组),指明域名和端口号 -
发送数据
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
-
recv(max)
,接收数据,max
指明最大接收字节
服务端
-
绑定端口
s.bind('127.0.0.1', 9999)
-
监听,指定等待连接的最大数量
s.listen(5)
import socket
import threading
import time
def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Welcome!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
sock.close()
print('Connection from %s:%s closed.' % addr)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
while True:
# 接受一个新连接:
sock, addr = s.accept()
# 创建新线程来处理TCP连接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9999))
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
# 发送数据:
s.send(data)
print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()
UDP 编程
- 面向无连接的协议,不需要建立连接,只需要知道 IP 和端口号就可直接发送数据包
电子邮件
MUA(Mail User Agent) —— 邮件用户代理
MTA(Mail Transfer Agent) —— 邮件传输代理
MDA(Mail Delivery Agent) —— 邮件投递代理
电子邮件流程
发件人 -> MUA -> MTA -> 若干MTA -> MDA <- MUA <- 收件人
发邮件,MUA 和 MTA 使用 SMTP(Simple Mail Transfer Protocol)
收邮件,MDA 和 MUA 使用 POP:Post Office Protocol,目前版本是3,俗称POP3;IMAP:Internet Message Access Protocol,目前版本是4 两种协议
SMTP 发送邮件
模块 email
构造邮件,smtp
发送邮件
POP3收取邮件
nop
Web开发
nop
异步 IO
nop