26、单例模型
单例设计模型旨在减少多余类的创建,节约内存。
单例模型的实现方式通常包括:__new__()方式实现单例、装饰器方式实现单例、import方式等等。以前我写过相关的博客,这里不细讲了,直接贴代码…
# __new__()方式:主要思想是在创建类对象的时候判断一下这个类对象是不是已有实例对象了,如果有就返回那个实例对象,否则创建一个
class A():
# instance = {}
# def __new__(cls):
# if cls not in cls.instance:
# cls.instance[cls] = super().__new__(cls)
# return cls.instance[cls]
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "instance"):
cls.instance = super().__new__(cls, *args, **kwargs)
return cls.instance
a = A()
b = A()
a is b
#装饰器方式:
def set_func(cls):
instance = {}
def call_func():
if cls not in instance:
instance[cls] = cls()
return instance[cls]
return call_func
@set_func # <==> B = set_func(B)
class B():
pass
b1 = B()
b2 = B()
b1 is b2
True
class Single():
def __init__(self, class_):
self.instance = {}
self.class_ = class_
def __call__(self):
if self.class_ not in self.instance:
self.instance[self.class_] = self.class_()
return self.instance[self.class_]
@Single
class C():
pass
c1 = C()
c2 = C()
c1 is c2
True
27、python中的GIL
python并不支持正真意义上的多线程,虽然它提供了多线程的包,但是如果想要通过多线程来提高代码的速度,这并不是一个好主意。因为历史原因,python中存在一个线程全局锁(GIL)的东西,每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU。
如何解决GIL锁问题?
- 更换cpython解释器为jpython(Java写的解释器,这里面没有GIL,但是不建议)
- 使用多进程完成多线程任务
- 多线程任务可以使用c语言去实现
什么时候会释放GIL锁?
- 像遇到I/O操作,会有空闲的情况,造成CPU空闲的情况,会释放GIL
- 会有一个专门的ticks进行计数,一旦ticks数值达到100,这时候就会释放GIL锁,线程之间开始GIL锁(ticks这个值可以进行设置以延长或缩短GIL锁的线程使用CPU的时间)
互斥锁与GIL锁的关系?
- GIL锁:保证同一时刻只有一个线程使用CPU
- 互斥锁:多线程时,保证修改共享数据时有序修改,不会产生数据修改混乱
假设只有一个进程,这个进程中有两个线程,thread1和thread2,要修改共享数据data,并且有互斥锁,执行过程是什么?
- 1、多线程运行,假设thread1拿到了GIL可以使用CPU,这是thread1获得了互斥锁lock,thread1可以修改共享数据data(假设并没有修改data);
- 2、thread1在修改data数据之前发生了I/O操作或者ticks计数满100,这时候,thread1让出GIL,GIL可以竞争;
- 3、thread1和thread2开始竞争GIL(注意:如果thread1是因为I/O阻塞让出的GIL,thread2肯定会拿到GIL,如果是ticks计数满100让出的GIL,那么它们会公平竞争);
- 4、假设thread2拿到了GIL,运行代码去修改data,但是thread1有互斥锁lock,所以thread2无法更改共享数据,这是thread2让出GIL,GIL锁再次发生竞争;
- 5、假设thread1抢到了GIL,由于有互斥锁,所以可以修改共享数据data,当thread1修改完数据释放互斥锁lock,下一次只有thread2在获取GIL和lock后才可对data修改;
python是一种胶水语言,可以调用c/c++、Java、js等语言。python使用c语言编写多线程调用的函数:
- 1、gcc **.c -shared -o lib**.so 编译成.so文件
- 2、在Python中:
- from ctypes import *
- lib = cdll.LoadLibrary("./xx/lib**.so")
- lib.** # 调用函数
28、检查一个函数的执行效率的高低
import cProfile
from random import random
def f1(li):
l1 = sorted(li)
l2 = [i for i in l1 if i < 0.5]
return [i**2 for i in l2]
def f2(li):
l1 = [i for i in li if i < 0.5]
l2 = sorted(l1)
return [i**2 for i in l2]
list_ = [random() for _ in range(100000)]
cProfile.run("f1(list_)")
cProfile.run("f2(list_)")
7 function calls in 0.055 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.054 0.054 <ipython-input-4-6e570260133b>:4(f1)
1 0.003 0.003 0.003 0.003 <ipython-input-4-6e570260133b>:6(<listcomp>)
1 0.013 0.013 0.013 0.013 <ipython-input-4-6e570260133b>:7(<listcomp>)
1 0.001 0.001 0.055 0.055 <string>:1(<module>)
1 0.000 0.000 0.055 0.055 {built-in method builtins.exec}
1 0.037 0.037 0.037 0.037 {built-in method builtins.sorted}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
7 function calls in 0.030 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.003 0.003 0.003 0.003 <ipython-input-4-6e570260133b>:10(<listcomp>)
1 0.012 0.012 0.012 0.012 <ipython-input-4-6e570260133b>:12(<listcomp>)
1 0.000 0.000 0.029 0.029 <ipython-input-4-6e570260133b>:9(f2)
1 0.001 0.001 0.030 0.030 <string>:1(<module>)
1 0.000 0.000 0.030 0.030 {built-in method builtins.exec}
1 0.014 0.014 0.014 0.014 {built-in method builtins.sorted}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
29、正则表达式中的search()与match()区别?
match()函数是从string的开始位置匹配,search()会扫描真个string查找匹配。也就是说,match()只有在0位置匹配成功才会返回,否则返回None。
import re
string = "123阅读次数为99999"
string2 = "阅读次数为99999"
ret = re.match(string=string, pattern=r"\d+")
ret1 = re.match(string=string2, pattern=r"\d+")
print(ret) # 从头匹配,从首字母开始,匹配成功后match对象,失败返回None
print(ret.group())
print(ret1)
ret2 = re.search(string=string, pattern=r"\d+")
ret2_1 = re.search(string=string2, pattern=r"\d+")
print(ret2) # 扫描string,如果存在与正则表达式匹配的,则返回match对象,如果存在多个,返回第一个
print(ret2.group())
print(ret2_1.group())
ret3 = re.findall(string=string, pattern=r"\d+")
ret3_1 = re.findall(string=string2, pattern=r"\d+")
print(ret3) # 匹配所有,返回列表
print(ret3_1)
# 获取match对象的值,使用.group()获取
<_sre.SRE_Match object; span=(0, 3), match='123'>
123
None
<_sre.SRE_Match object; span=(0, 3), match='123'>
123
99999
['123', '99999']
['99999']
30、正则表达式的贪婪与非贪婪匹配
python中默认是贪婪的,总是尝试匹配尽可能多的字符,非贪婪则相反。
在“*”,“?”,“+”, “{m,n}”后面加上?,是贪婪变成非贪婪
import re
string = "<a>xyz</a>"
ret1 = re.match(pattern=r"<.*>", string=string)
print("贪婪匹配结果:", ret1.group())
ret2 = re.match(pattern=r"<.*?>", string=string)
print("非贪婪匹配结果:", ret2.group())
贪婪匹配结果: <a>xyz</a>
非贪婪匹配结果: <a>
31、日志模块logging
日志等级:
- 1、DEBUG:详细的信息,通常只出现在诊断问题上(调试时用)
- 2、INFO:确认一切按预期运行
- 3、WARNING:一个迹象表明,一些意想不到的事情发生,或表明一些问题在不久的将来(如:磁盘空间低),软件还能按预期完成
- 4、ERROR:更严重的问题,软件没能执行一些功能
- 5、CRITICAL:一个严重的错误,表明程序本身可能无法继续运行
上面的等级由:低1------>高5,默认是WARNING,当只有在WARNING等级之上才被跟踪。
日志文件输出:两种方式,一是控制台输出,二是记录到文件中。
# 新建一个test.py,执行下面代码
import logging
logging.basicConfig(level=logging.WARNING, format="%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s")
logging.info("这是logging info message")
logging.debug("这是logging debug message")
logging.warning("这是logging warning message")
logging.error("这是logging error message")
logging.critical("这是logging critical message")
2019-05-23 19:43:26,420-<ipython-input-7-ca5658839a4b>[line:8]-WARNING:这是logging warning message
2019-05-23 19:43:26,421-<ipython-input-7-ca5658839a4b>[line:9]-ERROR:这是logging error message
2019-05-23 19:43:26,423-<ipython-input-7-ca5658839a4b>[line:10]-CRITICAL:这是logging critical message
将日志输出到文件
import logging
logging.basicConfig(level=logging.WARNING,
filename="./log.txt",
filemode="wb",
format="%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s")
logging.info("这是logging info message")
logging.debug("这是logging debug message")
logging.warning("这是logging warning message")
logging.error("这是logging error message")
logging.critical("这是logging critical message")
2019-05-23 19:43:26,432-<ipython-input-8-90fabcdb7af8>[line:10]-WARNING:这是logging warning message
2019-05-23 19:43:26,435-<ipython-input-8-90fabcdb7af8>[line:11]-ERROR:这是logging error message
2019-05-23 19:43:26,438-<ipython-input-8-90fabcdb7af8>[line:12]-CRITICAL:这是logging critical message
既要输出到控制台又要输出到文件中
这里需要一个叫做logger的对象
import logging
# 1、创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # Log等级总开关
# 2、创建一个handler,用于写入日志文件
logfile = './log.txt'
fh = logging.FileHandler(logfile, mode="a") # “a”追加的形式
fh.setLevel(logging.DEBUG) # 输出到file的log等级的开关
# 3、在创建一个handler,用于输出控制台
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING) # 输出到console的log等级的开关
# 4、定义handler输出格式
formatter = logging.Formatter("%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 5、将logger添加到handler里面
logger.addHandler(fh)
logger.addHandler(ch)
logging.info("这是logging info message")
logging.debug("这是logging debug message")
logging.warning("这是logging warning message")
logging.error("这是logging error message")
logging.critical("这是logging critical message")