目录css
本博客仅仅记录我学习使用Python期间遇到的一些问题,一些解决方案之类的,学习部分参考的廖雪峰的Python教程,学完以后作一个记录备忘node
查看Python的位置很简单,直接输入python
where python
一些第三方Python包通常都是经过pip下载的,查看这些包的位置以及其余信息可使用mysql
# 例子 pip show 包名 pip show googletrans
pip install pyinstaller
须要进入到你的Python文件所在的地方,执行linux
#若是有图标的话能够执行 pyinstaller -F -i favicon.ico nhdz.py #若是没有图标,能够不写 pyinstaller -F nhdz.py
想把数据变成json仍是很简单的,首先键值对,我使用的是dict字典,而后json化git
Specifications = {} for tr in table.find_elements_by_css_selector('tr'): if tr.get_attribute('class'): Specifications[tr.find_element_by_css_selector( '.spec-name').text] = tr.find_element_by_css_selector('.spec-value').text Specifications = json.dumps(Specifications)
Dict使用json.dumps就变成json字符串了,能够直接存储到数据库里面或者其余里面github
例如,个人json是这样的sql
{"ERRORCODE":"0","RESULT":[{"port":"45021","ip":"183.15.122.171"}]}
我能够这样读取mongodb
jsonobj = json.loads(str(r.text)) print(jsonobj) print(jsonobj['RESULT']) print(jsonobj['RESULT'][0]['ip']) print(jsonobj['RESULT'][0]['port'])
若是多层就使用['XX']['XX']
若是json里面带有[] 那就使用[0]数据库
若是有中文json化的话,肯能会出现下图的问题
能够json化的时候这样写
ParameterDetails=json.dumps(ParameterDetails,ensure_ascii=False, indent=4)
这样有中文的json化以后也能够看中文
我写爬虫时,遇到网站没法访问的问题,就卡在那,动也不动,写try catch退出也不行,卡那了,没进try catch,因此我写了一个计时功能,10分钟后退出exe,Windows的计划任务定时启动exe
# 10秒钟退出exe import sys import time time_begin=int(time.time()) if(int(time.time()) - time_begin) >=10: #这里的单位是秒 sys.exit()
代码以下
def judge_description( next): if next > 0 : if next > 3 : return next else: next = next + 1 judge_description(next) else: return 0 print(judge_description(1))
返回结果应该是4才对,可是返回的倒是None,我竟然没有发现else那里没有写return,正确的写法应该是
def judge_description( next): if next > 0 : if next > 3 : return next else: next = next + 1 return judge_description(next) else: return 0 print(judge_description(1))
我有多个条件须要判断,每个都要知足不存在,我能够这样写
if a not in text and b not in text and....
这样写很差,五六个就已经很头疼了,可使用all函数,以下
s=['6','8'] text='12345' if all(t not in text for t in s): print('ok')
我想截取字符串以下
http://netdna-cdn.com/wp-content/uploads/2015/12/C4468-image.gif
我只想要uploads/2015/12/C4468-image.gif 这个部分,我本来想的是使用正则,但是大佬告诉我有更快的方法,就是使用截取
text = "http://netdna-cdn.com/wp-content/uploads/2015/12/C4468-image.gif" print(text.index('uploads')) sss=text[text.index('uploads'):] aaa=sss.split('/') print(aaa[0]+"--"+aaa[1]+aaa[2]+"---"+aaa[3])
讲解一下,首先text.index('uploads')是获得uploads的位置,而后根据这个位置我能够截取,经过[:]方式
text[text.index('uploads'):]就是从uploads开始截取到最后
而后就是split分割了,这个很简单
只要是计算长度的,均可以使用len函数,计算数量的也可使用len()函数,好比我查询了全部的a标签,我想知道a标签到底有多少个,我可使用
pagelist = pages.find_elements_by_css_selector('a') print(len(pagelist))
先安装PyMongo,而后代码很简单,只须要写几行代码就能够了
client = pymongo.MongoClient("mongodb://admin:test123@192.168.1.1:27017/") db = client.database collection = db.test
链接字符串里面是个人MongoDB的帐号和密码,后面才是MongoDB的ip地址
先定义一个集合,而后insert
message = { 'PartNumber': a.text, 'Address': a.get_attribute('href'), 'url': url } collection.insert_one(message)
这里我分为两种状况,一种是查询一堆,也就是模糊查询
def get_address(): arr=[] datalist = collectionAddress.find({'url': re.compile('char=0&popular=1')}) for data in datalist: arr.append(data['Address']) return arr
还有一个状况,就是我插入数据的时候,我想检测数据是否已经存在MongoDB数据库了,若是存在就不插入
arrs = get_address() for url in arrs: isexit = collectionData.find_one({'Address': url}) if isexit: print('有数据') else: save_data(url)
这里要使用find_one,我使用find什么也查不出来
我以前爬虫都是直接F5运行,挂着VS Code爬取的,可是目前遇到了一个数据量很大并且很难爬的网站,决定多开几个爬虫的,同时爬取
先安装Python发布的库
pip install pyinstaller
打包exe的命令须要进入到你的python文件的目录,而后执行
pyinstaller -F data1.py
稍等片刻,就会生成exe
推荐查看官网的wiki文档:https://github.com/mkleehammer/pyodbc/wiki
导入库import pyodbc
这个库厉害了,不只仅是SQLServer,Mysql,Oracle都是能够的,并且很好用
import pymongo import re import pyodbc def insertsql(name,sex,age):#插入SQL数据库 conn = pyodbc.connect('DRIVER={SQL Server};SERVER=127.0.0.1,1433;DATABASE=test;UID=sa;PWD=test123') try: cursor = conn.cursor() # cursor.execute('insert into Test(Name,Sex,Age) values(?,?,?)',(name,sex,age)) 插入 # cursor.execute('delete from Test where Name=?',(name)) 删除 cursor.execute('select * from Test where Name = ?',(name)) #查询 cursor.execute('update Test set Name=? where Name=?',('蜀云泉','许嵩')) #更新 # data=cursor.fetchone() 查询一个 data=cursor.fetchall() print(data) # conn.commit()# 插入和删除,更新数据的时候执行,查询不须要执行 print('成功') except Exception as e: print(str(e)) finally: conn.close() insertsql('许嵩','男',35)
pymongo用起来很是的舒服,就是下面的写法
cursor.execute(""" select * from Test where Name like '%许嵩%' """ )
若是想加参数传入能够这样
cursor.execute(""" select * from Test where Name like ? """,'%许嵩%' )
你可能发现了,查询的结果是下面这样的
[(2, '许嵩', '男 ', 32), (3, '许嵩', '女 ', 33), (4, '许嵩', '男 ', 30), (6, '许嵩 ', '男 ', 35)]
有中括号,还有括号,其实能够这样获得想要的数据
cursor.execute(""" select Name,Sex,Age from Test where Name like '%许嵩%' """ ) datalist=cursor.fetchall() for data in datalist: print(data.Name) print(data.Sex) print(data.Age)
指名想要的列,而后就能够遍历输出了,结果以下
许嵩
男
32
许嵩
女
33
许嵩
男
30
许嵩
男
35
操做SQL Server的 pyodbc 就能够直接操做Mysql,可是我是头🐷
我看不懂官方文档是怎么操做的,网上的博客抄来抄去没有实质内容,因此换了一个包
先安装操做MySQL的包
pip install pymysql
而后链接字符串改变了,其余的都没变,因此只放出链接字符串
import pymysql conn = pymysql.connect( host='192.168.1.1', user='root', passwd='123456', port=3306, db='VaeDB', charset='utf8' ) cursor = conn.cursor()
pymysql操做mysql和pyodbc操做Sql Server一点都不同
# 查询 sql="select id from Memory where MemoryName='%s'" %MemoryName mysqlcursor.execute(sql) id=mysqlcursor.fetchone()[0] # 增长一条 cursor.execute(""" INSERT product (`Name`) VALUES('yh') """) conn.commit() id=cursor.lastrowid print('Id是:' + str(id)) # 增长多条 datalist=[] if Density != '': tuple=(id,'Density',Density) datalist.append(tuple) if Org != '': tuple=(id,'Org',Org) datalist.append(tuple) if Refresh != '': tuple=(id,'Refresh',Refresh) datalist.append(tuple) sqls="INSERT INTO `MemoryParameter`( `MemoryId`, `MemoryParameterName`, `MemoryParameterValue`) VALUES (%s,%s,%s);" mysqlcursor.executemany(sqls,datalist) mysqlconn.commit()
注意:commit执行是conn的事,我遇到了插入以后就须要Id的问题,很简单,执行一个cursor.lastrowid就能够
if __name__ == "__main__": cursor.execute(""" INSERT product (`Name`) VALUES('yh') """) conn.commit() id=cursor.lastrowid print('Id是:' + str(id))
#append直接在后面加上 list.append('林俊杰') #insert能够跟数字,制定插入的位置 list.insert(1,'张泉') #删除最后一个元素 list.pop() #删除指定的数据,删除第二个元素 list.pop(1) #改 list[1]='唐宋元明清' #能够print出来查看 print str(list).decode('string_escape') #能够查看list的长度 print len(list) #获取倒数的内容 print list[-1] #倒数第一 print list[-2] #倒数第二
Tuple和List差很少,可是是(),并且Tuple不可变,可是Tuple内部的List能够改,相似下面的tuple里面的第三个元素是List就能够变,前两个tuple元素不可变
tuple=(1,True,['许嵩','蜀云泉'])
range(5)就是[0, 1, 2, 3, 4] range(100)就是[0, 1, 2, 3, 4 ... 99]
可使用for循环也可使用while循环
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} #读取 d['Michael'] #赋值 d['Adam'] = 67
s = set([1, 2, 3]) s.add(4) s.remove(4)
abs(-100)
#结果返回3 max(2, 3, 1, -5)
int('123') float('12.34') str(100) bool(1)
def nop(): pass #什么也不作就写pass
返回多个值,本质就是返回一个tuple,tuple的()是能够省略的
import math def move(x, y, step, angle=0): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny # 调用 x, y = move(100, 100, 60, math.pi / 6)
普通的参数就不说了,例如
def enroll(name, gender, age=6, city='Beijing'): def add_end(L=[]):
可变参数能够像这样写,numbers前面加了一个*,那么接受的就是一个tuple了
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum
这个也很经常使用
def fact(n): if n==1: return 1 return n * fact(n - 1)
有一个数组以下
arr=['1','2','3','4','5']
我想取数组的前3个元素,可使用arr[0],arr[1],arr[2]来获取,那若是我想获取前20个元素我能够写一个for循环取到.可是,切片最简单
#索引从0开始,取到2,是不包括后边的索引3的 arr[0:3] #倒着取数据 arr[-2:]
#我想生成[1x1, 2x2, 3x3, ..., 10x10] L = [] for x in range(1, 11): L.append(x * x) #上面的循环能够实现,可是很差,使用下面的列表生成式 [x * x for x in range(1, 11)] [x * x for x in range(1, 11) if x % 2 == 0] [m + n for m in 'ABC' for n in 'XYZ']
生成器和列表生成式的区别就是生成器能够for循环
g = (x * x for x in range(10)) for n in g: print(n)
def add(x, y, f): return f(x) + f(y) #调用例子 add(-5, 6, abs)
def f(x): return x * x r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #r的结果 [1, 4, 9, 16, 25, 36, 49, 64, 81] #list所有元素转换为字符串 list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) #结果是 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
#把序列[1, 3, 5, 7, 9]变换成整数13579 from functools import reduce def fn(x, y): return x * 10 + y reduce(fn, [1, 3, 5, 7, 9]) 13579
#函数返回true保留,返回false丢弃 def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15] #把一个序列中的空字符串删掉,能够这么写: def not_empty(s): return s and s.strip() list(filter(not_empty, ['A', '', 'B', None, 'C', ' '])) # 结果: ['A', 'B', 'C']
sorted([36, 5, -12, 9, -21]) #结果 [-21, -12, 5, 9, 36] #传入key参数,忽略大小写的排序 sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) ['about', 'bob', 'Credit', 'Zoo']
init就至关于构造函数,Python类里面的全部的方法的第一个参数永远是self,表示建立的实例自己
讲一下%s这个,%s的意思是后面的变量格式化为一个string的字符串,而且有占位符的做用,因此我写了两个%s 后面%后接着的就是变量,多个变量能够括起来
class Student(object): def __init__(self,name,score): self.name=name self.score=score def print_score(self): print('%s: %s' % (self.name, self.score)) Vae=Student('许嵩',100) JJ=Student('林俊杰',100) Vae.print_score() JJ.print_score()
上面的类能够随意的给属性name和score赋值,若是想把属性变成私有的,可使用两个下划线,这样就变成私有的了,以下
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score))
这样一来,你就没法访问name和score属性了,没法读取和赋值了
可是仍是能够读取和赋值私有属性,以下
Vae._Student__name='蜀云泉' print(Vae._Student__name)
因此,Python的私有全靠自学,Python自己是没有限制的,靠你的自学去准守规则
上面的Python类里面,我写了一个参数是object,这个参数是继承使用的,如
class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): pass dog = Dog() dog.run()
能够看对象的类型和方法,感受有点反射的意思啊
print(type(1)) print(type("as")) print(type(Vae)) #结果以下 <class 'int'> <class 'str'> <class '__main__.Student'>
isinstance()方法能够判断类的依赖关系,前面是子类的实例对象,后面是基类名称
print(isinstance(dog, Animal))
接下来是获取全部属性和方法的dir方法
print(dir(Vae)) #结果 ['_Student__name', '_Student__score', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'print_score']
hasattr()能够判断属性存不存在,但貌似只能判断public的
print(hasattr(Vae,'name')) #能够传入一个default参数,若是属性不存在,就返回默认值: print(getattr(obj, 'z', 404))
这个有趣啊,实例找不到属性的时候会去类里面找,其余语言里面,类.属性都是得静态类才能够的,Python直接能够
class Student(object): name = 'Student' s = Student() # 建立实例s print(s.name) print(Student.name) # 打印类的name属性
__slots__
限制动态添加属性在Python中,使用Vae.age=18就能够为Vae对象添加一个age属性,若是想限制一个类的实例只能动态的添加指定的属性,可使用__slots__
class Student(object): __slots__ = ('name','age') Vae=Student() Vae.name='许嵩' Vae.age=33 print(Vae.name) print(Vae.age)
==我不明白,这个限制属性的__slots__
后面不能够写方法了,我一个类只写一个限制属性的玩意,其余内容都写不了???==
咱们在写类的时候,属性通常都写get和set方法,这样能够对数据作一个判断,例如
class Student(object): def get_score(self): return self._score def set_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 Vae=Student() Vae.set_score(100) print(Vae.get_score())
调用的时候都是方法set_score(),get_score()
而后能够变成属性.....真无聊啊这个东西.....
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 Vae=Student() Vae.score=100 print(Vae.score)
能够看到,方法名称都变成score了,调用的时候也是score,代码少了不少吗?
就是一个子类继承两个父类,多个父类,除了第一个后面的须要加MixIn
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
文件的读取操做以后,须要关闭文件,否则就会占用系统资源,因此原始写法是这样的
try: f = open('/path/to/file', 'r',,encoding='utf-8') print(f.read()) finally: if f: f.close()
很麻烦,因此Python提供了一个方便的写法
import sys path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt' with open(path,'r',encoding='utf-8') as f: print(f.read()) 相对路径 with open(r"userinfo.txt",'r',encoding='utf-8') as f: print(f.readline)
这个和原始写法的try catch是同样的,自动close了
Python读取文件的方式有三种,看状况使用
代码以下
import sys path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt' # read方法,一次性读取所有内容,若是文件过大会爆内存 with open(path,'r',encoding='utf-8') as f: print(f.read()) # read(size)方法,每次读取固定大小 with open(path,'r',encoding='utf-8') as f: while True: x = f.read(10) if not x: break print(x) # readline方法,每次读取一行 with open(path,'r',encoding='utf-8') as f: for line in f.readline(): print(line) # readlines方法,一次性读取所有内容,以list的形式返回.若是文件过大会爆内存 with open(path,'r',encoding='utf-8') as f: for line in f.readlines(): print(line)
若是文件很小,read()
一次性读取最方便;若是不能肯定文件大小,反复调用read(size)
比较保险;若是是配置文件,调用readlines()
最方便
前面的读取read方法都是文件格式的,也能够读取成二进制文件
import sys path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt' # read方法,一次性读取所有内容,若是文件过大会爆内存 with open(path,'rb') as f: print(f.read())
若是读取的文件有非编码字符的话,能够errors忽略
with open(path,'r', encoding='utf-8', errors='ignore') as f:
写文件和读文件是同样的,惟一区别是调用open()
函数时,传入标识符'w'
或者'wb'
表示写文本文件或写二进制文件:
import sys path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt' with open(path,'r',encoding='utf-8') as f: #我不知道为何不可使用相对路径,只能使用绝对路径也太垃圾了吧 f.write('Hello 许嵩 最佳歌手')
若是文件已存在,会直接覆盖.若是咱们但愿追加到文件末尾能够传入'a'
import sys path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt' with open(path,'a',encoding='utf-8') as f: #我不知道为何不可使用相对路径,只能使用绝对路径也太垃圾了吧 f.write('蜀云泉很帅,没意见吧')
write就是写入内存,读取可使用getvalue()
from io import StringIO f=StringIO() f.write('许嵩,最佳歌手') f.write('蜀云泉') print(f.getvalue())
除了上面的getvalue()还有其余的方法
from io import StringIO f=StringIO('我是已有的字符串\n我是第二行') while True: s=f.readline() if s == '': break print(s.strip())
因此读取内存文件的方式有两种,一种所有读取getvalue(),一种是一行一行的读取readline()
from io import BytesIO f=BytesIO() f.write('中文'.encode('utf-8')) print(f.getvalue())
f=BytesIO(b'\xe4\xb8\xad\xe6\x96\x87') print(f.read())
把两个路径合成一个时,不要直接拼字符串,而要经过os.path.join()
函数,这样能够正确处理不一样操做系统的路径分隔符
一样的道理,要拆分路径时,也不要直接去拆字符串,而要经过os.path.split()
函数,这样能够把一个路径拆分为两部分,后一部分老是最后级别的目录或文件名
# 查看当前路径 print(os.path.abspath('.')) # 在当前路径下直接新建文件夹 os.mkdir('Vae') # 在当前路径下直接删除文件夹 os.rmdir('Vae') # 指定的文件夹下面新建文件夹 os.mkdir('StudyCode/Vae') # 指定的文件夹下删除文件夹 os.rmdir('StudyCode/Vae') 直接获取到文件的后缀扩展名 print(os.path.splitext('/StudyCode/data/studyIO.txt')) # 文件重命名 os.rename('test.txt', 'test.py') # 删除文件 os.remove('test.py')
Python的序列化的意思竟然是内存的变量保存到本地磁盘的意思....
在其余语言里面这就是持久化.........
from multiprocessing import Process import os # 子进程要执行的代码 def run_proc(name): for i in range(0,200): print('我是子进程: '+str(i)+'\n') if __name__=='__main__': p = Process(target=run_proc, args=('test',)) p.start() # p.join() 若是想子线程执行完成再执行主线程,可使用join方法,一般用于进程的同步 for i in range(0,200): print('我是父进程: '+str(i)+'\n')
结果是父进程和子进程交错输出的
from multiprocessing import Pool import os, time, random # 子进程要执行的代码 def run_proc(name): for i in range(0,200): print(name +str(i)+'\n') if __name__ == '__main__': try: p = Pool(3) p.apply_async(run_proc, args=('1进程: ',)) p.apply_async(run_proc, args=('2进程: ',)) p.apply_async(run_proc, args=('3进程: ',)) p.close() # p.join() 进程的同步,执行完3个子进程才会执行主进程,join必须在close以后 for i in range(0,200): print('主进程' +str(i)+'\n') except Exception as identifier: pass
进程间通讯是经过Queue
、Pipes
等实现
from multiprocessing import Process, Queue import os, time, random # 写数据进程执行的代码: def write(q): print('Process to write: %s' % os.getpid()) for value in ['A', 'B', 'C']: print('Put %s to queue...' % value) q.put(value) time.sleep(random.random()) # 读数据进程执行的代码: def read(q): print('Process to read: %s' % os.getpid()) while True: value = q.get(True) print('Get %s from queue.' % value) if __name__=='__main__': # 父进程建立Queue,并传给各个子进程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 启动子进程pw,写入: pw.start() # 启动子进程pr,读取: pr.start() # 等待pw结束: pw.join() # pr进程里是死循环,没法等待其结束,只能强行终止: pr.terminate()
Python的标准库提供了两个模块:_thread
和threading
,_thread
是低级模块,threading
是高级模块,对_thread
进行了封装。绝大多数状况下,咱们只须要使用threading
这个高级模块。
import time, threading # 新线程执行的代码: def loop(): n = 0 print('线程开启: '+threading.current_thread().name) while n < 5: n = n + 1 print('线程计数: '+str(n)) time.sleep(1) print('线程结束: '+threading.current_thread().name) if __name__ == "__main__": print('当前线程开始:'+threading.current_thread().name) t = threading.Thread(target=loop, name='Vae') t.start() t.join() print('当前线程结束:'+threading.current_thread().name)
执行结果是
当前线程开始:MainThread 线程开启: Vae 线程计数: 1 线程计数: 2 线程计数: 3 线程计数: 4 线程计数: 5 线程结束: Vae 当前线程结束:MainThread
主方法里面默认就 有一个线程,名字默认就是MainThread
咱们使用threading.Thread新开了一个线程,执行loop方法,而且指定了线程名是Vae
多进程之间变量是每一个进程都有一份,而多线程是共享变量的,因此会出现多个线程修改同一个变量从而致使变量数据出错的状况,以下
import time, threading # 假定这是你的银行存款: balance = 0 def change_it(n): # 先存后取,结果应该为0: global balance balance = balance + n balance = balance - n def run_thread(n): for i in range(100000): change_it(n) t1 = threading.Thread(target=run_thread, args=(5,)) t2 = threading.Thread(target=run_thread, args=(8,)) t1.start() t2.start() t1.join() t2.join() print(balance)
上面的代码多运行几回,会发现,有时候结果是0有时候却不是0,因此当一个线程在修改一个变量的时候,其余线程不容许访问,这个时候就须要加锁
# 假定这是你的银行存款: balance = 0 lock=threading.Lock() def change_it(n): # 先存后取,结果应该为0: global balance balance = balance + n balance = balance - n def run_thread(n): for i in range(100000): #加锁 lock.acquire() try: change_it(n) finally: #改完释放锁 lock.release() t1 = threading.Thread(target=run_thread, args=(5,)) t2 = threading.Thread(target=run_thread, args=(8,)) t1.start() t2.start() t1.join() t2.join() print(balance)
使用lock = threading.Lock()获取了锁对象,在修改方法以前调用 lock.acquire()锁住了变量,执行完以后又释放了锁
由于Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先得到GIL锁,而后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把全部线程的执行代码都给上了锁,因此,多线程在Python中只能交替执行,即便100个线程跑在100核CPU上,也只能用到1个核。
GIL是Python解释器设计的历史遗留问题,一般咱们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。
因此,在Python中,可使用多线程,但不要期望能有效利用多核。若是必定要经过多线程利用多核,那只能经过C扩展来实现,不过这样就失去了Python简单易用的特色。
不过,也不用过于担忧,Python虽然不能利用多线程实现多核任务,但能够经过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
Python解释器因为设计时有GIL全局锁,致使了多线程没法利用多核。多线程的并发在Python中就是一个美丽的梦。
多个线程之间,使用全局变量还得加锁,使用局部变量,线程的每一个方法调用的时候又麻烦,因此可使用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
变量虽然是全局变量,但每一个线程都只能读写本身线程的独立副本,互不干扰。ThreadLocal
解决了参数在一个线程中各个函数之间互相传递的问题
ThreadLocal
最经常使用的地方就是为每一个线程绑定一个数据库链接,HTTP请求,用户身份信息等,这样一个线程的全部调用到的处理函数均可以很是方便地访问这些资源。
主进程,输出结果
#!/usr/bin/env python3 # -*- coding : utf-8 -*- # master.py for windows import time,queue from multiprocessing.managers import BaseManager from multiprocessing import freeze_support #任务个数 task_number = 10; #定义收发队列 task_queue = queue.Queue(task_number); result_queue = queue.Queue(task_number); def gettask(): return task_queue; def getresult(): return result_queue; def test(): #windows下绑定调用接口不能使用lambda,因此只能先定义函数再绑定 BaseManager.register('get_task',callable = gettask); BaseManager.register('get_result',callable = getresult); #绑定端口并设置验证码,windows下须要填写ip地址,linux下不填默认为本地 manager = BaseManager(address = ('127.0.0.1',5002),authkey = b'123'); #启动 manager.start(); try: #经过网络获取任务队列和结果队列 task = manager.get_task(); result = manager.get_result(); #添加任务 for i in range(task_number): print('Put task %d...' % i) task.put(i); #每秒检测一次是否全部任务都被执行完 while not result.full(): time.sleep(1); for i in range(result.qsize()): ans = result.get(); print('task %d is finish , runtime:%d s' % ans); except: print('Manager error'); finally: #必定要关闭,不然会爆管道未关闭的错误 manager.shutdown(); if __name__ == '__main__': # windows下多进程可能会炸,添加这句能够缓解 freeze_support() test();
新开进程,执行任务写入结果
#!/usr/bin/env python3 # -*- coding : utf-8 -*- # task.py for windows import time import sys import queue import random from multiprocessing.managers import BaseManager BaseManager.register('get_task') BaseManager.register('get_result') conn = BaseManager(address=('127.0.0.1', 5002), authkey=b'123') try: conn.connect() except: print('链接失败') sys.exit() task = conn.get_task() result = conn.get_result() while not task.empty(): n = task.get(timeout=1) print('run task %d' % n) sleeptime = random.randint(0, 3) time.sleep(sleeptime) rt = (n, sleeptime) result.put(rt) if __name__ == '__main__': pass
结果展现
import time from datetime import datetime,timedelta vae = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # 获取当前时间 print(vae) #结果 2019-08-20 23:19:48 print(datetime.now()) # 结果 2019-08-20 23:19:48.417210 # datetime转化为timestamp (就是时间戳) print(datetime.now().timestamp()) # 结果 1566314388.418206 # timestamp转化为datetime t=1429417200.0 print(datetime.fromtimestamp(t)) # 结果 2015-04-19 12:20:00 # str转化为datetime,这个经常使用,字符串格式的时间转为时间格式 cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S') print(cday) # 结果 2015-06-01 18:19:59 # datetime转化为str字符串 now = datetime.now() print(now.strftime('%a, %b %d %H:%M')) # 结果 Tue, Aug 20 23:27 # datetime加减,这个也经常使用,须要导入timedelta类,用法以下 datetime.now() + timedelta(hours=10) datetime.now() - timedelta(days=1) datetime.now() + timedelta(days=2,hours=10)
很简单,直接Request请求
import requests import json headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'} def get_country(url,id): try: headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'} response = requests.request("GET", url, headers=headers) jsonobj = json.loads(response.text) print(jsonobj['data'][0]['country']) except Exception: pass if __name__ == "__main__": get_country('https://www.v2ex.com/api/nodes/show.json')