0%

Python实用特性:线程池与装饰器详解

1.线程池ThreadPoolExecutor

https://www.jianshu.com/p/120b61aa4cee

2. 装饰器

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能
先来看一个简单例子,如果你要对多个函数进行统计运行时间,不使用装饰器会是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from time import time, sleep

def fun_one():
start = time()
sleep(1)
end = time()
cost_time = end - start
print("func one run time {}".format(cost_time))

def fun_two():
start = time()
sleep(1)
end = time()
cost_time = end - start
print("func two run time {}".format(cost_time))

在每个函数里都需要获取开始时间start、结束时间end、计算耗费时间cost_time、加上一个输出语句。
使用装饰器的方法是这样的

简单装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def run_time(func):
def wrapper():
start = time()
func() # **函数在这里运行**
end = time()
cost_time = end - start
print("func three run time {}".format(cost_time))
return wrapper

@run_time
def fun_one():
sleep(1)

@run_time
def fun_two():
sleep(1)

3.anconda升级python版本

  • 先查看本地镜像源,清华镜像源从2019年已经停用了,建议使用中科大的镜像源
  • 然后直接命令指定升级python版本即可,如果不升级镜像源的话可能报404或者下载速度慢

3.1 修改镜像源

先查看已经安装过的镜像源,cmd窗口执行命令:

1
conda config --show

查看配置项channels,如果显示带有tsinghua,则说明已安装过清华镜像。

1
2
3
4
5
6
channels:
- https://mirrors.tuna.tsinghua.edu.cn/tensorflow/linux/cpu/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/

下一步,使用conda config --remove channels url地址删除清华镜像,如下命令删除第一个。然后,依次删除所有镜像源

1
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/tensorflow/linux/cpu/

添加目前可用的中科大镜像源:

1
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/

并设置搜索时显示通道地址:

1
conda config --set show_channel_urls yes

确认是否安装镜像源成功,执行conda config --show,找到channels值为如下:

1
2
3
channels:
- https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
- defaults

3.2 升级python版本

打开Anaconda Prompt,
输入

1
conda create -n python36 python=3.6 anaconda

4.文件io

4.1 文件打开方式,r/w/a/+的说明

  • r只读,r+读写(覆盖写),不创建
  • w新建只写,w+新建读写,二者都会将文件内容清零
  • w+与r+区别:
  • r+:可读可写,若文件不存在,报错;w+: 可读可写,若文件不存在,创建

  • a:附加写方式打开,不可读;
  • a+: 附加读写方式打开

#5. 代码规范性测试

pylint代码规范测试

6. fileinput 懒惰行迭代

只读取实际需要的文件部分

7. 编码规范

  1. 多次使用的常量使用全大写命名,并考虑设置为全局变量
  2. 创建单独的配置文件模块,保存一些配置的参数

    简单配置或者使用configparser模块

  3. 在需要的时候使用日志记录(logging模块)

8.魔法方法理解

魔法方法是python内置方法,不需要主动调用,存在的目的是为了给python的解释器进行调用,几乎每个魔法方法都有一个对应的内置函数,或者运算符,当我们对这个对象使用这些函数或者运算符时就会调用类中的对应魔法方法,可以理解为重写内置函数

实际上, 当我们调用x = SomeClass()的时候调用,__init__并不是第一个执行的, __new__才是。所以准确来说,是__new__和__init__共同构成了”构造函数”.

new() 是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器修饰,且该方法会优先 init() 初始化方法被调用

9.正则表达式

##9.1 match()和search()比较

1
2
3
4
5
6
7
m = re.match('foo', 'seafood') 
m.group()
>>> None # 匹配失败

m = re.search('foo', 'seafood')
m.group()
>>> 'foo' # 搜索成功, 但是之前匹配失败

match是从字符串的起始部分开始匹配模式,把字符串当作一个整体来匹配,而search表示字符串中任意位置出现符合匹配模式的字符串,都将其提取出来。
##9.2 中括号([cr][23][dp][o2])和或运算符(r2d2|c3po)的区别

  • [cr][23][dp][o2]

    上述表示匹配由四个字符组成的类似‘ABCD’字符串,每一个字符匹配一个中括号内的两个字符中的一个

  • r2d2|c3po

    上述表示匹配“r2d2”或“c3po”

##9.3 使用group()或groups()访问匹配组

pattern中每一个括号为一个匹配模式,返回的结果保存在group()中,而groups()可以查看所有的子组匹配结果(如果pattern中无括号分组模式则groups为空)。

m = re.match(‘(\w\w\w)-(\d\d\d)’, ‘abc-123’)
m.group() # 完整匹配
‘abc-123’
m.group(1) # 子组 1
‘abc’
m.group(2) # 子组 2
‘123’
m.groups() # 全部子组
(‘abc’, ‘123’)

9.4 findall和search方法的区别

findall()查询字符串中某个正则表达式模式全部的非重复出现情况,和search类似,但是findall总是返回一个列表,当匹配失败时列表为空;当匹配成功时返回所有成功的匹配部分

9.5 sub()和subn(),表示替换

sub和subn基本一样,但是subn还返回了替换的次数。

10 python多线程与多进程

参考python进程池:multiprocessing.pool - jihite - 博客园 (cnblogs.com)

10.1 总结

i/o密集型任务用多线程,多任务计算使用多进程

10.2 使用进程池

10.2.1 样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding: utf-8
import multiprocessing
import time

def func(msg):
print "msg:", msg
time.sleep(3)
print "end"

if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in xrange(4):
msg = "hello %d" %(i)
pool.apply_async(func, (msg, )) #维持**同时**执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去(多通道)
#pool.apply(func, (msg, )) # 而使用apply方法的时候是通道阻塞的,一次只有一个进程在运行,当一个进程执行完毕后再添加新的进程(单通道)
print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"
pool.close()
pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print "Sub-process(es) done."

10.2.2 结果:

apply_async方法,多通道

1
2
3
4
5
6
7
8
9
10
mMsg: hark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~ello 0

msg: hello 1
msg: hello 2
end
msg: hello 3
end
end
end
Sub-process(es) done.

apply方法,单通道

1
2
3
4
5
6
7
8
9
10
msg: hello 0
end
msg: hello 1
end
msg: hello 2
end
msg: hello 3
end
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
Sub-process(es) done. 

11 python 集合set中 add与update的区别

集合set是一个无序不重复元素的集

1
2
3
4
set(['hello','hello','hi'])
# {'hello', 'hi'}
set('hello hello hi')
# {' ', 'e', 'h', 'i', 'l', 'o'}

set.add()set.update()的区别

1
2
3
4
5
6
7
8
9
10
myset1 = set()
myset1.add('hello')
#{'hello'}
myset1.update('world')
#{'d', 'hello', 'l', 'o', 'r', 'w'}

myset2 = set()
myset2.add('123')
myset2.update('123')
#{'1', '123', '2', '3'}

12. python离线环境迁移

12.1 依赖文件requirement.txt生成

1
2
3
4
# 在当前目录生成该目录下项目所需的所有依赖文件:注意生成包之后需要手动核对一下版本最好,会有部分版本生成错误的问题
pipreqs ./ --encoding=utf8
# 与pip freeze的区别
# freeze 是生成当前python环境的所有包

12.2 然后根据requirement.txt批量下载包文件

1
2
3
4
pip wheel --wheel-dir=.pip wheel --wheel-dir=./packages -r requirements.txt
# 最后一起迁移到离线环境中并进行安装
#在新服务器的site-packages目录下执行:
pip install --no-index --find-links=/xxx/xxx/packages -r /xxx/xxx/packages/requirements.txt

12.3 出现问题

使用pipreqs生成的requirement.txt无法完整安装所有的包

使用python -m pip freeze > req.txt生成依赖文件然后替换用该文件安装

13. collection方法详解

13.1 命名元组 namedtuple

使用namedtuple可以对元组中的每一个值进行命名

1
2
3
4
5
6
7
# 使用命名元组解析csv
from collections import *
import csv

EmployeeRecord = namedtuple('EmployeeRecord','name, age, title, department, paygrade')
for emp in map(EmployeeRecord._make,csv.reader(open("employee.csv","rb"))):
print (emp.name,emp.title)

13.2 defaultdict

功能和dict差不多,区别是会对未存在的key值赋予一个默认值而不报异常

并且 defaultdict()的default_factory参数可以传入list,int,tuple等类型

1
2
3
4
5
6
7
>>> from collections import *
>>> >>> s = [('yellow',1),('blue',2),('yellow',3),('blue',4),('red',5)]
>>> d = defaultdict(list)
>>> for k,v in s:
... d[k].append(v)
>>> d.items()
[('blue', [2, 4]), ('red', [5]), ('yellow', [1, 3])]

13.3 Counter

counter可以支持方便、快速的计数

1
2
3
4
5
>>> from collections import *

>>> cnt = Counter('aabbcc')
>>> cnt
Out[14]: Counter({'a': 2, 'b': 2, 'c': 2})

13.4 deque

deque是栈和队列的一种广义实现,deque是”double-end queue”的简称;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。

1
2
3
4
5
6
7
8
>>> from collections import deque
>>> dq = deque(range(10), maxlen=10)
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
# rotate 将循环队列向右移三次
>>> dq.rotate(3)
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)

————其他—————————

  • mysql/oracle

内连接是保证两个表中所有的行都要满足连接条件,而外连接则不然。在外连接中,某些不满条件的列也会显示出来,也就是说,只限制其中一个表的行,而不限制另一个表的行。分左连接、右连接、全连接三种。

  • Django/Flask
  • git

14 多线程,多进程,协程

程序是由多个进程组成的,然后每个进程可能有多个线程(线程更加底层)

而python由于GIL(全局解释器锁)的存在,使得多线程无法充分使用多核的优势

如果使用爬虫这种i/o密集型程序,多线程操作还是很明显的。

15 pandas操作

16 scrapy相关

17 restful api

遵循 REST 架构规范的应用编程接口

api:

客户端和服务器端信息的传递者

传输格式:

JSON(Javascript 对象表示法)、HTML、XLT、Python、PHP 或纯文本