目錄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')