前言
本博客僅僅記錄我學習使用Python期間遇到的一些問題,一些解決方案之類的,學習部分參考的廖雪峰的Python教程,學完之后做一個記錄備忘
垃圾pip的下載速度
眾多周知的原因,pip下載包的時候超級慢,慢到令人發指,所以使用國內的鏡像會快很多,例如我要下載pillow,可以使用
pip install -i https://pypi.doubanio.com/simple/ pillow
在pip install -i https://pypi.doubanio.com/simple/ 后面加上要下載的包名就可以了,不要使用pip install pillow,慢的很
查看Python以及第三方包安裝位置
查看Python的位置很簡單,直接輸入
where python
查看你現在安裝的所有的包
pip list
一些第三方Python包一般都是通過pip下載的,查看這些包的位置以及其他信息可以使用
# 例子 pip show 包名
pip show googletrans
Python打包成exe
安裝pyinstaller打包工具
pip install pyinstaller
打包
需要進入到你的Python文件所在的地方,執行,順便再提一下,這個圖標啊,好像必須是ico格式的,我用gif不行,再有,打包之后的exe圖標還是沒變化,你把exe移出來換個地方圖標就正常顯示了
#如果有圖標的話可以執行
pyinstaller -F -i favicon.ico nhdz.py
#如果沒有圖標,可以不寫
pyinstaller -F nhdz.py
打包報錯
錯誤 :AttributeError: module 'win32ctypes.pywin32.win32api' has no attribute 'error'
原因是因為我的圖片不是 .ico格式的,只要不是.ico格式的圖片好像不能作為圖標,然后我把jpg改成ico,事實證明只改格式是不行的,所以,如果沒有ico的圖,就不要圖標了吧
selenium打包之后給別人使用需要安裝驅動
事情是這樣的,有一個網頁需要每天自動簽到,然后我就寫了個自動簽到的工具,就是Python調用Selenium,打包成exe給也需要簽到的我的朋友
但是他一打開就閃退,我給他打了個log發現別人電腦上沒有python環境,想要運行selenium驅動chrome,必須安裝ChromeDriver
ChromeDriver的exe版下載地址,下載完之后把這個exe放到一個文件夾,可以是Chrome的安裝文件夾,也可以是任意一個文件夾,不過我喜歡和Chrome放在一起,好找,然后這個exe是不能雙擊安裝的,你需要做的就是打開環境變量,把這個ChromeDriver所在的目錄加到Path環境變量里面,然后再次運行我寫的簽到exe就ok了
如果還是出現了Chrome閃退,那就是Chrome版本和ChromeDriver版本不一致,好好對一下
Python操作json
儲存json
想把數據變成json還是很簡單的,首先鍵值對,我使用的是dict字典,然后json化
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字符串了,可以直接存儲到數據庫里面或者其他里面
讀取json
從數據庫讀取json字符串
例如,我的json是這樣的
{"ERRORCODE":"0","RESULT":[{"port":"45021","ip":"183.15.122.171"}]}
我可以這樣讀取
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是很方便的,所以我按照上面讀取數據庫json字符串的方式讀取本地json文件,報錯了,正確做法如下
with open(r"config.json",'r',encoding='utf-8') as f:
config = json.load(f)
print(config)
print(config['ConnectionString'])
print(config['ProductCategoryID'])
注意,使用的是load方法,不是loads,如果這里使用loads方法,會報錯
中文問題
如果有中文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()
Python遞歸之return None
代碼如下
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))
多個not in的寫法
我有多個條件需要判斷,每一個都要滿足不存在,我可以這樣寫
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')
清除字符串前后的空格
僅僅清除前后的空格,字符串內部的空格不會被清除
asd = ' asd asd '
asd = asd.strip()
asd的結果是asd asd
截取字符串
我想截取字符串如下
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函數,計算數量的也可以使用len()函數,比如我查詢了所有的a標簽,我想知道a標簽到底有多少個,我可以使用
pagelist = pages.find_elements_by_css_selector('a')
print(len(pagelist))
Python操作MongoDB
配置連接MongoDB
先安裝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什么也查不出來
VS Code發布Python為exe
我之前爬蟲都是直接F5運行,掛着VS Code爬取的,但是目前遇到了一個數據量很大而且很難爬的網站,決定多開幾個爬蟲的,同時爬取
先安裝Python發布的庫
pip install pyinstaller
打包exe的命令需要進入到你的python文件的目錄,然后執行
pyinstaller -F data1.py
稍等片刻,就會生成exe
Python操作SqlServer
推薦查看官網的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
pyodbc操作SQLserver,獲取剛插入數據的Id
這個是真的麻煩,我在數據庫里面嘗試這個語句是完全ok的
select ident_current('表名')
但是在python里面寫了獲取的時候根本獲取的不是這樣的,獲取的是包含Decimal的一個什么類型
沒辦法,我無法解決,百度,Google了半天,官網也看了,還是沒解決,我真是🐷
沒辦法,我最后只好使用一個字符串截取的方法了
cursor.execute("""
select CAST((select ident_current('VaeDB.dbo.Netcomponents_SupplierInformation')) as Integer)
""")
SupplierInformationId = str(cursor.fetchone())
SupplierInformationId=int(SupplierInformationId[SupplierInformationId.index('(')+1:SupplierInformationId.index(',')])
Python操作Mysql
操作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操作
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()
插入之后立即獲取插入后的ID
注意:commit執行是conn的事,我遇到了插入之后就需要Id的問題,很簡單,執行一個cursor.lastrowid就可以
if __name__ == "__main__":
cursor.execute("""
INSERT product (`Name`) VALUES('yh')
""")
conn.commit()
id=cursor.lastrowid
print('Id是:' + str(id))
基礎
List:數組
#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:不可變的數組
Tuple和List差不多,但是是(),而且Tuple不可變,但是Tuple內部的List可以改,類似下面的tuple里面的第三個元素是List就可以變,前兩個tuple元素不可變
tuple=(1,True,['許嵩','蜀雲泉'])
range():生成整數
range(5)就是[0, 1, 2, 3, 4]
range(100)就是[0, 1, 2, 3, 4 ... 99]
循環
可以使用for循環也可以使用while循環
- break:跳出循環,結束
- continue:跳過本次循環,進入下一次循環
dict:key-value形式的字典
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
#讀取
d['Michael']
#賦值
d['Adam'] = 67
set:不會重復的集合
s = set([1, 2, 3])
s.add(4)
s.remove(4)
常用函數
abs():絕對值
abs(-100)
max():返回最大值
#結果返回3
max(2, 3, 1, -5)
隨機數
num = random.randint(2,5) 隨機2~5,包括2和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:]
列表生成式:for循環的快速計算
#我想生成[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)
map():將函數作用於Iterable的每個元素
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']
reducr():必須接受兩個參數的函數作用於序列的下一個元素
#把序列[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
filter():和map類似,函數返回的是bool類型,主要功能過濾篩選
#函數返回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']
sort():排序
sorted([36, 5, -12, 9, -21])
#結果
[-21, -12, 5, 9, 36]
#傳入key參數,忽略大小寫的排序
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
面向對象編程
Python里的類
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()
獲取對象信息:type()獲取類型,dir()獲取屬性方法
可以看對象的類型和方法,感覺有點反射的意思啊
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__
后面不可以寫方法了,我一個類只寫一個限制屬性的玩意,其他內容都寫不了???
@property:get,set方法變成屬性調用
我們在寫類的時候,屬性一般都寫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'))
IO
讀取文件
原始寫法,手動close
文件的讀取操作之后,需要關閉文件,不然就會占用系統資源,所以原始寫法是這樣的
try:
f = open('/path/to/file', 'r',,encoding='utf-8')
print(f.read())
finally:
if f:
f.close()
很麻煩,所以Python提供了一個方便的寫法
方便寫法,open方法
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讀取文件的方式有三種,看情況使用
- read():一次性讀取文件全部內容,如果文件很大,比如10G,內存可能會崩,所以read方法適合小文件
- read(size)方法,每次讀取固定大小,這種方式最普遍適用
- readline(),每次讀取一行
- readlines(),一次性讀取全部內容,以list的形式返回.如果文件過大會爆內存
代碼如下
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: #相對路徑 path=r"userinfo.txt"
f.write('Hello 許嵩 最佳歌手')
如果文件已存在,會直接覆蓋.如果我們希望追加到文件末尾可以傳入'a'
import sys
path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt'
with open(path,'a',encoding='utf-8') as f: #相對路徑 path=r"userinfo.txt"
f.write('蜀雲泉很帥,沒意見吧' + '\n') #可以加一個 \n來換行
StringIO:內存中讀寫str文件
寫入內存
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()
BytesIO:內存中讀寫二進制文件
寫二進制
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的序列化的意思居然是內存的變量保存到本地磁盤的意思....
在其他語言里面這就是持久化.........
進程和線程
進程
multiprocessing
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')
結果是父進程和子進程交錯輸出的
Pool進程池,多個進程的啟動
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
Lock:鎖
多進程之間變量是每個進程都有一份,而多線程是共享變量的,所以會出現多個線程修改同一個變量從而導致變量數據出錯的情況,如下
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全局鎖
因為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:線程的局部變量
多個線程之間,使用全局變量還得加鎖,使用局部變量,線程的每個方法調用的時候又麻煩,所以可以使用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
結果展示
內建模塊
datetime
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)
Python訪問返回json數據的接口
很簡單,直接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')