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