Python我的筆記

目錄css

前言

本博客僅僅記錄我學習使用Python期間遇到的一些問題,一些解決方案之類的,學習部分參考的廖雪峯的Python教程,學完以後作一個記錄備忘node

查看Python以及第三方包安裝位置

查看Python的位置很簡單,直接輸入python

where python

一些第三方Python包通常都是經過pip下載的,查看這些包的位置以及其餘信息可使用mysql

# 例子 pip show 包名
pip show googletrans

Python打包成exe

安裝pyinstaller打包工具

pip install pyinstaller

打包

須要進入到你的Python文件所在的地方,執行linux

#若是有圖標的話能夠執行
pyinstaller -F -i favicon.ico nhdz.py
#若是沒有圖標,能夠不寫
pyinstaller -F nhdz.py

Python操做json

儲存json

想把數據變成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

例如,個人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()

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

截取字符串

我想截取字符串以下

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

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)

類型轉換

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: #我不知道爲何不可使用相對路徑,只能使用絕對路徑也太垃圾了吧
    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('蜀雲泉很帥,沒意見吧')

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

進程之間的通訊

進程間通訊是經過QueuePipes等實現

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的標準庫提供了兩個模塊:_threadthreading_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')
相關文章
相關標籤/搜索