Python 入门
2024-03-14 10:43:35 1 举报
AI智能生成
廖大佬的python 基础知识点梳理
作者其他创作
大纲/内容
入土
进程线程
多进程
Linux 环境下
os.fork() :子进程永远返回0, 父进程返回子进程ID
import os
print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
os.getpid():返回当前进程ID
os.getppid():返回父进程ID
windows 和 Linux 环境下 可使用 multiprocessing 模块
Process(target=函数名,args=参数)
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步
Pool(子进程数) :创建子进程池,进程数默认为cpu核数
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了
进程间通信
多线程
操作系统能够进行运算调度的最小单位
相关函数方法
threading.Thread(target=线程执行方法名, name='线程名称') 创建子线程
threading.current_thread()函数,它永远返回当前线程的实例
threading.current_thread().name 获取当前线程名称,主线程默认是 MainThread,其他根据子线程参数传入确定
加锁
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
参考文章
ThreadLocal
全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰
import threading
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
# 获取当前线程关联的student:
std = local_school.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
# 获取当前线程关联的student:
std = local_school.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
正则表达式
r前缀,就不用考虑转义的问题
s = r'ABC\-001' # Python的字符串
# 对应的正则表达式字符串不变:
# 'ABC\-001'
# 对应的正则表达式字符串不变:
# 'ABC\-001'
re 模块
match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None
test = '用户输入的字符串'
if re.match(r'正则表达式', test):
print('ok')
else:
print('failed')
if re.match(r'正则表达式', test):
print('ok')
else:
print('failed')
Match 对象 分组
>>> t = '19:05:30'
>>> m = re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$', t)
>>> m.groups()
('19', '05', '30')
>>> m = re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$', t)
>>> m.groups()
('19', '05', '30')
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
re.split() 切分字符串
>>> 'a b c'.split(' ')
['a', 'b', '', '', 'c']
['a', 'b', '', '', 'c']
>>> re.split(r'[\s\,]+', 'a,b, c d')
['a', 'b', 'c', 'd']
['a', 'b', 'c', 'd']
>>> re.split(r'[\s\,\;]+', 'a,b;; c d')
['a', 'b', 'c', 'd']
['a', 'b', 'c', 'd']
默认贪婪匹配,即尽可能多的匹配字符
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')
('102300', '')
非贪婪匹配(也就是尽可能少匹配)
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')
('1023', '00')
re.compile(r'正则表达式') 编译正则表达式,可重复使用
>>> import re
# 编译:
>>> 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')
# 编译:
>>> 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')
常用内建模块
datetime模块
获取当前日期和时间
>>> from datetime import datetime
>>> now = datetime.now() # 获取当前datetime
>>> print(now)
2015-05-18 16:28:07.198690
>>> print(type(now))
<class 'datetime.datetime'>
>>> now = datetime.now() # 获取当前datetime
>>> print(now)
2015-05-18 16:28:07.198690
>>> print(type(now))
<class 'datetime.datetime'>
获取指定日期和时间
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
>>> print(dt)
2015-04-19 12:20:00
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
>>> print(dt)
2015-04-19 12:20:00
datetime 转换为 timestamp
1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
>>> dt.timestamp() # 把datetime转换为timestamp
1429417200.0
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
>>> dt.timestamp() # 把datetime转换为timestamp
1429417200.0
整数位为秒
timestamp转换为datetime
>>> from datetime import datetime
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t)) # 本地时间
2015-04-19 12:20:00
>>> print(datetime.utcfromtimestamp(t)) # UTC时间
2015-04-19 04:20:00
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t)) # 本地时间
2015-04-19 12:20:00
>>> print(datetime.utcfromtimestamp(t)) # UTC时间
2015-04-19 04:20:00
str转换为datetime
>>> from datetime import datetime
>>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
>>> print(cday)
2015-06-01 18:19:59
>>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
>>> print(cday)
2015-06-01 18:19:59
注意转换后的datetime是没有时区信息的
datetime加减,需引入 timedelta
>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 16, 57, 3, 540997)
>>> now + timedelta(hours=10)
datetime.datetime(2015, 5, 19, 2, 57, 3, 540997)
>>> now - timedelta(days=1)
datetime.datetime(2015, 5, 17, 16, 57, 3, 540997)
>>> now + timedelta(days=2, hours=12)
datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 16, 57, 3, 540997)
>>> now + timedelta(hours=10)
datetime.datetime(2015, 5, 19, 2, 57, 3, 540997)
>>> now - timedelta(days=1)
datetime.datetime(2015, 5, 17, 16, 57, 3, 540997)
>>> now + timedelta(days=2, hours=12)
datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)
本地时间转换为UTC时间
>>> from datetime import datetime, timedelta, timezone
>>> tz_utc_8 = timezone(timedelta(hours=8)) # 创建时区UTC+8:00
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 17, 2, 10, 871012)
>>> dt = now.replace(tzinfo=tz_utc_8) # 强制设置为UTC+8:00
>>> dt
datetime.datetime(2015, 5, 18, 17, 2, 10, 871012, tzinfo=datetime.timezone(datetime.timedelta(0, 28800)))
>>> tz_utc_8 = timezone(timedelta(hours=8)) # 创建时区UTC+8:00
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 17, 2, 10, 871012)
>>> dt = now.replace(tzinfo=tz_utc_8) # 强制设置为UTC+8:00
>>> dt
datetime.datetime(2015, 5, 18, 17, 2, 10, 871012, tzinfo=datetime.timezone(datetime.timedelta(0, 28800)))
时区转换
# 拿到UTC时间,并强制设置时区为UTC+0:00:
>>> utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
>>> print(utc_dt)
2015-05-18 09:05:12.377316+00:00
# astimezone()将转换时区为北京时间:
>>> bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
>>> print(bj_dt)
2015-05-18 17:05:12.377316+08:00
# astimezone()将转换时区为东京时间:
>>> tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt)
2015-05-18 18:05:12.377316+09:00
# astimezone()将bj_dt转换时区为东京时间:
>>> tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt2)
2015-05-18 18:05:12.377316+09:00
>>> utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
>>> print(utc_dt)
2015-05-18 09:05:12.377316+00:00
# astimezone()将转换时区为北京时间:
>>> bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
>>> print(bj_dt)
2015-05-18 17:05:12.377316+08:00
# astimezone()将转换时区为东京时间:
>>> tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt)
2015-05-18 18:05:12.377316+09:00
# astimezone()将bj_dt转换时区为东京时间:
>>> tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt2)
2015-05-18 18:05:12.377316+09:00
先通过utcnow()拿到当前的UTC时间,时区转换的关键在于,拿到一个datetime时,要获知其正确的时区,然后强制设置时区,作为基准时间
collections模块
namedtuple 方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2
deque 是为了高效实现插入和删除操作的双向列表,适合用于队列和栈
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])
defaultdict 给dict 设置默认值,避免抛出KeyError
>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入
OrderedDict 有序的dict
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
OrderedDict的Key会按照插入的顺序排列,不是Key本身排序
>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> list(od.keys()) # 按照插入的Key的顺序返回
['z', 'y', 'x']
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> list(od.keys()) # 按照插入的Key的顺序返回
['z', 'y', 'x']
ChainMap 把一组dict串起来并组成一个逻辑上的dict
Counter 是一个简单的计数器
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'programming':
... c[ch] = c[ch] + 1
...
>>> c
Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
>>> c.update('hello') # 也可以一次性update
>>> c
Counter({'r': 2, 'o': 2, 'g': 2, 'm': 2, 'l': 2, 'p': 1, 'a': 1, 'i': 1, 'n': 1, 'h': 1, 'e': 1})
>>> c = Counter()
>>> for ch in 'programming':
... c[ch] = c[ch] + 1
...
>>> c
Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
>>> c.update('hello') # 也可以一次性update
>>> c
Counter({'r': 2, 'o': 2, 'g': 2, 'm': 2, 'l': 2, 'p': 1, 'a': 1, 'i': 1, 'n': 1, 'h': 1, 'e': 1})
argparse 库
parser.add_argument() 设定参数类型 默认值
$ ./backup.py -u root -p hello --database testdb backup.sql
parsed args:
outfile = backup.sql
host = localhost
port = 3306
user = root
password = hello
database = testdb
gzcompress = False
parsed args:
outfile = backup.sql
host = localhost
port = 3306
user = root
password = hello
database = testdb
gzcompress = False
parser.parse_args() 获取有效参数
base64 模块
>>> base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd++//'
>>> base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd--__'
>>> base64.urlsafe_b64decode('abcd--__')
b'i\xb7\x1d\xfb\xef\xff'
b'abcd++//'
>>> base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd--__'
>>> base64.urlsafe_b64decode('abcd--__')
b'i\xb7\x1d\xfb\xef\xff'
hashlib 模块
hashlib.md5()
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
hashlib.sha1()
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())
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())
hmac 模块
Hmac算法:Keyed-Hashing for Message Authentication。它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中
采用Hmac替代我们自己的salt算法,可以使程序算法更标准化,也更安全
采用Hmac替代我们自己的salt算法,可以使程序算法更标准化,也更安全
>>> import hmac
>>> message = b'Hello, world!'
>>> key = b'secret'
>>> h = hmac.new(key, message, digestmod='MD5')
>>> # 如果消息很长,可以多次调用h.update(msg)
>>> h.hexdigest()
'fa4ee7d173f2d97ee79022d1a7355bcf'
>>> message = b'Hello, world!'
>>> key = b'secret'
>>> h = hmac.new(key, message, digestmod='MD5')
>>> # 如果消息很长,可以多次调用h.update(msg)
>>> h.hexdigest()
'fa4ee7d173f2d97ee79022d1a7355bcf'
itertools 模块
count()会创建一个无限的迭代器
>>> import itertools
>>> natuals = itertools.count(1)
>>> for n in natuals:
... print(n)
...
1
2
3
...
>>> natuals = itertools.count(1)
>>> for n in natuals:
... print(n)
...
1
2
3
...
cycle()会把传入的一个序列无限重复下去
>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
... print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
...
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
... print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
...
repeat()负责把一个元素无限重复下去,不过如果提供第二个参数就可以限定重复次数:
>>> ns = itertools.repeat('A', 3)
>>> for n in ns:
... print(n)
...
A
A
A
>>> for n in ns:
... print(n)
...
A
A
A
chain()可以把一组迭代对象串联起来,形成一个更大的迭代器
>>> for c in itertools.chain('ABC', 'XYZ'):
... print(c)
# 迭代效果:'A' 'B' 'C' 'X' 'Y' 'Z'
... print(c)
# 迭代效果:'A' 'B' 'C' 'X' 'Y' 'Z'
groupby()把迭代器中相邻的重复元素挑出来放在一起
>>> for key, group in itertools.groupby('AAABBBCCAAA'):
... print(key, list(group))
...
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']
... print(key, list(group))
...
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']
contextlib 模块
任何对象,只要正确实现了上下文管理,就可以用于with语句
@contextmanager
这个decorator接受一个generator,用yield语句把with ... as var把变量输出出去,然后,with语句就可以正常地工作了
from contextlib import contextmanager
class Query(object):
def __init__(self, name):
self.name = name
def query(self):
print('Query info about %s...' % self.name)
@contextmanager
def create_query(name):
print('Begin')
q = Query(name)
yield q
print('End')
class Query(object):
def __init__(self, name):
self.name = name
def query(self):
print('Query info about %s...' % self.name)
@contextmanager
def create_query(name):
print('Begin')
q = Query(name)
yield q
print('End')
with create_query('Bob') as q:
q.query()
q.query()
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
print("world")
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
print("world")
<h1>
hello
world
</h1>
hello
world
</h1>
closing()
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://www.python.org')) as page:
for line in page:
print(line)
from urllib.request import urlopen
with closing(urlopen('https://www.python.org')) as page:
for line in page:
print(line)
实际上是:
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
urllib 模块
Get 请求
from urllib import request
with request.urlopen('https://api.douban.com/v2/book/2129650') as f:
data = f.read()
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', data.decode('utf-8'))
with request.urlopen('https://api.douban.com/v2/book/2129650') as f:
data = f.read()
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', data.decode('utf-8'))
模拟浏览器发送GET请求
from urllib import request
req = request.Request('http://www.douban.com/')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
with request.urlopen(req) as f:
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', f.read().decode('utf-8'))
req = request.Request('http://www.douban.com/')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
with request.urlopen(req) as f:
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', f.read().decode('utf-8'))
Post 请求
模拟微博登录
xml 模块
start_element事件,在读取<a href="/">时;
char_data事件,在读取python时;
end_element事件,在读取</a>时
char_data事件,在读取python时;
end_element事件,在读取</a>时
html 模块
常用第三方模块
Pillow 图像处理
pip install pillow
requests 访问网络资源
pip install requests
chardet 字符串编码
pip install chardet
psutil 获取系统信息
pip install psutil
venv
python3 -m venv <目录>就可以创建一个独立的Python运行环境
生成目录文件:bin include lib pyvenv.cfg
bin目录的内容,里面有python3、pip3等可执行文件,实际上是链接到Python系统目录的软链接
继续进入bin目录,Linux/Mac用source activate,Windows用activate.bat激活该venv环境
安装相关第三方模块会存放到lib目录下,不影响系统环境
退出当前的venv环境,使用deactivate命令
原理:把系统Python链接或复制一份到venv的环境,用命令source activate进入一个venv环境时,venv会修改相关环境变量,让命令python和pip均指向当前的venv环境
删除只需先确认该venv没有处于“激活”状态,然后直接把整个目录删掉就行
入门
规范
大小写敏感
tab缩进为4个空格
文件开头两行
第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码
数据类型
整数
多个0时允许数字中间 _ 分隔
10000000000 == 10_000_000_000
没有大小限制
不可变变量
float 浮点数
科学计数法
0.000012 == 1.2e-5
1.23 乘 10 的 9次幂 == 1.23e9
也没有大小限制,但是超出一定范围就直接表示为inf(无限大)
str 字符串
函数
ord() 获取字符整数表示
ord('A')
chr() 整数转换成对应字符
chr(12323)
encode() 表示字符串 str 编码为 字节 bytes
如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes
示例
'ABC'.encode('ascii')
b'ABC'
'中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
中文无法编码为 ASCII
decode() 和 encode() 相反
示例
b'ABC'.decode('ascii')
'ABC'
b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
中文
b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
中
errors='ignore' 忽略错误的字节
len()
计算str 包含多少个字符
len('ABC')
3
计算 bytes 包含多少字节数
len(b'ABC')
3
len('中文'.encode('utf-8'))
5
格式化
占位符
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数
格式化方式
%
'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'
'growth rate: %d %%' % 7
'growth rate: 7 %'
用 %% 表示一个 %
format()
'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'
.1f指定了格式化参数(即保留1位小数)
f-string
不可变变量
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'
bool布尔值
True
False
空值
用 None 表示
None不能理解为0,因为0是有意义的,而None是一个特殊的空值
常量
跟普通变量没啥区别,约定使用大写表示
list 列表
一种有序集合
超越索引范围返回 IndexError 错误
索引值带上 - 符号可倒序获取列表元素
classmates[-2]
直接赋值某个索引上的值
classmates[1] = 'Sarah'
函数
len() 获取list 元素个数
append() 追加列表元素
classmates.append('Adam')
insert() 指定位置插入元素
classmates.insert(1, 'Jack')
pop() 删除list末尾元素,返回最后一个元素值
classmates.pop()
classmates.pop(1)
删除指定位置元素
list()
将字符串转换为列表
my_string = "hello"
my_list = list(my_string)
print(my_list) # 输出:['h', 'e', 'l', 'l', 'o']
my_list = list(my_string)
print(my_list) # 输出:['h', 'e', 'l', 'l', 'o']
将元组转换为列表
my_tuple = (1, 2, 3)
my_list = list(my_tuple)
print(my_list) # 输出:[1, 2, 3]
my_list = list(my_tuple)
print(my_list) # 输出:[1, 2, 3]
将集合转换为列表
my_set = {4, 5, 6}
my_list = list(my_set)
print(my_list) # 输出:[4, 5, 6]
my_list = list(my_set)
print(my_list) # 输出:[4, 5, 6]
将字典的键转换为列表
my_dict = {'a': 1, 'b': 2, 'c': 3}
my_list = list(my_dict)
print(my_list) # 输出:['a', 'b', 'c']
my_list = list(my_dict)
print(my_list) # 输出:['a', 'b', 'c']
tuple 元组
一种有序列表 和 list 非常相似,对应元素不可变
初始化形式
classmates = ('python', 'php', 'go')
注意单个元素时的初始化 必须加一个逗号,,来消除歧义
t = (1,)
初始化空tuple
classmates = ()
dict 字典
key-value 形式存放元素,key 不存在直接报错
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
key必须是不可变对象
整数、字符串 可以
list 不可以
无序
校验 key 是否存在
通过 in 判断key 是否存在
'Thomas' in d
get() 方法,不存在返回 None
d.get('Thomas')
d.get('Thomas', -1)
不存在则返回默认值 -1
删除key
pop(key)
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}
75
>>> d
{'Michael': 95, 'Tracy': 85}
同 list 列表区别
查找和插入的速度极快,不会随着key的增加而变慢
需要占用大量的内存,内存浪费多
set 集合
和dict类似,也是一组key的集合,但不存储value,没有重复的key
方法
set(list) 初始化
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}
>>> s
{1, 2, 3}
add(key) 添加元素,可重复添加
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s
{1, 2, 3, 4}
remove(key) 删除元素
>>> s.remove(4)
>>> s
{1, 2, 3}
>>> s
{1, 2, 3}
获取交集、并集
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}
运算符
and:逻辑与运算符,用于判断两个条件是否同时成立,只有当两个条件都为真时,整个表达式才为真
not:逻辑非运算符,用于对一个条件取反,如果条件为真,则返回假;如果条件为假,则返回真
in:成员运算符,用于检查某个值是否存在于序列(如列表、元组、字符串)中,如果存在则返回真,否则返回假
not in:成员运算符的否定形式,用于检查某个值是否不存在于序列中,如果不存在则返回真,否则返回假
is:身份运算符,用于比较两个对象的身份(即内存地址)是否相同,如果是同一个对象则返回真,否则返回假
函数
函数定义
def 定义函数
def my_abs(x):
if x >= 0:
return x
else:
return -x
if x >= 0:
return x
else:
return -x
函数名也是变量
>>> f = abs
>>> f(-10)
10
>>> f(-10)
10
可修改系统函数,但一般不建议这样操作
返回多值
返回值是一个tuple
参数
默认参数
定义默认参数要牢记一点:默认参数必须指向不变对象!
错误示范
def add_end(L=[]):
L.append('END')
return L
L.append('END')
return L
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
['END', 'END']
>>> add_end()
['END', 'END', 'END']
正确示范
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
if L is None:
L = []
L.append('END')
return L
>>> add_end()
['END']
>>> add_end()
['END']
['END']
>>> add_end()
['END']
可变参数
定义
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
sum = 0
for n in numbers:
sum = sum + n * n
return sum
>>> calc(1, 2)
5
>>> calc()
0
5
>>> calc()
0
在函数内部,参数numbers接收到的是一个tuple
外部参数已经是 list 或者 tuple
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
>>> calc(*nums)
14
关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
print('name:', name, 'age:', age, 'other:', kw)
>>> person('Michael', 30)
name: Michael age: 30 other: {}
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
name: Michael age: 30 other: {}
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
传入 dict 字典类型变量
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'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)
print(name, age, city, job)
默认参数
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
print(name, age, city, job)
>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer
Jack 24 Beijing Engineer
参数组合
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
空函数
def nop():
pass
pass
递归函数
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。
由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出
RuntimeError: maximum recursion depth exceeded in comparison
特定递归形式可进行尾递归优化
高阶函数
可以接收另一个函数作为参数
map()
接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list
Iterator 和 Iterable 的区别可查看高级特性——迭代器
reduce()
把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
filter()
过滤序列
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回lis
埃氏筛选法获取质数
sorted()
接收一个key函数来实现自定义的排序
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
[-21, -12, 5, 9, 36]
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
[5, 9, -12, -21, 36]
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
['about', 'bob', 'Credit', 'Zoo']
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
return t[0].lower()
L2 = sorted(L, key=by_name)
print(L2)
// [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
def by_name(t):
return t[0].lower()
L2 = sorted(L, key=by_name)
print(L2)
// [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
反向排序,传入第三个参数 reverse=True
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
['Zoo', 'Credit', 'bob', 'about']
返回函数
函数中返回函数,即闭包函数
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
每次调用都会返回一个新的函数,即使传入相同的参数
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
返回函数不要引用任何循环变量,或者后续会发生变化的变量
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
9
>>> f2()
9
>>> f3()
9
原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9
可再创建一个函数,用该函数的参数绑定当前循环变量当前的值
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
>>> f1()
1
>>> f2()
4
>>> f3()
9
使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。
def inc():
x = 0
def fn():
nonlocal x
x = x + 1
return x
return fn
f = inc()
print(f()) # 1
print(f()) # 2
x = 0
def fn():
nonlocal x
x = x + 1
return x
return fn
f = inc()
print(f()) # 1
print(f()) # 2
匿名函数
lambda 创建,格式:lambda 参数(多个逗号隔开): 表达式
# 创建一个接受两个参数的 lambda 函数,并返回它们的和
add_func = lambda x, y: x + y
# 调用 lambda 函数计算 3 和 4 的和
result = add_func(3, 4)
print(result) # 输出 7
add_func = lambda x, y: x + y
# 调用 lambda 函数计算 3 和 4 的和
result = add_func(3, 4)
print(result) # 输出 7
只能有一个表达式,不用写return,返回值就是该表达式的结果
装饰器
@语法 把相关函数设置到对应需装饰的函数定义处
无参数
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
def now():
print('2015-3-25')
有参数
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
def now():
print('2015-3-25')
使用 functools.wraps 方法获取 func 真正方法名
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
偏函数
把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
其他函数
range()
函数用于创建一个整数序列,通常用于循环中指定范围
for i in range(2, 6):
print(i)
print(i)
生成从 2 到 5(但不包括 6)的整数序列
for i in range(1, 10, 2):
print(i)
print(i)
这将生成从 1 开始,以步长为 2,直到小于 10 的整数序列。输出为:1, 3, 5, 7, 9。
for i in range(5):
print(i)
print(i)
这将生成从 0 到 4(但不包括 5)的整数序列。
IO 函数
open() 打开文件,不存在则抛出 IOError 错误,存在则返回文件描述符对象 file-like Object
打开二进制文件,使用 rb 模式打开
>>> f = open('/Users/michael/test.jpg', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
读取非UTF-8 编码的文本文件,需传入 encoding 参数
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
>>> f.read()
'测试'
>>> f.read()
'测试'
编码不规范会发生 UniocodeDecodeError,可传入 errors='ignore' 忽略报错
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
传入标识符 w 或 wb 可写文本文件或二进制文件
>>> f = open('/Users/michael/test.txt', 'w')
>>> f.write('Hello, world!')
>>> f.close()
>>> f.write('Hello, world!')
>>> f.close()
模式
只读模式
'r':只读模式,文件必须存在,如果文件不存在会引发异常
'rb':以二进制格式打开一个文件,只读。文件必须存在,如果文件不存在会引发异常
只写模式
'w':只写模式,打开一个文件只用于写入。如果文件存在则将其覆盖,如果文件不存在则创建新文件
'wb':以二进制格式打开一个文件,只用于写入。如果文件存在则将其覆盖,如果文件不存在则创建新文件
'w+':读写模式,打开一个文件用于读写。如果文件存在则将其覆盖,如果文件不存在则创建新文件
'wb+':以二进制格式打开一个文件,用于读写。如果文件存在则将其覆盖,如果文件不存在则创建新文件
追加模式
'a':追加模式,打开一个文件用于写入。如果文件存在,文件指针将会放在文件的结尾。如果文件不存在则创建新文件
'ab':以二进制格式打开一个文件,用于追加。如果文件存在,文件指针将会放在文件的结尾。如果文件不存在则创建新文件
'a+':读写模式,打开一个文件用于读写。如果文件存在,文件指针将会放在文件的结尾。如果文件不存在则创建新文件
'ab+':以二进制格式打开一个文件,用于追加和读取。如果文件存在,文件指针将会放在文件的结尾。如果文件不存在则创建新文件
StirngIO() ,读写字符串
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('world!')
6
>>> print(f.getvalue())
world!
>>> f = StringIO()
>>> f.write('world!')
6
>>> print(f.getvalue())
world!
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!
BytesIO() ,读写bytes
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
>>> 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'
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
f.read() 一次性读取文件全部内容,可传入 size ,反复调用 read(size) 避免一次性读取大文件
f.readline() 每次读取一行内容
f.readlines() 一次读取所有内容并按行返回list
f.write() 写入文件
操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')
f.write('Hello, world!')
不必调用f.close()方法
使用 with 语句保险
f.close() 文件使用完毕后必须关闭
因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的
两种关闭方法,避免异常时无法关闭文件描述符
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
with open('/path/to/file', 'r') as f:
print(f.read())
print(f.read())
不必调用f.close()方法
f.seek() 移动文件指针
offset:偏移量,表示从某个位置开始移动的字节数。
whence:可选参数,表示参照位置,可以取三个值
0:表示从文件开头开始计算偏移量(默认值)
1:表示从当前位置开始计算偏移量
2:表示从文件末尾开始计算偏移量
with open('example.txt', 'r') as f:
f.seek(0, 0) # 将文件指针移动到文件开头
data = f.read()
print(data)
f.seek(0, 0) # 将文件指针移动到文件开头
data = f.read()
print(data)
操作文件和目录
序列化
pickle模块
pickle.dumps() 序列化
对象序列化
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
对象序列化写入文件
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()
>>> pickle.dump(d, f)
>>> f.close()
读取文件序列化内容
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
只支持python语法读取
json 模块
json 类型 对应 python 类型
{}
dict
[]
list
"string"
str
1234.56
int或float
true/false
True/False
null
None
即以上python类型为可序列化对象
json 操作
序列化为 json
>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
json.dumps 支持写入 file-like Object
class 对象处理
序列化为 json
print(json.dumps(s, default=lambda obj: obj.__dict__))
default 参数支持传入将class转化为dict 的方法
json 反序列化
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>
return Student(d['name'], d['age'], d['score'])
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>
反序列化为python对象
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{'age': 20, 'score': 88, 'name': 'Bob'}
>>> json.loads(json_str)
{'age': 20, 'score': 88, 'name': 'Bob'}
流程控制
条件判断
score = 'B'
if score == 'A':
print('score is A.')
elif score == 'B':
print('score is B.')
elif score == 'C':
print('score is C.')
else:
print('invalid score.')
if score == 'A':
print('score is A.')
elif score == 'B':
print('score is B.')
elif score == 'C':
print('score is C.')
else:
print('invalid score.')
模式匹配
匹配多值、一定范围、单个值
age = 15
match age:
case x if x < 10:
print(f'< 10 years old: {x}')
case 10:
print('10 years old.')
case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
print('11~18 years old.')
case 19:
print('19 years old.')
case _:
print('not sure.')
match age:
case x if x < 10:
print(f'< 10 years old: {x}')
case 10:
print('10 years old.')
case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
print('11~18 years old.')
case 19:
print('19 years old.')
case _:
print('not sure.')
匹配列表
args = ['gcc', 'hello.c', 'world.c']
# args = ['clean']
# args = ['gcc']
match args:
# 如果仅出现gcc,报错:
case ['gcc']:
print('gcc: missing source file(s).')
# 出现gcc,且至少指定了一个文件:
case ['gcc', file1, *files]:
print('gcc compile: ' + file1 + ', ' + ', '.join(files))
# 仅出现clean:
case ['clean']:
print('clean')
case _:
print('invalid command.')
# args = ['clean']
# args = ['gcc']
match args:
# 如果仅出现gcc,报错:
case ['gcc']:
print('gcc: missing source file(s).')
# 出现gcc,且至少指定了一个文件:
case ['gcc', file1, *files]:
print('gcc compile: ' + file1 + ', ' + ', '.join(files))
# 仅出现clean:
case ['clean']:
print('clean')
case _:
print('invalid command.')
循环
for 循环
sum = 0
for x in range(101):
sum = sum + x
print(sum)
for x in range(101):
sum = sum + x
print(sum)
while 循环
n = 1
while n <= 100:
if n > 10: # 当n = 11时,条件满足,执行break语句
break # break语句会结束当前循环
print(n)
n = n + 1
print('END')
while n <= 100:
if n > 10: # 当n = 11时,条件满足,执行break语句
break # break语句会结束当前循环
print(n)
n = n + 1
print('END')
n = 0
while n < 10:
n = n + 1
if n % 2 == 0: # 如果n是偶数,执行continue语句
continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
print(n)
while n < 10:
n = n + 1
if n % 2 == 0: # 如果n是偶数,执行continue语句
continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
print(n)
高级特性
切片
list 有序集合切片
>>> L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> L[-10:]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> L[10:20]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> L[:10:2]
[0, 2, 4, 6, 8]
[0, 2, 4, 6, 8]
前10个数,每两个取一个:
>>> L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
tuple 元组切片
>>> (0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)
(0, 1, 2)
字符串 切片
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'
迭代
判断是否可迭代
>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
str 迭代
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
... print(ch)
...
A
B
C
list 迭代
使用 for 循环迭代
my_list = [1, 2, 3, 4, 5]
for item in my_list:
print(item)
for item in my_list:
print(item)
使用索引迭代
my_list = [1, 2, 3, 4, 5]
for i in range(len(my_list)):
print(my_list[i])
for i in range(len(my_list)):
print(my_list[i])
使用 enumerate() 迭代
my_list = [1, 2, 3, 4, 5]
for index, value in enumerate(my_list):
print(f"Index: {index}, Value: {value}")
for index, value in enumerate(my_list):
print(f"Index: {index}, Value: {value}")
使用 while 循环迭代
my_list = [1, 2, 3, 4, 5]
i = 0
while i < len(my_list):
print(my_list[i])
i += 1
i = 0
while i < len(my_list):
print(my_list[i])
i += 1
dict 迭代
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b
>>> for key in d:
... print(key)
...
a
c
b
列表生成式
for 循环加上 if 判断
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
[4, 16, 36, 64, 100]
使用两层循环,可以生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
使用两个甚至多个变量
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
所有字符串变为小写
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
if ... else
>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
生成器
列表生成式初始化一个生成器,使用()接收
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
使用 yield 关键字
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
for i in fib(3):
print(i)
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
for i in fib(3):
print(i)
请务必注意:调用generator函数会创建一个generator对象,多次调用generator函数会创建多个相互独立的generator。
用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
迭代器
Iterable 可迭代对象
直接作用于for 循环的对象统称为可迭代对象:Iterable
isinstance()判断一个对象是否是Iterable对象
>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
Iterator 迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator
通过iter() 函数转换
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
True
>>> isinstance(iter('abc'), Iterator)
True
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的
类
类定义
Class 首字母大写类名(object):
class Student(object):
pass
pass
如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类
继承
Class Child(Father):
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
检查变量是否某个类
>>> isinstance(c, Animal)
True
True
在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类
多重继承:MixIn
class MyTCPServer(TCPServer, CoroutineMixIn):
pass
pass
多态
子类覆盖父类相同方法名
方法
与实例绑定的函数
__init__(self, 其他属性字段) 方法
self就指向创建的实例本身
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __init__(self, name, score):
self.name = name
self.score = score
>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59
>>> bart.name
'Bart Simpson'
>>> bart.score
59
属性
私有变量
两个下划线开头 __,只用内部可以访问
和特殊变量区分开,__name__、__score__,特殊变量是可以公开访问的
类属性
直接在class中定义属性
class Student(object):
name = 'Student'
name = 'Student'
实例属性
实例绑定属性的方法是通过实例变量,或者通过self变量
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
和类属性的区别
外部绑定
实例属性
>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print(s.name)
Michael
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print(s.name)
Michael
实例方法
>>> def set_age(self, age): # 定义一个函数作为实例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25
对其他实例不起作用
类方法
>>> def set_score(self, score):
... self.score = score
...
>>> Student.set_score = set_score
... self.score = score
...
>>> Student.set_score = set_score
所有实例均可调用
使用__slots__
仅对当且类起作用的限制外部绑定实例属性字段
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
@property 装饰器
通过属性形式调用方法
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
属性的方法名不要和实例变量重名
class Student(object):
# 方法名称和实例变量均为birth:
@property
def birth(self):
return self.birth
# 方法名称和实例变量均为birth:
@property
def birth(self):
return self.birth
首先转换为方法调用,在执行return self.birth时,又视为访问self的属性,于是又转换为方法调用,造成无限递归,最终导致栈溢出报错RecursionError
魔术方法
__init__(self, ...)
构造器方法,当一个实例被创建时调用,用于初始化对象。
class Example:
def __init__(self, value):
self.value = value
def __init__(self, value):
self.value = value
__str__(self)
定义对象的“非正式”或可打印的字符串表示,通过print()函数或str()调用。
class Example:
def __str__(self):
return "Example object"
def __str__(self):
return "Example object"
__repr__(self)
定义对象的“正式”字符串表示,用于调试和其他开发者需要精确了解对象的情况。如果没有提供__str__,则会使用__repr__作为替代。
__del__(self)
析构器方法,当对象被销毁(回收)之前调用。
class Example:
def __del__(self):
print("Example object is being deleted")
def __del__(self):
print("Example object is being deleted")
__call__(self, ...)
允许类的一个实例像函数那样被调用
class Example:
def __call__(self, value):
print(f'Called with {value}')
obj = Example()
obj(10)
def __call__(self, value):
print(f'Called with {value}')
obj = Example()
obj(10)
__getitem__(self, key)
获取序列的元素,如 obj[key]
class Example:
def __getitem__(self, key):
return key * 2
obj = Example()
print(obj[5])
def __getitem__(self, key):
return key * 2
obj = Example()
print(obj[5])
__setitem__(self, key, value)
设置序列的元素,如 obj[key] = value。
class Example:
def __setitem__(self, key, value):
print(f'Set {key} to {value}')
obj = Example()
obj[1] = 2
def __setitem__(self, key, value):
print(f'Set {key} to {value}')
obj = Example()
obj[1] = 2
__len__(self)
返回容器的大小。
class Example:
def __len__(self):
return 10
obj = Example()
print(len(obj))
def __len__(self):
return 10
obj = Example()
print(len(obj))
__iter__(self) 和 __next__(self)
使对象成为迭代器。
实例(Instance)初始化
实例变量 = 类名()
获取对象信息
type() 函数
判断两个变量类型是否相同
>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
types 模块常量
函数类型:types.FunctionType
方法类型:types.BuildtinFunctionType
匿名函数:types.LambdaType
>>> import types
>>> def fn():
... pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
>>> def fn():
... pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
动态创建类
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
参数说明
class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
另外一种动态创建类的方法:元类 metaclass
ORM 编写会用到,目前只需了解即可
isinstance() 函数
一个对象是否是该类型本身,或者位于该类型的父继承链上
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
dir() 函数
获得一个对象的所有属性和方法
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
getattr()
setattr()
hasattr()
模块
初始化
模块管理
通过包(Package)按目录组织模块,包目录下必须存在__init__.py 文件
mycompany
├─ web
│ ├─ __init__.py
│ ├─ utils.py
│ └─ www.py
├─ __init__.py
├─ abc.py
└─ utils.py
├─ web
│ ├─ __init__.py
│ ├─ utils.py
│ └─ www.py
├─ __init__.py
├─ abc.py
└─ utils.py
文件www.py的模块名就是mycompany.web.www
不能和Python自带的模块名称冲突
检查是否存在,可在python交互环境执行 import 模块 ,成功即存在
代码规范
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
第6行使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;
作用域
非公开(private)变量
_xxx 或者 __xxx 变量不应该被直接使用
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public
安装使用
安装第三方模块
pip install XXXX
pypi 官网
设置PyPI 镜像
pip.ini 或者 pip.conf 文件 新增国内清华大学PyPI 镜像
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
常用工具
Anaconda
一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库
Anaconda会把系统Path中的python指向自己自带的Python,并且,Anaconda安装的第三方模块会安装在Anaconda自己的路径下,不影响系统已安装的Python目录
模块引入
import xxx模块
默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中
增加要搜素的目录
修改sys.path
>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
>>> sys.path.append('/Users/michael/my_py_scripts')
设置环境变量
PYTHONPATH该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。
从一个模块中导入一个或多个特定的函数、类或变量,而不是导入模块中的所有内容
from module_name import some_function, another_function, SomeClass
不建议使用 from module_name import * 导入所有,会导致命名冲突
错误、调试和测试
错误处理
异常捕获
try ... except ... finally ...
try:
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally...')
print('END')
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally...')
print('END')
try ... except ... except ... else... finally ...
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
无论是否异常都会执行finally,可以不设置 finally
所有错误类型都继承自 BaseException
调用栈
从上往下调用
记录错误
logging 模块
logging.basicConfig() 参数
filename:指定日志输出的文件名,如果设置了这个参数,日志信息将会被写入到指定的文件中 否则日志消息会被发送到标准输出(console)
level:指定日志记录的级别阈值,只有大于等于该级别的日志消息才会被记录下来
logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR
format:指定日志输出的格式,可以自定义输出的格式
%(asctime)s
日志记录的时间,格式由datefmt参数指定
%(levelname)s
日志级别(如DEBUG、INFO、WARNING、ERROR、CRITICAL)
%(message)s
日志消息内容
datefmt:指定日期和时间的格式
'%Y-%m-%d %H:%M:%S'
filemode:指定日志文件的打开模式,默认为 'a'(追加模式)
stream:指定日志输出流,如果设置了 stream 参数,则日志信息将会被发送到指定的流对象,而不是文件
logging.basicConfig()只能在程序的启动时被调用一次,后续对它的调用将不会有任何效果,除非在此之前调用了logging.shutdown()
抛出错误
raise 抛出错误实例
# err_raise.py
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
raise语句如果不带参数,就会把当前错误原样抛出。此外,在except中raise一个Error,还可以把一种类型的错误转化成另一种类型
try:
10 / 0
except ZeroDivisionError:
raise ValueError('input error!')
10 / 0
except ZeroDivisionError:
raise ValueError('input error!')
调试
assert 断言
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
assert语句本身就会抛出AssertionError
启动Python解释器时可以用-O参数来关闭assert
logging 日志记录
pdb 调试器
-m pdb 启动,例如 python -m pdb err.py
p 变量名,可查看变量
n,单步执行代码
q,结束调试
单步调试过于麻烦
pdb.set_trace()
设置断点,,程序会自动在pdb.set_trace()暂停并进入pdb调试环境,可以用命令p查看变量,或者用命令c继续运行
# err.py
import pdb
s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)
import pdb
s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)
单元测试
类以及方法定义
测试类需继承 unittest.TestCase
以 test 开头的方法才是测试方法
import unittest
from calc import add
class TestCalc(unittest.TestCase):
def test_add(self):
result = add(3, 5)
self.assertEqual(result, 8)
def test_add_negative_numbers(self):
result = add(-2, -3)
self.assertEqual(result, -5)
if __name__ == '__main__':
unittest.main()
from calc import add
class TestCalc(unittest.TestCase):
def test_add(self):
result = add(3, 5)
self.assertEqual(result, 8)
def test_add_negative_numbers(self):
result = add(-2, -3)
self.assertEqual(result, -5)
if __name__ == '__main__':
unittest.main()
方法
期待抛出指定类型的Error
with self.assertRaises(KeyError):
value = d['empty']
value = d['empty']
assertEqual(a, b):断言 a 和 b 相等
assertTrue(x):断言 x 为 True
assertFalse(x):断言 x 为 False
assertIn(a, b):断言 a 存在于 b 中
assertIsInstance(a, b):断言 a 是 b 的实例
assertRaises(exception, callable, *args, kwargs) 断言调用 callable 时会引发特定的异常
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
要确保在 with 块内执行的代码确实会引发指定的异常,否则测试将失败
运行
新增以下代码后直接运行 python mydict_test.py
if __name__ == '__main__':
unittest.main()
unittest.main()
命令行通过参数-m unittest直接运行单元测试
python -m unittest mydict_test
文档测试
0 条评论
下一页