Python 【基礎常識概念】

  • 深淺拷貝

淺copy與deepcopyhtml

    • 淺copy: 無論多麼複雜的數據結構,淺拷貝都只會copy一層
    • deepcopy : 深拷貝會徹底複製原變量相關的全部數據,在內存中生成一套徹底同樣的內容,咱們對這兩個變量中任意一個修改都不會影響其餘變量

深淺拷貝之間的區別?python

  • 對象的賦值就是簡單的引用,a = [1,2,3], b=a, 在上述狀況下,a和b是同樣的,他們指向同一片內存,b不過是a的別名,是引用,咱們可使用b is a 去判斷,返回True,表名他們地址相同內容也相同,也可使用id()函數來查看.看兩個列表地址是否相同.
  • 深拷貝就是將一個對象拷貝到另外一個對象中,這意味着若是你對一個對象的拷貝作出改變時,不會影響原對象。在Python中,咱們使用函數deepcopy()執行深拷貝
import copy
b=copy.deepcopy(a)

avatar

  • 而淺拷貝則是將一個對象的引用拷貝到另外一個對象上,因此若是咱們在拷貝中改動,會影響到原對象。咱們使用函數function()執行淺拷貝、切片操做是淺拷貝
b=copy.copy(a)

可變與不可變linux

  • 可變類型(mutable):列表,字典
  • 不可變類型(unmutable):數字,字符串,元組
  • 這裏的可變不可變,是指內存中的那塊內容(value)是否能夠被改變。若是是不可變類型,在對對象自己操做的時候,必須在內存中新申請一塊區域(由於老區域#不可變#)。若是是可變類型,對對象操做的時候,不須要再在其餘地方申請內存,只須要在此對象後面連續申請(+/-)便可,也就是它的address會保持不變,但區域會變長或者變短。
copy.copy()  # 淺拷貝
copy.deepcopy()  # 深拷貝
  • 淺拷貝是新建立了一個跟原對象同樣的類型,可是其內容是對原對象元素的引用。這個拷貝的對象自己是新的,但內容不是。拷貝序列類型對象(列表\元組)時,默認是淺拷貝。

 

垃圾回收機制

 

引計數git

  • 原理
    • 當一個對象的引用被建立或者複製時,對象的引用計數加1;當一個對象的引用被銷燬時,對象的引用計數減1.
    • 當對象的引用計數減小爲0時,就意味着對象已經再沒有被使用了,能夠將其內存釋放掉。
  • 優勢
    • 引用計數有一個很大的優勢,即實時性,任何內存,一旦沒有指向它的引用,就會被當即回收,而其餘的垃圾收集技術必須在某種特殊條件下才能進行無效內存的回收。
  • 缺點
    • 引用計數機制所帶來的維護引用計數的額外操做與Python運行中所進行的內存分配和釋放,引用賦值的次數是成正比的,
    • 顯然比其它那些垃圾收集技術所帶來的額外操做只是與待回收的內存數量有關的效率要低。
    • 同時,由於對象之間相互引用,每一個對象的引用都不會爲0,因此這些對象所佔用的內存始終都不會被釋放掉。

 

標記-清除正則表達式

 

  • 它分爲兩個階段:第一階段是標記階段,GC會把全部的活動對象打上標記,第二階段是把那些沒有標記的對象非活動對象進行回收。
  • 對象之間經過引用(指針)連在一塊兒,構成一個有向圖
  • 從根對象(root object)出發,沿着有向邊遍歷對象,可達的(reachable)對象標記爲活動對象,不可達的對象就是要被清除的非活動對象。
  • 根對象就是全局變量、調用棧、寄存器。

             

  • 在上圖中,能夠從程序變量直接訪問塊1,而且能夠間接訪問塊2和3,程序沒法訪問塊4和5
  • 第一步將標記塊1,並記住塊2和3以供稍後處理。
  • 第二步將標記塊2,第三步將標記塊3,但不記得塊2,由於它已被標記。
  • 掃描階段將忽略塊1,2和3,由於它們已被標記,但會回收塊4和5。

 

分代回收shell

 

  • 分代回收是創建在標記清除技術基礎之上的,是一種以空間換時間的操做方式。
  • Python將內存分爲了3「代」,分別爲年輕代(第0代)、中年代(第1代)、老年代(第2代)
  • 他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減少。
  • 新建立的對象都會分配在年輕代,年輕代鏈表的總數達到上限時,Python垃圾收集機制就會被觸發
  • 把那些能夠被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,依此類推
  • 老年代中的對象是存活時間最久的對象,甚至是存活於整個系統的生命週期內。

 

三次握手四次揮手

TCP協議express

  • 可靠傳輸, TCP數據包沒有長度限制, 理論上能夠無限長, 可是爲了保證網絡的效率,
  • 一般TCP數據包的長度不會超過IP數據包的長度, 以確保單個TCP數據包沒必要再分割

UDP協議windows

  • 不可靠傳輸, ""報頭""部分一共只有8個字節, 總長度不超過65535字節, 正好放進一個IP數據包

三次握手緩存

  • 置位概念: 根據TCP的包頭字段, 存在3個重要的表示ACK, SYN, FIN
  • ACK: 表示驗證字段
  • SYN: 位數置1, 表示創建TCP鏈接
  • FIN:位數置1,表示斷開TCP鏈接

 三次握手過程說明bash

  1. 由客戶端發送創建TCP鏈接的請求報文, 其中報文中包含seq序列號, 是由發送端隨機生成的,而且將報文中的SYN字段置爲1, 表示須要創建TCP鏈接 (SYN=1, seq=x, x爲隨機生成數值)
  2. 由服務端回覆客戶端發送的TCP鏈接請求報文, 其中包含seq序列號, 是由回覆端隨機生成的, 而且將SYN置爲1,並且會產生ACK字段, ACK字段數值是在客戶端發送過來的序列號seq的基礎上加1進行回覆,以便客戶端收到信息時,知曉本身的TCP創建請求已獲得驗證 (SYN=1, ACK=x+1, seq=y, y爲隨機生成數值) 這裏的ACK加1能夠理解爲是確認和誰創建鏈接
  3. 客戶端收到服務端發送的TCP創建驗證請求後, 會使本身的序列號加1表示, 而且再次回覆ACK驗證請求, 在服務端發過來的seq上加1進行回覆(SYN=1, ACK=y+1, seq=x+1)

四次揮手 

四次揮手過程說明

  1. 客戶端發送斷開TCP鏈接請求的報文, 其中報文中包含seq序列號, 是由發送端隨機生成的, 而且還將報文中的FIN字段置爲1, 表示須要斷開TCP鏈接 (FIN=1, seq=x, x由客戶端隨機生成)
  2. 服務端會回覆客戶端發送的TCP斷開請求報文, 其包含seq序列號, 是由回覆端隨機生成的,並且會產生ACK字段, ACK字段數值是在客戶端發過來的seq序列號基礎上加1進行回覆,以便客戶端收到信息時, 知曉本身的TCP斷開請求已經獲得驗證 (FIN=1, ACK=x+1, seq=y, y由服務端隨機生成)
  3. 服務端在回覆完客戶端的TCP斷開請求後,不會立刻進行TCP鏈接的斷開,服務端會先確保斷開前,全部傳輸到A的數據是否已經傳輸完畢,一旦確認傳輸數據完畢,就會將回復報文的FIN字段置1,而且產生隨機seq序列號。(FIN=1,ACK=x+1,seq=z,z由服務端隨機生成)
  4. 客戶端收到服務端的TCP斷開請求後,會回覆服務端的斷開請求,包含隨機生成的seq字段和ACK字段,ACK字段會在服務端的TCP斷開請求的seq基礎上加1,從而完成服務端請求的驗證回覆。(FIN=1,ACK=z+1,seq=h,h爲客戶端隨機生成)至此TCP斷開的4次揮手過程完畢

11種狀態

  1. 一開始,創建鏈接以前服務器和客戶端的狀態都爲CLOSED; 
  2. 服務器建立socket後開始監聽,變爲LISTEN狀態; 
  3. 客戶端請求創建鏈接,向服務器發送SYN報文,客戶端的狀態變味SYN_SENT; 
  4. 服務器收到客戶端的報文後向客戶端發送ACK和SYN報文,此時服務器的狀態變爲SYN_RCVD; 
  5. 而後,客戶端收到ACK、SYN,就向服務器發送ACK,客戶端狀態變爲ESTABLISHED; 
  6. 服務器端收到客戶端的ACK後變爲ESTABLISHED。此時3次握手完成,鏈接創建!

因爲TCP鏈接是全雙工的,斷開鏈接會比創建鏈接麻煩一點點。 

  1. 客戶端先向服務器發送FIN報文,請求斷開鏈接,其狀態變爲FIN_WAIT1; 
  2. 服務器收到FIN後向客戶端發送ACK,服務器的狀態圍邊CLOSE_WAIT; 
  3. 客戶端收到ACK後就進入FIN_WAIT2狀態,此時鏈接已經斷開了一半了若是服務器還有數據要發送給客戶端,就會繼續送; 
  4. 直到發完數據,就會發送FIN報文,此時服務器進入LAST_ACK狀態; 
  5. 客戶端收到服務器的FIN後,立刻發送ACK給服務器,此時客戶端進入TIME_WAIT狀態; 
  6. 再過了2MSL長的時間後進入CLOSED狀態。服務器收到客戶端的ACK就進入CLOSED狀態。 
  7. 至此,還有一個狀態沒有出來:CLOSING狀態。 

CLOSING狀態表示:

  • 客戶端發送了FIN,可是沒有收到服務器的ACK,卻收到了服務器的FIN,這種狀況發生在服務器發送的ACK丟包的時候,由於網絡傳輸有時會有意外。
LISTEN  --------------- # 等待從任何遠端TCP 和端口的鏈接請求。

SYN_SENT  ----------- # 發送完一個鏈接請求後等待一個匹配的鏈接請求。

SYN_RECEIVED  ------ # 發送鏈接請求而且接收到匹配的鏈接請求之後等待鏈接請求確認。

ESTABLISHED  -------- # 表示一個打開的鏈接,接收到的數據能夠被投遞給用戶。鏈接的數據傳輸階段的正常狀態。

FIN_WAIT_1  ----------- # 等待遠端TCP 的鏈接終止請求,或者等待以前發送的鏈接終止請求的確認。

FIN_WAIT_2  ---------- #  等待遠端TCP 的鏈接終止請求。

CLOSE_WAIT  --------- #  等待本地用戶的鏈接終止請求。

CLOSING  ------------- #   等待遠端TCP 的鏈接終止請求確認。

LAST_ACK  ------------ #  等待先前發送給遠端TCP 的鏈接終止請求的確認(包括它字節的鏈接終止請求的確認)

TIME_WAIT  ----------- #  等待足夠的時間過去以確保遠端TCP 接收到它的鏈接終止請求的確認。

CLOSED  --------------- # 不在鏈接狀態(這是爲方便描述假想的狀態,實際不存在)
11種狀態解析

TIME_WAIT 兩個存在的理由:

  • 可靠的實現tcp全雙工鏈接的終止;
  • 容許老的重複分節在網絡中消逝。

 

高階函數

  • map函數
    • 通常狀況map()函數接收兩個參數,一個函數(該函數接收一個參數),一個序列,將傳入的函數依次做用到序列的每一個元素,並返回一個新的Iterator(迭代器)。 例若有這樣一個list:['pYthon', 'jaVa', 'kOtlin'],如今要把list中每一個元素首字母改成大寫,其它的改成小寫,能夠這樣操做:
>>> def f(s):
...    return s.title()
... 
>>> l = map(f, ['pYthon', 'jaVa', 'kOtlin'])
>>> list(l)
['Python', 'Java', 'Kotlin']
map

 

lists= [11,22,33,44,55]
ret = map(lambda x:x if x % 2 != 0 else x + 100,lists)
print(list(ret))
# 運行結果: [11, 122, 33, 144, 55]
利用map,lambda表達式將全部偶數元素加100

 

lists= [11,22,33,44,55]
def add(num):
    if num%2 == 0:
        return num
    else:
        return num + 100
rs = map(add, lists)
自定義函數代替lambda實現相同功能

 

  • reduce函數
    • 和map()用法相似,reduce把傳入的函數做用在一個序列上,但傳入的函數須要接收兩個參數,傳入函數的計算結果繼續和序列的下一個元素作累積計算。
# 例若有一個list,裏邊的元素都是字符串,要把它拼接成一個字符串:

>>> from functools import reduce
>>> def f(x, y):
...    return x + y
... 
>>> reduce(f, ['ab', 'c', 'de', 'f'])
'abcdef'
reduce
from functools import reduce
def f(x, y):
 return x + y

print(reduce(f, [1, 3, 5, 7, 9]))  # 25
# 一、先計算頭兩個元素:f(1, 3),結果爲4;
# 二、再把結果和第3個元素計算:f(4, 5),結果爲9;
# 三、再把結果和第4個元素計算:f(9, 7),結果爲16;
# 四、再把結果和第5個元素計算:f(16, 9),結果爲25;
# 五、因爲沒有更多的元素了,計算結束,返回結果25。

print( reduce(lambda x, y: x + y, [1, 3, 5, 7, 9])  )  # 25
使用reduce進行求和運算
'''使用reduce將字符串反轉'''
s = 'Hello World'
from functools import reduce

result = reduce(lambda x,y:y+x,s)
# 一、第一次:x=H,y=e  => y+x = eH
# 二、第二次:x=l,y=eH  => y+x = leH
# 三、第三次:x=l,y=leH  => y+x = lleH
print( result )      # dlroW olleH
使用reduce將字符串反轉
  • filter函數
    • filter()一樣接收一個函數和一個序列,而後把傳入的函數依次做用於序列的每一個元素,若是傳入的函數返回true則保留元素,不然丟棄,最終返回一個Iterator。
例如一個list中元素有純字母、純數字、字母數字組合的,咱們要保留純字母的:

>>> def f(s):
...    return s.isalpha()
... 
>>> l = filter(f, ['abc', 'xyz', '123kg', '666'])
>>> list(l)
['abc', 'xyz']
filter
  • sorted函數
    • sorted()函數就是用來排序的,同時能夠本身定義排序的規則。
>>> sorted([6, -2, 4, -1])
[-2, -1, 4, 6]

>>> sorted([6, -2, 4, -1], key=abs)
[-1, -2, 4, 6]

>>> sorted([6, -2, 4, -1], key=abs, reverse=True)
[6, 4, -2, -1]

>>> sorted(['Windows', 'iOS', 'Android'])
['Android', 'Windows', 'iOS']

>>> d = [('Tom', 170), ('Jim', 175), ('Andy', 168), ('Bob', 185)]
>>> def by_height(t):
...     return t[1]
... 
>>> sorted(d, key=by_height)
 [('Andy', 168), ('Tom', 170), ('Jim', 175),  ('Bob', 185)]
sorted

sorted和sort區別

  • sort 是應用在 list 上的方法,sorted 能夠對全部可迭代的對象進行排序操做。
  • sort 是對已經存在的列表進行操做,無返回值,而 sorted 方法返回的是一個新的 list,而不是在原來的基礎上進行的操做。

sorted使用

  • sorted 語法:sorted(iterable, cmp=None, key=None, reverse=False)
    • iterable -- 可迭代對象。
    • cmp -- 比較的函數
    • key -- 主要是用來進行比較的元素,只有一個參數,具體的函數的參數就是取自於可迭代對象中,指定可迭代對象中的一個元素來進行排序。
    • reverse -- 排序規則,reverse = True 降序 , reverse = False 升序(默認)。
students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
print( sorted(students, key=lambda s: s[2], reverse=False) )    # 按年齡排序
# 結果:[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
sorted對列表排序
d = {'k1':1, 'k3': 3, 'k2':2}
# d.items() = [('k1', 1), ('k3', 3), ('k2', 2)]
a = sorted(d.items(), key=lambda x: x[1])
print(a)            # [('k1', 1), ('k2', 2), ('k3', 3)]
sorted對字典排序

 

is和==區別

  • is不只數據同樣內存地址也同樣
  • == 只判斷數據和數據類型同樣便可
  • 首先要知道Python中對象包含的三個基本要素,分別是:id(身份標識)、type(數據類型)和value(值)。
  • is和==都是對對象進行比較判斷做用的,但對對象比較判斷的內容並不相同。下面來看看具體區別在哪。
  • ==比較操做符和is同一性運算符區別
  • ==是python標準操做符中的比較操做符,用來比較判斷兩個對象的value(值)是否相等,例以下面兩個字符串間的比較:
>>> a = 'cheesezh'
>>> b = 'cheesezh'
>>> a == b
True
示例1
  • is也被叫作同一性運算符,這個運算符比較判斷的是對象間的惟一身份標識,也就是id是否相同。經過對下面幾個list間的比較,你就會明白is同一性運算符的工做原理:
>>> x = y = [4,5,6]
>>> z = [4,5,6]
>>> x == y
True
>>> x == z
True
>>> x is y
True
>>> x is z
False
>>>
>>> print id(x)
3075326572
>>> print id(y)
3075326572
>>> print id(z)
3075328140
示例2
  • 前二個例子都是True,這什麼最後一個是False呢?x、y和z的值是相同的,因此前兩個是True沒有問題。至於最後一個爲何是False,看看三個對象的id分別是什麼就會明白了。

下面再來看一個例子,例3中同一類型下的a和b的(a==b)都是爲True,而(a is b)則否則。

>>> a = 1 #a和b爲數值類型
>>> b = 1
>>> a is b
True
>>> id(a)
14318944
>>> id(b)
14318944
>>> a = 'cheesezh' #a和b爲字符串類型
>>> b = 'cheesezh'
>>> a is b
True
>>> id(a)
42111872
>>> id(b)
42111872
>>> a = (1,2,3) #a和b爲元組類型
>>> b = (1,2,3)
>>> a is b
False
>>> id(a)
15001280
>>> id(b)
14790408
>>> a = [1,2,3] #a和b爲list類型
>>> b = [1,2,3]
>>> a is b
False
>>> id(a)
42091624
>>> id(b)
42082016
>>> a = {'cheese':1,'zh':2} #a和b爲dict類型
>>> b = {'cheese':1,'zh':2}
>>> a is b
False
>>> id(a)
42101616
>>> id(b)
42098736
>>> a = set([1,2,3])#a和b爲set類型
>>> b = set([1,2,3])
>>> a is b
False
>>> id(a)
14819976
>>> id(b)
14822256
示例3

經過例3可看出,只有數值型和字符串型的狀況下,a is b才爲True,當a和b是tuple,list,dict或set型時,a is b爲False。

 

讀寫文件

open函數用來打開文件

  • open(name[, mode[, buffering]])  打開文件可傳的參數
    • open函數使用一個文件名做爲惟一的強制參數,而後返回一個文件對象。
    • 模式(mode)和緩衝(buffering)參數都是可選的
  • 打開文件的模式有
    • r — 只讀模式(默認)。
    • w — 只寫模式。【不可讀;不存在則建立;存在則刪除內容;】
    • a — 追加模式。【可讀; 不存在則建立;存在則只追加內容;】
    • 注:  "+" 表示能夠同時讀寫某個文件
    • w — 只寫模式。【不可讀;不存在則建立;存在則刪除內容;】
    • w+ — 寫讀
    • a+ — 同a
  • with語句
    • 做用:將打開文件寫在with中當對文件操做完成後with語句會自動幫關閉文件,避免忘記寫f.close()
with open("data1.txt",'r',encoding = 'utf-8') as f:
    for line in f:
        print(line)
with讀文件

三種讀操做比較

  • readline()每次讀取一行,當前位置移到下一行
  • readlines()讀取整個文件全部行,保存在一個列表(list)變量中,每行做爲一個元素
  • read(size)從文件當前位置起讀取size個字節,若是不加size會默認一次性讀取整個文件(適用於讀取小文件)
#1. read()一次讀取全部內容
'''aaa111
bbb222'''
f = open(r"data.txt")
print(f.read())
f.close()

#2. readline(),每次只讀取一行,光標下移
'''
0: aaa111

1: bbb222
'''
f = open(r"data.txt")
for i in range(2):
    print(str(i) + ": " + f.readline(),)

#3. 一次讀取全部,每行做爲列表的一個值
'''['aaa111\n', 'bbb222\n']'''
f = open(r"data.txt")
print(f.readlines())
三種讀操做舉例

讀取大文件正確方式

  • 咱們使用了一個 while 循環來讀取文件內容,每次最多讀取 8kb 大小
  • 這樣能夠避免以前須要拼接一個巨大字符串的過程,把內存佔用下降很是多。 
#!/usr/bin/python
# -*- coding: utf-8 -*-
def read_big_file_v(fname):
    block_size = 1024 * 8
    with open(fname,encoding="utf8") as fp:
        while True:
            chunk = fp.read(block_size)
            # 當文件沒有更多內容時,read 調用將會返回空字符串 ''
            if not chunk:
                break
            print(chunk)
path = r'C:\aaa\luting\edc-backend\tttt.py'
read_big_file_v(path)
python讀取大文件

 使用read()讀文件

  • read(n)讀取指定長度的文件
f = open(r"somefile.txt")
print(f.read(7))        # Welcome        先讀出 7 個字符
print(f.read(4))        #‘ to ‘                接着上次讀出 4 個字符
f.close()
read讀取指定長度字符串
  • seek(offset[, whence])          隨機訪問
  • 做用:從文件指定位置讀取或寫入
f = open(r"somefile.txt", "w")
f.write("01234567890123456789")
f.seek(5)
f.write("Hello, World!")
f.close()
f = open(r"somefile.txt")
print(f.read())                 # 01234Hello, World!89
從指定位置寫入
  • tell                                        返回當前讀取到文件的位置下標
f = open(r"somefile.txt")
f.read(1)
f.read(2)
print(f.tell())             # 3     3就是讀取到文件的第三個字符
返回讀取位置下標

readline()讀文件

  • 做用:readline 的用法,速度是fileinput的3倍左右,每秒3-4萬行,好處是 一行行讀 ,不佔內存,適合處理比較大的文件,好比超過內存大小的文件
f1 = open('test02.py','r')
f2 = open('test.txt','w')
while True:
    line = f1.readline()
    if not line:
        break
    f2.write(line)
f1.close()
f2.close()
readline讀取大文件

readlines()讀文件

  • 做用:readlines會把文件都讀入內存,速度大大增長,可是木有這麼大內存,那就只能乖乖的用readline
f1=open("readline.txt","r")
for line in f1.readlines():
    print(line)
readlines讀文件

將data1.txt中內容讀取並寫入到data2.txt中

f1 = open('data1.txt','r')
f2 = open('data2.txt','w')

for line in f1:
    f2.write(line)

f1.close()
f2.close()
將data1.txt內容讀取到data2.txt

使用eval()方法將文件讀取成字典 

f = open('data1.txt')
f1 = (f.read())
data = eval(f1)
f.close()
print(data)         # 運行結果: {'k2': 'v2', 'k3': 'v3', 'k1': 'v1'}
將文件讀取成字典

將文件內容讀取成列表

lock = []
f = open("password.txt")
for name in f.readlines():
    lock.append(name.strip('\n'))
print(lock)
運行結果: ['aaa 111', 'bbb 222', 'ccc 333']
內容讀取成列表

 

閉包

閉包概念    

  • 在一個外函數中定義了一個內函數,內函數裏運用了外函數的臨時變量,而且外函數的返回值是內函數的引用,這樣就構成了一個閉包
  • 通常狀況下,在咱們認知當中,若是一個函數結束,函數的內部全部東西都會釋放掉,還給內存,局部變量都會消失。
  • 可是閉包是一種特殊狀況,若是外函數在結束的時候發現有本身的臨時變量未來會在內部函數中用到,就把這個臨時變量綁定給了內部函數,而後本身再結束。

閉包特色    

  • 必須有一個內嵌函數
  • 內嵌函數必須引用外部函數中的變量
  • 外部函數的返回值必須是內嵌函數
#閉包函數的實例
def outer( a ):
    b = 10
    def inner():          # 在內函數中 用到了外函數的臨時變量
        print(a+b)        # 外函數的返回值是內函數的引用
    return inner

if __name__ == '__main__':
    demo = outer(5)
    demo()    # 15

# 在這裏咱們調用外函數傳入參數5
# 此時外函數兩個臨時變量 a是5 b是10 ,並建立了內函數,而後把內函數的引用返回存給了demo
# 外函數結束的時候發現內部函數將會用到本身的臨時變量,這兩個臨時變量就不會釋放,會綁定給這個內部函數
# 咱們調用內部函數,看一看內部函數是否是能使用外部函數的臨時變量
# demo存了外函數的返回值,也就是inner函數的引用,這裏至關於執行inner函數
閉包實例

閉包中內函數修改外函數局部變量 

  • 在基本的python語法當中,一個函數能夠隨意讀取全局數據,可是要修改全局數據的時候有兩種方法:
    • global 聲明全局變量
    • 全局變量是可變類型數據的時候能夠修改
  • 在閉包狀況下使用下面兩種方法修改
    • 在python3中,能夠用nonlocal 關鍵字聲明 一個變量, 表示這個變量不是局部變量空間的變量,須要向上一層變量空間找這個變量。
    • 在python2中,沒有nonlocal這個關鍵字,咱們能夠把閉包變量改爲可變類型數據進行修改,好比列表。
#修改閉包變量的實例
def outer( a ):
    b = 10            # a和b都是閉包變量
    c = [a]           # 這裏對應修改閉包變量的方法2
    def inner():
        # 方法一: nonlocal關鍵字聲明(python3)
        nonlocal  b
        b+=1

        # 方法二: 把閉包變量修改爲可變數據類型 好比列表(python2)
        c[0] += 1
        print(c[0])
        print(b)
    return inner         # 外函數的返回值是內函數的引用

if __name__ == '__main__':
    demo = outer(5)
    demo()             # 6  11
閉包中內函數修改外函數局部變量

 

上下文管理with

什麼是with語句

  • with是一種上下文管理協議,目的在於從流程圖中把 try,except 和finally 關鍵字和資源分配釋放相關代碼通通去掉,簡化try….except….finlally的處理流程。
  • 因此使用with處理的對象必須有enter()和exit()這兩個方法
  • with經過enter方法初始化(enter方法在語句體執行以前進入運行)
  • 而後在exit中作善後以及處理異常(exit()方法在語句體執行完畢退出後運行)

with語句使用場景

  • with 語句適用於對資源進行訪問的場合,確保無論使用過程當中是否發生異常都會執行必要的「清理」操做,釋放資源
  • 好比文件使用後自動關閉、線程中鎖的自動獲取和釋放等。

with處理文件操做的實例

with open('/etc/passwd') as f:
    for line in f:
        print(line)
  # 這段代碼的做用:打開一個文件,若是一切正常,把文件對象賦值給f,而後用迭代器遍歷文件中每一行,當完成時,關閉文件;
  # 而不管在這段代碼的任何地方,若是發生異常,此時文件仍會被關閉。

 

字符編碼

 

各類編碼由來

 

  • ASCII : 不支持中文(一個字母一個字節:a/b/c)
  • GBK : 是中國的中文字符,其包含了簡體中文和繁體中文的字符
  • Unicode : 萬國編碼(Unicode 包含GBK)

 

Unicode(每一個字母須要用兩個字節:a/b/c)

 

  • 存儲全部字符串都用連個字節
  • Unicode 是爲了解決傳統的字符編碼方案的侷限而產生的,它爲每種語言中的每一個字符設定了統一而且惟一的二進制編碼
  • 規定全部的字符和符號最少由 16 位來表示(2個字節),即:2 **16 = 65536
  • 這裏還有個問題:使用的字節增長了,那麼形成的直接影響就是使用的空間就直接翻倍了

 

Utf-8 : 可變長碼, 是Unicode 的擴展集

 

 

  • UTF-8編碼:是對Unicode編碼的壓縮和優化,他再也不使用最少使用2個字節,而是將全部的字符和符號進行分類
  • ascii碼中的內容用1個字節保存、歐洲的字符用2個字節保存,東亞的字符用3個字節保存...
  • 存一個a字母用一個字節,存一箇中文用三個字節

 

 

python2與python3的區別 

 

    • Python2默認 編碼方式爲ASCII, Python3 默認編碼方式爲UTF-8(是Unicode 的擴展集)
    • python2中字符串有str和unicode兩種類型, python3 中字符串有str和字節(bytes) 兩種類型
    • python3中再也不支持u中文的語法格式
    • 異常處理 Python2中try:...except Exception, e:...,在Python3中改成了try:...except Exception as e:...
    • Python3中再也不使用xrange方法,只有range方法。
    • range在Python2中返回列表,而在Python3中返回range可迭代對象。
    • 在Python2中有兩個不等運算符!=和<>,在Python3中去掉了<>,只有!=符號表示不等
    • 在Python2中雙反引號`能夠替代repr函數,在Python3中去掉了雙反引號的表是方法,只能用repr`方法。
    • 在Python2中long是比int取值範圍更大的整數,Python3中取消了long類型,int的取值範圍擴大到以前的long類型範圍。
    • 列表推導 再也不支持[n for n in a,b]語法,改成[n for n in (a,b)]或[n for n in [a,b]]
    • python 2 中經過input輸入的類型是int,只有經過raw_input()輸入的類型纔是str。
    • python 3中經過input輸入的類型都是str,去掉了row_input()方法。
  • python2和python3中編碼轉換

    • 在python3中字符串默認是unicode因此不須要decode(),直接encode成想要轉換的編碼如gb2312
    • 在python2中默認是ASCII編碼,必須先轉換成Unicode,Unicode 能夠做爲各類編碼的轉換的中轉站

 

經常使用模塊

re模塊(一)

  • 經常使用正則表達式符號
    • 通配符( . 
      • 做用:點(.)能夠匹配除換行符之外的任意一個字符串
      • 例如:‘.ython’ 能夠匹配‘aython’ ‘bython’ 等等,但只能匹配一個字符串
  • 轉義字符( \ )
    • 做用:能夠將其餘有特殊意義的字符串以本來意思表示
    • 例如:‘python.org’ 由於字符串中有一個特殊意義的字符串(.)因此若是想將其按照普通意義就必須使用這樣表示:  ‘python\.org’ 這樣就只會匹配‘python.org’ 了
    • 注:若是想對反斜線(\)自身轉義可使用雙反斜線(\\)這樣就表示 ’\’
  • 字符集
    • 做用:使用中括號來括住字符串來建立字符集,字符集可匹配他包括的任意字串
      • ‘[pj]ython’ 只可以匹配‘python’  ‘jython’
      • ‘[a-z]’ 可以(按字母順序)匹配a-z任意一個字符
      • ‘[a-zA-Z0-9]’ 能匹配任意一個大小寫字母和數字    
      • ‘[^abc]’ 能夠匹配任意除a,b和c 以外的字符串
  • 管道符
    • 做用:一次性匹配多個字符串
    • 例如:’python|perl’ 能夠匹配字符串‘python’ 和 ‘perl’
  • 可選項和重複子模式(在子模式後面加上問號?)
    • 做用:在子模式後面加上問號,他就變成可選項,出現或者不出如今匹配字符串中都是合法的
    • 例如:r’(aa)?(bb)?ccddee’ 只能匹配下面幾種狀況
      • ‘aabbccddee’
      • ‘aaccddee’
      • ‘bbccddee’
      • ‘ccddee’
  • 字符串的開始和結尾
    • ‘w+’ 匹配以w開通的字符串
    • ‘^http’ 匹配以’http’ 開頭的字符串
    • ‘ $com’ 匹配以‘com’結尾的字符串
  • 最經常使用的匹配方法
    • \d     匹配任何十進制數;它至關於類 [0-9]。
    • \D     匹配任何非數字字符;它至關於類 [^0-9]。
    • \s     匹配任何空白字符;它至關於類 [ fv]。
    • \S     匹配任何非空白字符;它至關於類 [^ fv]。
    • \w     匹配任何字母數字字符;它至關於類 [a-zA-Z0-9_]。
    • \W     匹配任何非字母數字字符;它至關於類 [^a-zA-Z0-9_]。
    • \w*    匹配全部字母字符
    • \w+    至少匹配一個字符
'.'         # 默認匹配除\n以外的任意一個字符,若指定flag DOTALL,則匹配任意字符,包括換行
 2 '^'         # 匹配字符開頭,若指定flags MULTILINE,這種也能夠匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
 3 '$'         # 匹配字符結尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也能夠
 4 '*'        #  匹配*號前的字符0次或屢次,re.findall("ab*","cabb3abcbbac")  結果爲['abb', 'ab', 'a']
 5 '+'         # 匹配前一個字符1次或屢次,re.findall("ab+","ab+cd+abb+bba") 結果['ab', 'abb']
 6 '?'         # 匹配前一個字符1次或0次
 7 '{m}'       # 匹配前一個字符m次
 8 '{n,m}'     # 匹配前一個字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 結果'abb', 'ab', 'abb']
 9 '|'         # 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 結果'ABC'
10 '(...)'     # 分組匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 結果 abcabca456c
11  
12 '\A'       # 只從字符開頭匹配,re.search("\Aabc","alexabc") 是匹配不到的
13 '\Z'       # 匹配字符結尾,同$
14 '\d'       # 匹配數字0-9
15 '\D'       # 匹配非數字
16 '\w'       # 匹配[A-Za-z0-9]
17 '\W'       # 匹配非[A-Za-z0-9]
18 's'        # 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 結果 '\t'
19 \b           # 匹配一個單詞邊界,也就是指單詞和空格間的位置,如,「er\b」能夠匹配「never」中的「er」,但不能匹配「verb」中的「er」
20 \B          #  匹配非單詞邊界。「er\B」能匹配「verb」中的「er」,但不能匹配「never」中的「er」
re模塊更詳細表達式符號
函數                                          描述

compile(pattern[, flags])         # 根據正則表達式字符串建立模式對象

search(pattern, string[, flags]) # 在字符串中尋找模式

match(pattern, 經常使用模塊[, flags]) # 在字符串的開始處匹配模式

split(pattern, string[, maxsplit=0]) # 根據模式的匹配項來分割字符串

findall(pattern, string)     # 列出字符串中模式的全部匹配項並以列表返回

sub(pat, repl, string[, count=0])     # 將字符串中全部pat的匹配項用repl替換
escape(string)     # 將字符串中全部特殊正則表達式字符轉義 

re.compile(pattern[, flags])

  • 把一個正則表達式pattern編譯成正則對象,以即可以用正則對象的match和search方法
  • 用了re.compile之後,正則對象會獲得保留,這樣在須要屢次運用這個正則對象的時候,效率會有較大的提高
import re
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
ret = re.match(mobile_re,'18538762511')
print(ret)            # <_sre.SRE_Match object; span=(0, 11), match='18538652511'>
re.compile使用

search(pattern, string[, flags]) 和 match(pattern, string[, flags])

  • match :只從字符串的開始與正則表達式匹配,匹配成功返回matchobject,不然返回none;
  • search :將字符串的全部字串嘗試與正則表達式匹配,若是全部的字串都沒有匹配成功,返回none,不然返回matchobject;
import re
a =re.match('www.bai', 'www.baidu.com')
b = re.match('bai', 'www.baidu.com')
print(a.group())                                # www.bai
print(b)                                        # None

# 不管有多少個匹配的只會匹配一個
c = re.search('bai', 'www.baidubaidu.com')
print(c)                                        # <_sre.SRE_Match object; span=(4, 7), match='bai'>
print(c.group())                                # bai
match與search使用比較

split(pattern, string[, maxsplit=0])

  • 做用:將字符串以指定分割方式,格式化成列表
import re
text = 'aa 1bb###2cc3ddd'
print(re.split('\W+', text))        # ['aa', '1bb', '2cc3ddd']
print(re.split('\W', text))          # ['aa', '1bb', '', '', '2cc3ddd']
print(re.split('\d', text))           # ['aa ', 'bb###', 'cc', 'ddd']
print(re.split('#', text))            # ['aa 1bb', '', '', '2cc3ddd']
print(re.split('#+', text))          # ['aa 1bb', '2cc3ddd']
split使用

findall(pattern, string)

  • 做用:正則表達式 re.findall 方法可以以列表的形式返回能匹配的子串
import re
p = re.compile(r'\d+')
print(p.findall('one1two2three3four4'))             # ['1', '2', '3', '4']
print(re.findall('o','one1two2three3four4'))        # ['o', 'o', 'o']
print(re.findall('\w+', 'he.llo, wo#rld!'))         # ['he', 'llo', 'wo', 'rld']
findall使用

sub(pat, repl, string[, count=0])

  • 替換,將string裏匹配pattern的部分,用repl替換掉,最多替換count次而後返回替換後的字符串
  • 若是string裏沒有能夠匹配pattern的串,將被原封不動地返回
  • repl能夠是一個字符串,也能夠是一個函數
  • 若是repl是個字符串,則其中的反斜杆會被處理過,好比 \n 會被轉成換行符,反斜杆加數字會被替換成相應的組,好比 \6 表示pattern匹配到的第6個組的內容
import re
test="Hi, nice to meet you where are you from?"
print(re.sub(r'\s','-',test))          # Hi,-nice-to-meet-you-where-are-you-from?
print(re.sub(r'\s','-',test,5))        # Hi,-nice-to-meet-you-where are you from?
print(re.sub('o','**',test))           # Hi, nice t** meet y**u where are y**u fr**m?
sub使用

escape(string)

  • re.escape(pattern) 能夠對字符串中全部可能被解釋爲正則運算符的字符進行轉義的應用函數。
  • 若是字符串很長且包含不少特殊技字符,而你又不想輸入一大堆反斜槓,或者字符串來自於用戶(好比經過raw_input函數獲取輸入的內容),且要用做正則表達式的一部分的時候,能夠用這個函數
import re
print(re.escape('www.python.org'))
escape使用

re模塊中的匹配對象和組 group()

  • group方法返回模式中與給定組匹配的字符串,若是沒有給定匹配組號,默認爲組0
  • m.group() == m.group(0) == 全部匹配的字符
import re
a = "123abc321efg456"
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0))    # 123abc321
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).groups())    # ('123', 'abc', '321')
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1))    # 123
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2))    # abc
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3))    # 321


import re
m = re.match('(..).*(..)(..)','123456789')
print(m.group(0))              # 123456789
print(m.group(1))              # 12
print(m.group(2))              # 67
print(m.group(3))              # 89
group(0)與group(1)區別比較
import re
m = re.match('www\.(.*)\..*','www.baidu.com')
print(m.group(1))           # baidu
print(m.start(1))             # 4
print(m.end(1))             # 9
print(m.span(1))            # (4, 9)
group()匹配之返回匹配索引
import re
test = 'dsfdf 22 g2323  GigabitEthernet0/3        10.1.8.1        YES NVRAM up eee'
# print(re.match('(\w.*\d)\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+YES\s+NVRAM\s+(\w+)\s+(\w+)\s*', test).groups())

ret = re.search(  r'(\w*\/\d+).*\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*(\s+up\s+)',test ).groups()
print(ret)          # 運行結果: ('GigabitEthernet0/3', '10.1.8.1', ' up ')

#1. (\w*\d+\/\d+)      匹配結果爲:GigabitEthernet0/3
#1.1   \w*: 匹配全部字母數字     
#1.2   /\d+:匹配全部斜槓開頭後根數字 (好比:/3 )

#2. (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})  匹配結果爲:10.1.8.1

#3. \s+up\s+   匹配結果爲: up 這個單詞,先後都爲空格
group()匹配ip,狀態以元組返回

re模塊其餘知識點

import re
#匹配時忽略大小寫
print(re.search("[a-z]+","abcdA").group())                #abcd
print(re.search("[a-z]+","abcdA",flags=re.I).group())            #abcdA
#連同換行符一塊兒匹配:
#'.'默認匹配除\n以外的任意一個字符,若指定flag DOTALL,則匹配任意字符,包括換行
print(re.search(r".+","\naaa\nbbb\nccc").group())            #aaa
print(re.search(r".+","\naaa\nbbb\nccc",flags=re.S))            
        #<_sre.SRE_Match object; span=(0, 12), match='\naaa\nbbb\nccc'>
print(re.search(r".+","\naaa\nbbb\nccc",flags=re.S).group())
                                                                                   aaa
                                                                                   bbb
                                                                                   ccc
re匹配忽略大小寫,匹配換行
1)init_l=[i for i in re.split('(\-\d+\.*\d*)',expression) if i]
        a. 按照相似負數的字符串分割成列表
        b. \-\d+\.*\d*是爲了能夠匹配浮點數(好比:3.14)
        c. (if i)是爲了去除列表中的空元素
        d. 分割結果:['-1', '-2', '*((', '-60', '+30+(',
2)re.search('[\+\-\*\/\(]$',expression_l[-1])
        a. 匹配expression_l列表最後一個元素是 +,-,*,/,( 這五個符號就是負數
3)new_l=[i for i in re.split('([\+\-\*\/\(\)])',exp) if i]
        a. 將字符串按照+,-,*,/,(,)切分紅列表(不是正真的負數就切分)
4)print(re.split('([\+\-])','-1+2-3*(2*2+3)'))            #按照加號或者減號分割成列表
運行結果: ['', '-', '1', '+', '2', '-', '3*(2*2', '+', '3)']
計算器用到的幾個知識點

 

paramiko模塊(二)

在windows中安裝paramiko:   pip3 install paramiko

linux中scp命令的使用

ssh root@10.1.0.51     #ssh遠程登陸
scp -rp aa.txt  root@10.1.0.50:/tmp/    #將本地aa.txt文件複製到10.1.0.50的/tmp文件夾中

Paramiko模塊做用

  • 若是須要使用SSH從一個平臺鏈接到另一個平臺,進行一系列的操做時,
  • 好比:批量執行命令,批量上傳文件等操做,paramiko是最佳工具之一。
  • paramiko是用python語言寫的一個模塊,遵循SSH2協議,支持以加密和認證的方式,進行遠程服務器的鏈接
  • 因爲使用的是python這樣的可以跨平臺運行的語言,因此全部python支持的平臺,如Linux, Solaris, BSD,MacOS X, Windows等,paramiko均可以支持
  • 若是須要使用SSH從一個平臺鏈接到另一個平臺,進行一系列的操做時,paramiko是最佳工具之一
  • 如今若是須要從windows服務器上下載Linux服務器文件:
    • a. 使用paramiko能夠很好的解決以上問題,它僅須要在本地上安裝相應的軟件(python以及PyCrypto)
    • b. 對遠程服務器沒有配置要求,對於鏈接多臺服務器,進行復雜的鏈接操做特別有幫助。

paramiko基於用戶名密碼鏈接

import paramiko

# 1 建立SSH對象
ssh = paramiko.SSHClient()
# 2 容許鏈接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 3 鏈接服務器
ssh.connect(hostname='1.1.1.3', port=22, username='root', password='chnsys@2016')

# 4 執行命令                                         #stdin標準輸入: 本身輸入的命令
stdin, stdout, stderr = ssh.exec_command('pwd')      # stdout標準輸出:  命令執行結果
# 5 獲取命令結果                                     #stderr標準錯誤:  命令執行報錯的結果
res, err = stdout.read(), stderr.read()
result = res if res else err
print(result.decode())                              #運行結果: /root

# 6 關閉鏈接
ssh.close()
遠程執行命令
import paramiko

#1 鏈接客戶端
transport = paramiko.Transport(('10.1.0.50',22))
transport.connect(username='root',password='chnsys@2016')

#2 定義與客戶端交互    將剛剛定義的transport當參數傳遞給他
sftp = paramiko.SFTPClient.from_transport(transport)
#3 將location.py 上傳至服務器 /tmp/test.py
sftp.put(r'C:\bbb\file.txt', '/tmp/file.txt')

#4 將remove_path 下載到本地 local_path
sftp.get('/tmp/file.txt',r'C:\bbb\file.txt')

#5 關閉鏈接
transport.close()
SFTPClient實現對Linux服務器上傳和下載

在兩臺Linux中演示無密碼ssh登錄對方

  •  使用ssh-copy-id命令將公鑰copy到被管理服務器中(法1:簡單)
    • 操做目的:在10.1.0.50的tom用戶下生成祕鑰對,將生成的私鑰用ssh-copy-id拷貝給10.1.0.51的root用戶,那麼root用戶就可使用sshroot@10.1.0.51遠程登錄了
[tom@localhost .ssh]$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/tom/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/tom/.ssh/id_rsa.
Your public key has been saved in /home/tom/.ssh/id_rsa.pub.
ssh-keygen生成祕鑰
  • 執行完上面命令後再 /home/tom/.ssh 文件夾下生成了公鑰私鑰連個文件:  id_rsa  id_rsa.pub
  • 將在10.1.0.50中生成的公鑰複製到10.1.0.51的root加目錄下:
  • 執行完成後就會在10.1.0.51中生成 /home/zhangsan/.ssh/ authorized_keys 文件
  • 此時輸入: ssh root@10.1.0.51       就能夠直接不用密碼登錄了

手動建立祕鑰並手動copy到被管理服務器(法2:較複雜)

  • 使用10.1.0.51在不輸入密碼的狀況下ssh連接到10.1.0.50,使用10.1.0.50的tom用戶身份進行登陸
  • 在 10.1.0.51上建立用於認證的祕鑰對
[root@localhost /]# ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.        #存放私鑰 的路徑
Your public key has been saved in /root/.ssh/id_rsa.pub.        #存放公約 的路徑
注:將10.1.0.51上生成的密鑰對中的公鑰放到10.1.0.50的服務器tom用戶家目錄/home/tom/.ssh/authorized_keys 中,就能夠在10.1.0.51中無密碼登錄10.1.0.50了
ssh-keygen生成祕鑰對
  • 在10.1.0.51中生成的私鑰路徑:cat  ~/.ssh/id_rsa.pub
  • 在被登陸服務器中建立用戶tom,將剛剛在10.1.0.51上生成的私鑰內容放到10.1.0.50的/home/tom/.ssh/authorized_keys中,
  • 新建立用戶沒用ssh登陸過期沒有,能夠手動建立
一、mkdir /home/tom/.ssh                                               #建立/home/tom/.ssh目錄
二、chmod 700 /home/tom/.ssh/                                     #將目錄權限改成 700
三、touch /home/tom/.ssh/authorized_keys                              #建立/home/tom/.ssh/authorized_keys文件
四、chmod 600 /home/tom/.ssh/authorized_keys                     #將文件權限改成600
五、將10.1.0.51的公鑰文件粘貼到10.1.0.50的/home/tom/.ssh/authorized_keys中
手動建立.ssh中的文件
  • 完成上面幾步後就能夠在10.1.0.51上無密碼登錄10.1.0.50了
  • 登錄命令:  ssh tom@10.1.0.50

paramiko基於公鑰密鑰鏈接:(ssh_rsa)

  • 操做目的:在10.1.0.50中生成公鑰和私鑰,而後將生成的公鑰放到10.1.0.51的winuser的/home/zhangsan/.ssh/ authorized_keys 目錄下
  • 第一步:在10.1.0.50中建立anyuser,在10.1.0.51中建立winuser
  • 在10.1.0.50中使用anyuser登錄,而後生成公鑰和私鑰
[anyuser@localhost ~]$ ssh-keygen 
Generating public/private rsa key pair.

Enter file in which to save the key (/home/anyuser/.ssh/id_rsa): Created directory '/home/anyuser/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/anyuser/.ssh/id_rsa.
Your public key has been saved in /home/anyuser/.ssh/id_rsa.pub.
anyuser生成祕鑰對
  • 將在10.1.0.50中生成的公鑰複製到10.1.0.51的winuser目錄下:    ssh-copy-id winuser@10.1.0.51
  • 執行完ssh-copy-id命令後就能夠看到在10.1.0.51中新加了這樣的目錄和文件/home/zhangsan/.ssh/ authorized_keys   # authorized_keys就是剛剛在10.1.0.50中生成的私鑰
  • 將10.1.0.50中生成祕鑰文件內容放到windows的PyCharm運行文件同目錄,隨便命名爲:id_rsa50.txt
import paramiko

# 1 指定公鑰所在本地的路徑
private_key = paramiko.RSAKey.from_private_key_file('id_rsa50.txt')

# 2 建立SSH對象
ssh = paramiko.SSHClient()
# 3 容許鏈接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 4 鏈接服務器
ssh.connect(hostname='10.1.0.51', port=22, username='winuser', pkey=private_key)

# 5 執行命令
stdin, stdout, stderr = ssh.exec_command('pwd')  # stdout標準輸出:  命令執行結果
# 6 獲取命令結果                                          #stderr標準錯誤:  命令執行報錯的結果
res, err = stdout.read(), stderr.read()
result = res if res else err
print(result.decode())
# 7 關閉鏈接
ssh.close()
用PyCharm基於公鑰密鑰執行命令
複製代碼
import paramiko
#1 指定公鑰所在本地的路徑
private_key = paramiko.RSAKey.from_private_key_file('id_rsa50.txt')

#2 鏈接客戶端
transport = paramiko.Transport(('10.1.0.51', 22))
transport.connect(username='winuser', pkey=private_key )
#3 定義與客戶端交互
sftp = paramiko.SFTPClient.from_transport(transport)

#4上傳 將本地test1.py 上傳至服務器 /tmp/test1.py
sftp.put('test1.py', '/tmp/test1.py')

#5下載 將服務器/tmp/test1.py文件 下載到本地C:\bbb\test1.txt
sftp.get('/tmp/test1.py', r'C:\bbb\test1.txt')

transport.close()
用PyCharm基於公鑰密鑰上傳下載

 

subprocess模塊(三)

subprocess原理以及經常使用的封裝函數

  • 運行python的時候,咱們都是在建立並運行一個進程。像Linux進程那樣,一個進程能夠fork一個子進程,並讓這個子進程exec另一個程序
  • 在Python中,咱們經過標準庫中的subprocess包來fork一個子進程,並運行一個外部的程序。
  • subprocess包中定義有數個建立子進程的函數,這些函數分別以不一樣的方式建立子進程,因此咱們能夠根據須要來從中選取一個使用
  • 另外subprocess還提供了一些管理標準流(standard stream)和管道(pipe)的工具,從而在進程間使用文本通訊。
#一、返回執行狀態:0 執行成功
retcode = subprocess.call(['ping', 'www.baidu.com', '-c5'])

#二、返回執行狀態:0 執行成功,不然拋異常
subprocess.check_call(["ls", "-l"])

#三、執行結果爲元組:第1個元素是執行狀態,第2個是命令結果
>>> ret = subprocess.getstatusoutput('pwd')
>>> ret
(0, '/test01')

#四、返回結果爲 字符串 類型
>>> ret = subprocess.getoutput('ls -a')
>>> ret
'.\n..\ntest.py'


#五、返回結果爲'bytes'類型
>>> res=subprocess.check_output(['ls','-l'])
>>> res.decode('utf8')
'總用量 4\n-rwxrwxrwx. 1 root root 334 11月 21 09:02 test.py\n'
subprocess經常使用函數

 

subprocess.check_output(['chmod', '+x', filepath])
subprocess.check_output(['dos2unix', filepath])
將dos格式文件轉換成unix格式

subprocess.Popen()

  • 實際上,上面的幾個函數都是基於Popen()的封裝(wrapper),這些封裝的目的在於讓咱們容易使用子進程
  • 當咱們想要更個性化咱們的需求的時候,就要轉向Popen類,該類生成的對象用來表明子進程
  • 與上面的封裝不一樣,Popen對象建立後,主程序不會自動等待子進程完成。咱們必須調用對象的wait()方法,父進程纔會等待 (也就是阻塞block)
  • 從運行結果中看到,父進程在開啓子進程以後並無等待child的完成,而是直接運行print。
#一、先打印'parent process'不等待child的完成
import subprocess
child = subprocess.Popen(['ping','-c','4','www.baidu.com'])
print('parent process')

#二、後打印'parent process'等待child的完成
import subprocess
child = subprocess.Popen('ping -c4 www.baidu.com',shell=True)
child.wait()
print('parent process')
child.wait()等待子進程執行
child.poll()                                 # 檢查子進程狀態
child.kill()                                  # 終止子進程
child.send_signal()                   # 向子進程發送信號
child.terminate()                       # 終止子進程

subprocess.PIPE 將多個子進程的輸入和輸出鏈接在一塊兒

  • subprocess.PIPE實際上爲文本流提供一個緩存區。child1的stdout將文本輸出到緩存區,隨後child2的stdin從該PIPE中將文本讀取走
  • child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
  • 注意:communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成
import subprocess
#下面執行命令等價於: cat /etc/passwd | grep root
child1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["grep","root"],stdin=child1.stdout, stdout=subprocess.PIPE)
out = child2.communicate()               #返回執行結果是元組
print(out)
#執行結果: (b'root:x:0:0:root:/root:/bin/bash\noperator:x:11:0:operator:/root:/sbin/nologin\n', None)
分步執行cat /etc/passwd | grep root命
import subprocess

list_tmp = []
def main():
    p = subprocess.Popen(['ping', 'www.baidu.com', '-c5'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
    while subprocess.Popen.poll(p) == None:
        r = p.stdout.readline().strip().decode('utf-8')
        if r:
            # print(r)
            v = p.stdout.read().strip().decode('utf-8')
            list_tmp.append(v)
main()
print(list_tmp[0])
獲取ping命令執行結果

 

 

 

‘aabbccddee’

         ‘aaccddee’

         ‘bbccddee’

         ‘ccddee’

相關文章
相關標籤/搜索