天天看點

python_learn Ⅰ

基于 廖雪峰python3教程 學習。

目錄:

  • 01_輸入輸出.py
  • 02_list、tuple.py
  • 03_條件判斷.py
  • 04_循環.py
  • 05_利用循環排序.py
  • 06_自定義3元2次方程的根求解函數.py
  • 07_函數的參數.py
  • 08_遞歸函數.py
  • 09_切片.py
  • 10_疊代、索引.py
  • 11_清單生成式.py
  • 12_元組循環疊代清單生成式輸出天幹地支.py
  • 13_将清單大小寫字元全部小寫輸出.py
  • 14_生成器.py
  • 15_生成器生成斐波拉契數列.py
  • 16_生成器生成楊輝三角.py
  • 17_疊代器.py
  • 18_高階函數_map、reduce.py
  • 19_filter.py
  • 20_sorted.py
  • 21_傳回函數.py
  • 22_匿名函數.py
  • 23_偏函數.py
  • 24_類和執行個體.py
  • 25_通路限制.py
  • 26_繼承和多态.py
  • 27_擷取對象資訊.py
  • 28_執行個體屬性和類屬性.py
  • 29_限制執行個體屬性__slots__.py
  • 30_使用@property.py
  • 31_多重繼承.py
  • 32_枚舉類.py
  • 33_檔案讀寫.py
  • 34_StringIO_BytesIO.py
  • 35_操作檔案和目錄.py
  • 36_代碼實作dir -l功能.py
  • 37_序列化_pickle子產品.py
  • 38_JSON.py
  • 39_多程序.py
  • 40_多線程.py
  • 41_正規表達式.py
  • 42_datetime子產品.py
  • 43_collections子產品.py
  • 44_base64子產品.py
  • 45_hashlib子產品.py
  • 46_hmac子產品.py
  • 47_itertools子產品.py
  • 48_contextlib子產品.py
  • 49_urllib子產品.py
  • 50_XML解析.py
  • 51_HTMLParser子產品.py
  • 52_pillow子產品.py
  • 53_requests子產品.py
  • 54_chardet子產品.py
  • 55_psutil子產品.py
  • 56_TCP程式設計.py
  • 57_UDP程式設計.py
  • 58_SMTP發送郵件.py
  • 59_POP3收取郵件.py
  • 60_使用SQLite.py
  • 61_使用MySQL.py
  • 62_使用SQLAIchemy.py
  • 63_WSGI接口.py

條件判斷

#!/usr/bin/env python
# -*- coding: utf-8 -*-
h = float(input('請輸入你的身高: '))
w = float(input('請輸入你的體重: '))
bmi =float('%.1f' % (w//(h*h)))

if bmi<18.5:
    print('您的體重過輕')
elif bmi<25:
    print('您的處于正常範圍')
elif bmi<28:
    print('您體重過重')
elif bmi<32:
    print('您處于肥胖狀态')
else:
    print('您嚴重肥胖')
           

循環

#!/usr/bin/env python
# -*- coding: utf-8 -*-
L = ['Bart','Lisa','Adam']
for x in L:
    print('hello,',x)
           

利用循環排序

#!/usr/bin/env python
# -*- coding: utf-8 -*-

array = [1, 2, 5, 3, 6, 8, 4]
for i in range(len(array) - 1, 0, -1):
    print(i)
    for j in range(0, i):
        print(j)
        if array[j] > array[j + 1]:
            array[j], array[j + 1] = array[j + 1], array[j]
print(array)
           

三元二次方程求解

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import math
def quadratic(a,b,c):
    for s in (a,b,c):
        if not isinstance(s, (int, float)):
            raise TypeError('數字類型輸入錯誤,請重新輸入')
    d = float(b*b-4*a*c)
    if d < 0:
        print('方程無解')
    elif d == 0:
        x = (b + math.sqrt(d))/ (2*a)
        print('方程僅一個解: %.1f' % x)
    else:
        x1 = (b + math.sqrt(d))/(2*a)
        x2 = -(b + math.sqrt(d))/(2*a)
        print('方程有兩個解: %.1f' % x1,x2)

quadratic(1,4,4)
           

函數的參數

#!/usr/bin/env python
#-*- coding: utf:8 -*-
# 階乘函數
def power(x,n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

def power(x,n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

print(power(4,3))
print(power(5))
           

遞歸函數

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 利用遞歸函數計算階乘
# N! = 1 * 2 * 3 * ... * N
def fact(n):
    if n == 1:
        return 1
    return n * fact(n-1)

print('fact(1) =', fact(1))
print('fact(5) =', fact(5))
print('fact(10) =', fact(10))
print('遞歸調用次數太多容易出現棧溢出')
print('--------------------------')

# 利用遞歸函數移動漢諾塔:
def move(n, a, b, c):  # n 為最初A柱子上的圓盤個數,将其從A移到C,B為緩沖區
    if n == 1:
        print('move', a, '-->', c)  # n=1 時,直接從A到C
    else:
        move(n-1, a, c, b)  # n 個時,先将 n-1 從A移到B
        move(1, a, b, c)  # 再将剩下最大的一個從A移到C
        move(n-1, b, a, c)  # 将剛才放在B上的 n-1 從B移到C
move(3, 'A', 'B', 'C')
           

切片

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 切片隻能在list清單和tuple元組中,dict字典中不能進行切片輸出

# list
L = list(range(20))
print(L)        #輸出清單L
print(L[:])      #同上,輸出所有清單數字,從起始到結束,起始結束位置可不寫
print(L[:5])    #輸出前5個數字,起始位置可省略
print(L[-3:])    #輸出後3個數字,結尾位置可省略
print(L[::2])    #每隔一位輸出L清單(輸入偶數位)
print(L[1::2])  #從第一位起每隔兩位輸出L清單(輸出奇數位)
print(L[::-1])  #倒序輸出清單L
print(L[::-3])  #倒序每隔兩位輸出L
print('------------------------------------------------------------')

# tuple
s = (1,2,3,4,5)
print(s[:])
print(s[::-1])
           

疊代、索引

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#使用for...in...輸出就是一種疊代
d = {'a':5, 'b':6, 'c':7, 'd':8}

for key in d:
    print(key)    #疊代輸出字典d的字元

print('-----------------分隔符----------------')

for value in d.values():
    print(value)  #疊代輸出字典d的字元的值

print('-----------------分隔符-----------------')

#enumerate()函數可以索引列舉 list、tuple、dict 裡面的内容
for zifu in d:
    print('輸出字典d的内容:',zifu)

for value in d.values():
    print('輸出字典d的字元值:',value)

for suoyin in enumerate(d):
    print('索引列舉出字典d的内容:', suoyin)  #索引單個參數輸出的是元組,如 (1, 2)

for key,hey in enumerate(d):
    print('索引列舉出 key,hey:', key,hey)  #索引多個參數輸出的是 字元序号 和 字元内容本身。

for x,y in ((1,1),(2,4),(3,9)):
    print(x,y)

print('-----------------分隔符-----------------')

#疊代可輸出 清單、元組、字典、字元串,但不能疊代整數
#判斷是否可疊代, isinstance() 判斷對象類型
from collections import Iterable
print('isinstance([1,2,3],Iterable):', isinstance([1,2,3],Iterable))
print('isinstance((1,2,3),Iterable):', isinstance((1,2,3),Iterable))
print("isinstance({'a':1, 'b':2, 'c':3},Iterable):", isinstance({'a':1, 'b':2, 'c':3},Iterable))
print("isinstance('ABC'),Iterable:", isinstance('ABC',Iterable))
print('isinstance(123,Iterable):', isinstance(123,Iterable))
           

清單生成式

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#清單生成三種方法
print('法一:直接通過range()生成')
print(list(range(10)))

print('法二:通過循環在空清單末尾疊加')
L = []
for x in range(10):
    L.append(x*x)
print(L)

print('法三:清單生成式')
print([x*x for x in range(10)])              #直接清單生成式輸出
print([x*x for x in range(10) if x%2==0])    #輸出偶數的平方清單
print([x+y for x in 'ABCD' for y in 'EFGH'])  #二層循環輸出清單

# *.values()、*.items()函數使用
d = {'A':'a', 'B':'b', 'C':'c', 'D':'d'}
for x in d.values():
    print(x)              #輸出字元的值

for y,z in d.items():
    print(y,'=',z)        #将字元的值指派給字元,即 A=a
           

元組循環疊代清單生成式輸出天幹地支

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#天幹地支
print('***** 法一:循環 *****')

a=('甲','乙','丙','丁','戊','己','庚','辛','壬','癸')
b=('子','醜','寅','卯','辰','巳','午','未','申','酉','戌','亥')
c=[]
for i in range(60):
    print(i)
    c.append(a[i%len(a)]+b[i%len(b)])
print(c)

print('------------------------------------------------------')

print('***** 法二:清單生成式 *****')
#清單生成式一句代替法一循環輸出,代碼更簡潔
a = ['甲','乙','丙','丁','戊','己','庚','辛','壬','癸']
b = ['子','醜','寅','卯','辰','巳','午','未','申','酉','戌','亥']
c = [a[i%len(a)]+b[i%len(b)] for i in range(60)]
print (c)
           

将清單大小寫字元全部小寫輸出

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#将清單的内容全部小寫輸出

print('--------- 場景一 ---------')
L = ['Hello', 'World', 'Apple']
print([s.lower() for s in L])


print('--------- 場景二 ---------')
L1 = ['Hello', 'World', 18, 'Apple', None]
#print([s.lower() for s in L1])  在L清單中能這樣,L1中不能這樣輸出,因為 *.lower()函數對象隻針對于字元串,不能對整型,空值進行。
L2 = []
for s in L1:
    if isinstance(s, str):    # isinstance()函數 判斷 變量類型
        L2.append(s.lower())
    else:
        L2.append(s)
print(L2)
           

生成器

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#清單生成
L = [x*x for x in range(10)]
print(L)
print('----------- 分隔符 ----------')

#生成器,不一次生成,有效解決清單生成式循環次數過多帶來的記憶體限制及空間浪費,生成器邊循環邊計算邊輸出
g = (x*x for x in range(10))
print('第一個:',next(g))
print('第二個:',next(g))
print('第三個:',next(g))
print('第四個:',next(g))
print('----------- 分隔符 ----------')

#生成器也可利用循環一次單行輸出所有,不用向上面每次敲 next(g)
g = (x*x for x in range(10))
for s in g:
    print(s)
print('----------- 分隔符 ----------')

#生成器一次輸出成一個清單,如最上面的清單生成一樣
g = (x*x for x in range(10))
print([n for n in g])
           

生成器生成斐波拉契數列

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#斐波拉契數列:從第三位起,每位數都等于前兩位數字的和
# 1,1,2,3,5,8,13,21,34,...

#方法一:判斷、循環自定義顯示斐波拉契數列的數字個數
max = int(input('請輸入清單顯示的位數 max:'))
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
print(fib(max))
print('----------------- 分隔符 ----------------')

#方法二:生成器生成,使用了 yield ,然後循環列出,輸出沒有 None 字元
min = int(input('請輸入清單顯示的位數 min:'))
def fab(min):
    n, a, b = 0, 0, 1
    while n < min:
        yield b
        a, b = b, a+b
        n = n + 1

L = []
for i in fab(min):
    L.append(i)
print(L)
print('------------ 分隔符 -----------')

#方法三:遞歸、循環自定義顯示斐波拉契數列的數字個數
def xh(n):
    if n==1:
        return 1
    elif n==2:
        return 1
    else:
        return xh(n-1)+xh(n-2)

L = []
a = int(input('請輸入清單顯示的位數 a:'))
for s in range(1,a+1):
    L.append(xh(s))
print(L)

# 以上第一種和第三種會因函數中的變量值的加大而損耗記憶體,第二種生成器生成的,使用 yield 每次執行一次循環就挂起,
# 下次再從挂起的地方調用,不會一次性生成完,進而不會占用系統資源。雖然在此輸出結果是相同的,這是因為for...in...
# 循環調用輸出的次數使其全部輸出了。
           

生成器生成楊輝三角

疊代器

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 可直接作用于 for 循環的對象統稱為 可疊代對象:Iterable
# list、tuple、dict、str、for循環,是可疊代對象,整數不是可疊代對象。ininstance()判斷 變量類型。
from collections import Iterable
print('isinstance([],Iterable):',isinstance([],Iterable))
print('isinstance((),Iterable):',isinstance((),Iterable))
print('isinstance({},Iterable):',isinstance({},Iterable))
print("isinstance('abc',Iterable):",isinstance('abc',Iterable))
print('isinstance((x for x in range(10)),Iterable):',isinstance((x for x in range(10)),Iterable))
print('isinstance(100,Iterable):',isinstance(100,Iterable))
print('-------------------- 分隔符 ------------------')


# 不但可以作用于 for 循環,還可以被next()函數不斷調用并傳回下一個值的叫 疊代器:Iterator
# list、tuple、dict、str、int 不是 疊代器,for 循環的 為 疊代器
from collections import Iterator
print('isinstance([],Iterator):',isinstance([],Iterator))
print('isinstance((),Iterator):',isinstance((),Iterator))
print('isinstance({},Iterator):',isinstance({},Iterator))
print("isinstance('abc',Iterator):",isinstance('abc',Iterator))
print('isinstance((x for x in range(10)),Iterator):',isinstance((x for x in range(10)),Iterator))
print('isinstance(100,Iterator):',isinstance(100,Iterator))
print('-------------------- 分隔符 ------------------')


# 将 可疊代對象 變為 疊代器 可以使用 iter() 函數
from collections import Iterator
print('isinstance(iter([]),Iterator):',isinstance(iter([]),Iterator))
print('isinstance(iter(()),Iterator):',isinstance(iter(()),Iterator))
print('isinstance(iter({}),Iterator):',isinstance(iter({}),Iterator))
print("isinstance(iter('abc'),Iterator):",isinstance(iter('abc'),Iterator))
print('isinstance((x for x in range(10)),Iterator):',isinstance((x for x in range(10)),Iterator))
print('isinstance(iter(100),Iterator):',isinstance(iter(100),Iterator))  # 注:整數不是 可疊代對象,是以加iter()也不能成為疊代器

# 小結:凡是可作用于for循環的對象都是Iterable類型;
# 凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
# 疊代器都是可疊代對象,可疊代對象不一定是疊代器。
           

高階函數map、reduce

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# map()函數:map()接收兩個參數,一個是函數,一個是Iterable,map将傳入的函數依次作用到序列的每個元素,并把結果作為新的Iterator傳回。

# 示例一
def f(x):
    return x*x
r = map(f, [1, 2, 3, 4, 5])
print(list(r))

# 示例二
print(list(map(str, [1, 2, 3, 4, 5])))
print('------------------------------')


# reduce():作用在序列上,函數必須接收兩個參數。将結果繼續和序列下一個元素做累積計算。
# 在python3 中使用reduce函數要先從庫中加載

# 示例一:清單元素求和
from functools import reduce
def sum(x,y):
    return x+y
print(reduce(sum, [1, 2, 3, 4, 5]))

# 示例二:将 [1,3,5,7,9] 輸出為 13579
from functools import reduce
def fn(m, n):
    return 10 * m + n
print(reduce(fn, [1, 3, 5, 7, 9]))
print('------------------------------')

# 練習一:
# 利用map()函數,把使用者輸入的不規範的英文名字,變為首字母大寫,其他小寫的規範名字。
# 輸入:['adam', 'LISA', 'barT'],輸出:['Adam', 'Lisa', 'Bart']
# 大寫字母轉小寫函數 lower(), 小寫字母轉大寫函數 upper()
L1 = ['adam', 'LISA', 'barT']
def normalize(name):
    return name[0].upper()+name[1:].lower()
L2 = list(map(normalize, L1))
print(L2)
print('------------------------------')

# 練習二:
# sum()函數和上面reduce()函數代碼均可求 list 的和,仿照定義 pord() 函數,求 list 的積
from functools import reduce
def s(x,y):
    return x * y
L = [3, 5, 7, 9]
def prod(L):
    return reduce(s, L)
print(prod(L))

print('------------------------------')

# 練習三:
# 利用 map 和 reduce 編寫一個 str2float 函數,把字元串'123.456'轉換成浮點數123.456
from functools import reduce
def str2float(s):
    s = s.split('.')    #以小數點為分隔符,把字元串分為兩部分
    def f1(x,y):        #函數1,小數點之前的數用這個函數處理
        return x * 10 + y
    def f2(x,y):        #函數2,小數點之後的數用這個函數處理
        return x / 10 + y
    def str2num(str):    #函數3,用于把字元串'123'逐個變為數字
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[str]
    return reduce(f1,list(map(str2num,s[0]))) + reduce(f2,list(map(str2num,s[1]))[::-1])/10

print(str2float('123.456'))
# 最後一步是這個解法的精髓:
# 小數點前的數'123',用 x * 10 + y 正常求和就能得出123,小數點之後的數'456'要怎樣才能得出0.456呢?
# 首先把字元串'456'用list(map(str2num,s[1]))轉成一個清單[4,5,6]
# 然後用[::-1]切片的方式從後往前取,清單變為[6,5,4]
# 然後把[6,5,4]利用reduce函數放到f2函數中計算,( 6 / 10 + 5) / 10 + 4 = 4.56,得出結果4.56
# 再除以一個10,得出0.456,到此成功把字元串'456'變成了浮點數0.456
# 把前後結果加起來,就得到了最終解,成功把字元串'123.456'變成了浮點數123.456
           

filter

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 與map()類似,filter()也接收一個函數和一個序列。
# 和map()不同的是,filter()把傳入的函數依次作用于每個元素,然後根據傳回值是True還是False決定保留還是丢棄該元素。
# 而map()僅傳回 True or False
def is_odd(n):
    return n % 2 == 1
print(list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])))  # filter() 将自定義函數 is_odd() 為True的值傳回
print(list(map(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])))      # map() 僅判斷 True or False,不作篩選
print('----------------------------------------------------------')


# 練習一:計算素數
# 首先,列出從2開始的所有自然數,構造一個序列:
# 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
# 取序列的第一個數2,它一定是素數,然後用2把序列的2的倍數篩掉:
# 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
# 取新序列的第一個數3,它一定是素數,然後用3把序列的3的倍數篩掉:
# 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
# 取新序列的第一個數5,然後用5把序列的5的倍數篩掉:
# 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
# 不斷篩下去,就可以得到所有的素數。

def _odd_iter():    # 選取從 2 之後,且不為2倍數的數
    n = 1
    while True:
        n = n + 2
        yield n

def _not_divisible(n):    # 篩選掉以上所選數的倍數
    return lambda x: x % n > 0

def primes():    # 疊代過濾出滿足要求的數 n
    yield 2
    it = _odd_iter()
    while True:
        n = next(it)
        yield n
        it = filter(_not_divisible(n), it)

def main():        # 給 n 一個取值,輸出這些數
    for n in primes():
        if n < 10:
            print(n)
        else:
            break

if __name__ == '__main__':
    main()

print('----------------------------------------------------------')

# 練習二:列出 1~1000 的回數(順着倒着讀都一樣,如:12321,909),使用filter()過濾掉非回數
def is_palindrome(n):
    return str(n) == str(n)[::-1]

output = filter(is_palindrome, range(1, 1000))
print(list(output))
           

sorted

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# sorted() 函數可以對list元素按小到大排序
print(sorted([36, 5, -12, 9, -21]))

# 此外 sorted() 也是一個高階函數
print(sorted([36, 5, -12, 9, -21], key=abs))

# 字元串也是可以排序的,按的首字母ASCII的大小
print(sorted(['bob', 'about', 'Zoo', 'Credit']))

# 如果要按字母順序,就需要加參數排除首字母大小寫 ASCII 大小不一的幹擾
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower))

print('-----------------------------------')

# 練習:
# 一組學生名字和成績tuple,分别按名字字母、成績排序
# L = [('Bob', 75), ('adam', 92), ('bart', 66), ('Lisa', 88)]

L = [('Bob', 75), ('adam', 92), ('bart', 66), ('Lisa', 88)]
def by_name(name):
    return name[0].lower()

def by_score(score):
    return score[1]

print(sorted(L, key=by_name))
print(sorted(L, key=by_score))
# 對于單個元組,(名字,成績): name[0] 為名字,score[1] 為成績
           

傳回函數

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 函數作為傳回值
# map reduce filter sorted 等高階函數,它們将函數作為參數,除次之外,函數還可作為傳回值。

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

f = lazy_sum(1, 2, 4, 5, 7, 8, 9)
print(f)    # 輸出錯誤,因為輸出f時,f = lazy_sum() ,輸出的是 lazy_sum() 函數裡面的 sum() 求和函數
print(f())  # 輸出 f() 時,才是輸出 求和函數 sum() 的計算結果

f2 = lazy_sum(1, 2, 4, 5, 7, 8, 9)
print( f==f2 )      # 調用lazy_sum()時,每次調用都會傳回一個新的函數,即使傳入相同的參數。 是以 False
print( f()==f2() )  # 函數的參數一樣,傳回的結果值也一樣,是以 True

# *args 與 **args 的差別:
# *args和**args适用于函數的參數不确定的時候。*args可以看做是多個變量組成的tuple,**args可以看做是個字典。

# def funarg1(arg):  #隻允許傳入一個參數
#    print(arg)
# funarg1(1)
# >>1          #輸出執行結果

# def funarg(arg1,arg2,arg3): #必須傳入3個參數
#    print(arg1,arg2,arg3)
# funarg(1,2,3)
# >>1,2,3      #輸出執行結果

# def funargs(*args): #可以傳入任意數量個參數(包括零個)
#    for i in args:
#        print(i)
# funargs(1,2,3,4,5)  #傳入5個參數
# list2=(2,3,4,5,6)
# funargs(list2)      #傳入清單
# funargs()
# >>1 2 3 4 5 (2,3,4,5,6)

print('----------------------------')

# 閉包
# why f1(), f2(), f3() returns 9, 9, 9 rather than 1, 4, 9?
def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i * i
        fs.append(f)        # 此時函數f并沒有執行,隻是傳回了函數f
    return fs
f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())

print('----------------------------')

def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i * i
        fs.append(f())        # 此時函數f執行了,傳回了具體的結果數值。這就不屬于 傳回函數 了
    return fs

f1, f2, f3 = count()

print(f1)
print(f2)
print(f3)

print('----------------------------')

# fix:
def count():
    fs = []
    def f(n):
        def j():
            return n * n
        return j
    for i in range(1, 4):
        fs.append(f(i))  # f(i)立刻被執行,是以i的目前值被傳入f()
    return fs

f1, f2, f3 = count()

print(f1())
print(f2())
print(f3())

# 小結:
# 函數既可以的作為高階函數的參數,又可以作為一個函數的傳回函數。
# 傳回一個函數時,該函數并未執行,傳回函數中不要引用任何可能會變化的變量。
           

匿名函數

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 匿名函數 lambda x: x * x
# 匿名函數簡介友善,不用事先定義,也不用擔心函數重名,但其隻有有一個參數,一個表達式。

print(list(map(lambda x: x * x, [1, 2, 3, 4, 5])))

# 等同于:
def f(x):
    return x * x
print(list(f(x) for x in [1, 2, 3, 4, 5]))
           

偏函數

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# int()函數可以把字元串轉換成整數
print(int('12345'))

# int()還可提供額外的base參數
print(int('12345',base=8))  #轉化成8進制

print('----------------')

# 如果經常轉換,每次都加 base 參數,很麻煩,可以定義一個函數
def int2(x,base=2):
    return int(x, base)

print(int2('1011010'))

print('----------------')

# 其實不用自定義函數,python自帶的 functools.partial()偏函數 就能實作這一功能
import functools
int2 = functools.partial(int,base=2)  # functools.partial 就建立了一個偏函數

print(int2('1011010'))
           

類和執行個體

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 面向對象程式設計
# 類和執行個體

# 類是一個抽象的模版,執行個體是根據類建立出來的具體對象,屬性是這個執行個體所具有的能力
# 比如說貓類、狗類,然後狗類裡面又有具體的執行個體(泰迪、金毛、二哈),這些狗的執行個體都具有的屬性(愛吃肉、骨頭、會遊泳)

# 定義 類 使用 class:
# class Student(object):
#    psss
# class 後面緊跟 類名(Student), 類名一般首字母大寫。(object)表示該類從哪個類繼承下來的。

# 定義 執行個體, 通過 類名+()實作
class Student(object):    # 建立Student類
    pass
bart = Student()      # 建立 bart 執行個體
bart.name = 'simpson'  # 給執行個體綁定屬性,name,這個執行個體叫什麼
print(bart.name)

# 建立執行個體的時候,把一些必須綁定的屬性強制填寫進去,通過 __init__ 方法 添加上相關屬性。
# 以雙下劃線'__'開頭和結尾的是特殊變量
class Student(object):
    def __init__(self, name, score):    # __init__ 方法的第一個參數永遠是 self
        self.name = name
        self.score = score
bart = Student('simpson', 99)
print(bart.name)
print(bart.score)

print('------------------- ')


# 資料封裝
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))
    def grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'
# 以上整個類可以封裝起來,對于使用者來說,隻要輸入 名字、成績 即可,不需要了解過程

# 輸入
bart = Student('simpson', 99)
bart1 = Student('john', 67)
bart2 = Student('tom', 34)

# 輸出
print('bart.name=', bart.name)
print('bart.score=', bart.score)
bart.print_score()
print('grade of bart:', bart.grade())
print('grade of bart1:', bart1.grade())
print('grade of bart2', bart2.grade())
           

通路限制

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 普通的類、執行個體、屬性代碼,可執行無報錯
class Student():
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def print_score(self):
        print('%s: %s' % (self.name, self.score))

bart = Student('simpson', 99)
print(bart.name)

# 做了通路限制的屬性,從外部通路該執行個體的屬性,不能執行,回顯錯誤
class Student():
    def __init__(self, name, score):
        self.__name = name        # 學生類的 __name__ 屬性,是内部屬性,外部不可通路
        self.__score = score      # 學生類的 __score__ 屬性,是内部屬性,外部不可通路
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

bart = Student('simpson', 99)
# print(bart.__name)    # 不可通路,代碼報錯。因為python解釋器把 __name 變成了 _Student__name
print(bart._Student__name)    # 如果要在外部通路,可以使用 _Student__name
           

繼承和多态

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 繼承:可以把父類的功能直接拿過來,也可新增自己的方法,也可覆寫重寫父類的方法,
# 這也造就了子類比父類有更多的可能,形成多态。

class Animal(object):    # 父類 動物類
    def run(self):
        print('Animal is running...')

class Dog(Animal):      # 子類 狗類
    pass
class Cat(Animal):      # 子類 貓類
    pass

dog = Dog()
cat = Cat()

dog.run()    # 無需再定義run(),直接調用父類的函數
cat.run()

print('---------------------')

# 多态
class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    def run(self):
        print('Dog is running...')    # 覆寫父類的方法
    def eat(self):
        print('Liking eat meat.')      # 新增自己的方法

dog = Dog()

dog.run()
dog.eat()
           

擷取對象資訊

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# type() 判斷對象類型
print(type(123))    # int型
print(type('abc'))  # str
print(type(None))    # NoneType
print(type(abs))    # 函數方法

print(type(123)==type(456))  # 判斷是否為同種類型, True or False。
print(type(123)==int)

print('----------------')

# isinstance() 與 type() 作用相同, isinstance() 多用于 class 繼承關系裡
class Animal(object):
    pass
class Dog(Animal):
    pass
class Husky(Dog):
    pass

a = Animal()
d = Dog()
h = Husky()

print(isinstance(h, Husky))
print(isinstance(h, Dog))
print(isinstance(h, Animal))

# dir():獲得一個對象的所有屬性和方法
print(dir('ABC'))

# 除了把對象的屬性和方法列出來,還可使用 getattr()、setattr()、hasattr() 直接操作一個對象的狀态
# getattr() 擷取對象屬性, setattr() 設定對象屬性, hasattr() 詢問對象是否有某屬性
class MyObject(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x
obj = MyObject()

print(hasattr(obj, 'x'))    # obj 有 x 屬性嗎?
print(getattr(obj, 'x'))    # 擷取 x 屬性的結果
print(obj.x)                # 等同于 getattr()
print(hasattr(obj, 'y'))    # 詢問是否有 y 屬性

setattr(obj, 'y', 10)        # 設定 y 屬性及其具體值
print(getattr(obj, 'y'))    # 擷取 y 屬性結果
           

執行個體屬性和類屬性

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 執行個體屬性 和 類屬性 不要使用相同的名字,同名的 執行個體屬性 将覆寫 類屬性,當删除 執行個體屬性 後,通路的是 類屬性

class Student(object):        # Student類
    name = 'Student'          # 類的 name 屬性

s = Student()                # 類的 執行個體s

print(s.name)                # 列印 執行個體s 的 name屬性,s并沒有該屬性,往上找到它的類的 name屬性
print(Student.name)          # 列印 類的 name屬性

s.name = 'Michael'            # 給執行個體s設定一個name屬性 Michael
print(s.name)                # 由于 執行個體屬性 優先級比 類屬性 高,覆寫 類屬性,列印出 執行個體屬性
print(Student.name)          # 列印 類屬性

del s.name                    # 删除 執行個體屬性
print(s.name)                # 輸出的就是 類屬性
           

限制執行個體屬性__slots__

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 動态語言可以随時給執行個體綁定任何屬性和方法
class Student(object):      # 定義一個類
    pass

s = Student()              # 建立類的 執行個體s
s.name = 'Michael'          # 動态給 執行個體s 加一個 name屬性
print(s.name)

def set_age(self, age):    # 定義一個函數作為執行個體方法
    self.age = age

from types import MethodType
s.set_age = MethodType(set_age, s)    # 給 執行個體s 綁定 set_age 方法
s.set_age(25)        # 調用 set_age 方法
print(s.age)


s2 = Student()      # 建立類的 執行個體s2
# s2.set_age(15)      # 調用 set_age 方法
# print(s2.age)      # 輸出結果出錯,因為 s2 并未綁定 set_age 方法,給一個執行個體綁定方法,對另一個執行個體不起作用

# 為了給所有執行個體都添加可調用的方法,可對 class 綁定方法
# 方法一:動态綁定,可在程式運作過程中動态給 class 加上功能,這在靜态語言中很難實作
def set_score(self, score):        # 程式任意位置定義 set_score 方法
    self.score = score

Student.set_score = set_score      # 給 Student類 綁定 set_score 方法

s.set_score(100)        # 執行個體s 可直接調用 class 方法
print(s.score)
s2.set_score(99)        # 執行個體s2 也可直接調用 class 方法
print(s2.score)

# 方法二:直接在最初建立類的地方定義類的方法
class Student(object):
    def set_score(self, score):
        self.score = score

# 特殊變量 __slots__  (前面還學習到特殊變量__init__)
# 該變量可限制執行個體屬性,隻允許添加定義的屬性,相當于屬性白名單。
# __slots__ 僅對目前類的執行個體生效,對其繼承的子類執行個體不生效
class Student(object):
    __slots__ = ('name', 'age')    # 隻允許定義這兩種屬性

s = Student()      # 建立執行個體
s.name = 'Tom'      # 添加執行個體 name屬性
s.age = '17'        # 添加執行個體 age屬性
# s.score = '90'    # 添加執行個體 score屬性:不被允許,報錯
print(s.name)
print(s.age)
# print(s.score)

class Xiaoming(Student):    # 建立子類 Xiaoming
    pass

y = Xiaoming()        # 建立執行個體y
y.name = 'xm'
y.age = '18'
y.score = '99'        # 父類中 禁用的屬性,不影響 子類 的使用
print(y.name)
print(y.age)
print(y.score)        # 正常列印 y.score
           

使用@property

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#屬性函數(property):
#将類方法轉換為隻讀屬性
#重新實作一個屬性的setter和getter方法

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

s = Student()
s.score = 60
print('s.score =', s.score)
# ValueError: score must between 0 ~ 100!
s.score = 9999
           

多重繼承

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# MixIn
#“混入”:Mix in。
# 在設計類的繼承關系時,通常,主線都是單一繼承下來的。但是,如果需要“混入”額外的功能,
# 通過多重繼承就可以實作。

# 動物類
class Animal(object):
    pass

# 又将動物分為哺乳動物和鳥類:
class Mammal(Animal):
    pass
class Bird(Animal):
    pass

# 上面兩類又可以細分各種動物類:
class Dog(Mammal):
    pass
class Bat(Mammal):
    pass
class Parrot(Bird):
    pass
class Ostrich(Bird):
    pass

# 上面的各種動物也可以按 地上跑的 和 天上飛的分類:
class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')

# 細分的動物可以繼承多個類:
class Dog(Mammal, Runnable):
    pass
class Bat(Mammal, Flyable):
    pass
           

枚舉類

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Enum可以把一組相關常量定義在一個class中,且class不可變,而且成員可以直接比較。可以枚舉出該類。

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

print('--------------------------------------')

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

day1 = Weekday.Mon

print('day1 =', day1)
print('Weekday.Tue =', Weekday.Tue)
print('Weekday[\'Tue\'] =', Weekday['Tue'])
print('Weekday.Tue.value =', Weekday.Tue.value)
print('day1 == Weekday.Mon ?', day1 == Weekday.Mon)
print('day1 == Weekday.Tue ?', day1 == Weekday.Tue)
print('day1 == Weekday(1) ?', day1 == Weekday(1))

print('--------------------------------------')

for name, member in Weekday.__members__.items():
    print(name, '=>', member, ',', member.value)
           

檔案讀寫

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 檔案讀取

# 檔案讀寫時都有可能産生IOError,一旦出錯,後面的f.close()就不會調用。
# 是以,為了保證無論是否出錯都能正确地關閉檔案,我們可以使用try ... finally來實作:
try:
    f = open('/Users/xxx/Desktop/sqltest.txt', 'r')  #r 為read
    print(f.read())
finally:
    if f:
        f.close()

# 這麼寫太繁瑣,Python引入了with語句來自動幫我們調用close()方法
with open('/Users/xxx/Desktop/sqltest.txt', 'r') as f:
    print(f.read())

# 如果檔案很小,read()一次性讀取最友善;如果不能确定檔案大小,反複調用read(size)比較保險,
# 但一次次調用繁瑣,于是可以調用下面兩種:
# readline()可以每次讀取一行内容,readlines()一次讀取所有内容并按行傳回list。
with open('/Users/xxx/Desktop/sqltest.txt', 'r') as f:
    print(f.read())
    for line in f.readlines():
        print(line.strip()) # 把末尾的'\n'删掉

# 二進制檔案讀取
f = open('/Users/michael/test.jpg', 'rb') #讀取二進制檔案(read bytes),比如圖檔、視訊等,用 rb 模式打開
print(f.read())

# 字元編碼
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk') #gbk編碼
print(f.read())

# 寫檔案
# case1:
f = open('/Users/michael/test.txt', 'w')
f.write('hello world!')
f.close
# case2:
with open('/Users/michael/test.txt', 'w') as f:
    f.write('hello world!')
           

StringIO_BytesIO

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# StringIO 在記憶體中讀寫str。
from io import StringIO
f = StringIO()
f.write('hello')
f.write(' ')
f.write('world!')
print(f.getvalue())

print('----------------------')

from io import StringIO
f = StringIO('Hello!\nHi!\nGoodbye!')
while True:
    s = f.readline()
    if s == '':
        break
    print(s.strip())
print('----------------------')

# StringIO操作的隻能是str,如果要操作二進制資料,就需要使用BytesIO。
from io import BytesIO
f = BytesIO()
f.write('中文'.encode('utf-8'))
print(f.getvalue())

from io import BytesIO
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
print(f.read())
           

操作檔案和目錄

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 在作業系統中對檔案的操作可以直接使用cp、dir等系統指令完成。
# Python中内置的 os 子產品也可以直接調用作業系統提供的接口函數。

import os
print(os.name) # 輸出系統結果, nt 代表windows系統, posix 代表linux/unix/macos
#print(os.uname())  # 輸出詳細的系統資訊(隻針對于 非 windows系統,windows會報錯)

# 輸出環境變量
import os
print(os.environ)  # 輸出環境變量

# 輸出指定環境變量的值
print(os.environ.get('PATH'))  # 輸出環境變量的路徑


# 檢視目前目錄的絕對路徑
print(os.path.abspath('.'))

# 在某個目錄下建立一個新目錄,首先把新目錄的完整路徑表示出來
print(os.path.join('/Users/xxx', '111'))
# windows下會顯示 /Users/xxx\111 ,但不影響生成目錄
# linux下會顯示  /Users/xxx/111

# 在某個路徑下建立一個新目錄
print(os.mkdir('/Users/xxx/111'))

# 删除該新增目錄
print(os.rmdir('/Users/xxx/111'))

# 目錄拆分
print(os.path.split('/Users/xxx/111'))
# 這樣可以把一個路徑拆分為兩部分,後一部分總是最後級别的目錄或檔案名

# os.path.splitext() 可以得到檔案的擴充名
print(os.path.splitext('/Users/xxx/111/test.txt'))

#### 注: os.path.join()、os.path.split()、os.path.splitext(),合并、拆分不需要這些檔案、
#### 檔案夾真是存在,他們隻對字元串進行操作。


# 檔案重命名
print(os.rename('/Users/xxx/111/test.txt', '/Users/xxx/111/te.py'))

# 删除檔案
print(os.remove('/Users/xxx/111/123.txt'))

# 建立檔案可以結合前面的檔案讀寫
with open('/users/xxx/111/123.txt', 'w') as f:
    f.write('hello world')

# 複制檔案
# 雖然 os 子產品沒有複制檔案的函數,但 shutil子產品中有,它可以看作是 os子產品的補充子產品


# 過濾檔案
# 輸出目前目錄下的檔案夾
import os
L = [x for x in os.listdir('.') if os.path.isdir(x)]
print(L)

# 輸出目前目錄下的py檔案
M = [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
print(M)
           

代碼實作dir-l功能

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from datetime import datetime
import os

pwd = os.path.abspath('.')

print('      Size    Last Modified  Name')
print('------------------------------------------------------------')

for f in os.listdir(pwd):
    fsize = os.path.getsize(f)
    mtime = datetime.fromtimestamp(os.path.getmtime(f)).strftime('%Y-%m-%d %H:%M')
    flag = '/' if os.path.isdir(f) else ''
    print('%10d  %s  %s%s' % (fsize, mtime, f, flag))
           

序列化_pickle子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 序列化:
# Python中叫pickling。把變量從記憶體中變成可存儲或傳輸的過程稱之為序列化,如把記憶體中的字元字元串以二進制格式存儲在硬碟上。

# 反序列化:
# Python中叫unpickling。反過來,把變量存儲起來的内容從序列化的對象重新讀到記憶體裡稱之為反序列化。

# 将一個對象序列化并寫入檔案:
# 1.将對象序列化:
import pickle
d = dict(name='Bob', age=20, score=80)
print(pickle.dumps(d))  # pickle.dumps() 方法将任意對象序列化成一個bytes

print('---------------------------')

# 2.寫入檔案(建立檔案dump.txt)
f = open('dump.txt', 'wb')
pickle.dump(d, f)
f.close()

print('---------------------------')

# 将對象反序列化,從磁盤讀取到記憶體
f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
print(d)
           

JSON

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 不同的程式設計語言之間傳遞對象,就必須把對象序列化為标準格式,比如XML,
# 但更好的方法是序列化為JSON。
# JSON不僅是标準格式,并且比XML更快,而且可以直接在Web頁面中讀取,非常友善。

# json标準規定編碼為 utf-8

# dumps()方法傳回一個str,内容是标準json。類似dump()方法可以直接把json寫入一個file-likes Object
import json
import pickle
d = dict(name='Bob', age=20, score=88)
print(json.dumps(d))

# 把json反序列化為Python對象,用loads()或者load()
# 前者把json字元串反序列化,後者從file-like Object中讀取字元串并反序列化
json_str = '{"age":20, "score":88, "name":"Bob"}'
print(json.loads(json_str))
           

多程序

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# unix/linux作業系統提供了一個 fork() 函數,
# 通過系統調用建立一個與原來程序幾乎完全相同的程序,這個新産生的程序稱為子程序。
# 隻需要調用getppid()就可以拿到父程序的ID。
# 子程序用getpid()檢視PID。

import os
print('Process (%s) start...' % os.getpid())

# 以下在linux/unix/macos下執行,因為windows沒有fork調用
'''
if pid == 0:
    print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
    print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
'''

# multiprocessing:python多程序子產品
from multiprocessing import Process
import os

# 子程序執行的代碼
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('child process will start.')
    p.start()
    p.join()
    print('child process end.')
# join()方法 用于将序列中的元素以指定的字元連接配接生成一個新的字元串。


# Pool:程序池。用于要啟用大量子程序時,批量建立子程序。
from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random()*3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end-start)))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(long_time_task, args=(i, ))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')


# 子程序
# subprocess子產品可以讓我們非常友善地啟動一個子程序,然後控制其輸入和輸出。
import subprocess

print('$ nslookup www.python.org') # 列印出内容,為顯示好看,無實際作用。
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)


import subprocess

print('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('gbk'))
print('Exit code:', p.returncode)


# 程序間通信
# Process之間肯定是需要通信的,作業系統提供了很多機制來實作程序間的通信。
# Python的multiprocessing子產品包裝了底層的機制,提供了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()
           

多線程

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 多任務可以由多程序完成,也可由一個程序的多個線程完成。
# Python标準庫提供 _thread(低級子產品) 和 threading(進階子產品) 兩個子產品進行多線程處理。

import time, threading

# 新線程執行的代碼:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n<5:
        n = n+1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)

print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)


# Lock
# 多線程和多程序最大的不同在于,多程序中,同一個變量,各自有一份拷貝存在于每個程序中,
# 互不影響,而多線程中,所有變量都由所有線程共享,是以,任何一個變量都可以被任何一個線程修改,
# 是以,線程之間共享資料最大的危險在于多個線程同時改一個變量,把内容給改亂了。
import time, threading

balance = 0
lock = threading.Lock()

def change_it(n):
    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)

# 注:
# 程序(Process) 比 線程(thread) 更穩定,windows下建立程序開銷巨大。
           

正規表達式

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 正規表達式
# 正規表達式(Regular Expression,一般簡寫為RegEx或者RegExp),
# 也譯為正規表示法、正常表示法,在計算機科學中,是指一個用來描述
# 或者比對一系列符合某個句法規則的字元串的單個字元串。

# 用法細節見筆記“正規表達式”

# re子產品
import re
print(re.match(r'^\d{3}\-\d{3,8}$', '010-12345'))
print(re.match(r'^\d{3}\-\d{3,8}$', '010 12345'))
# match()方法判斷是否比對成功,成功傳回Match對象,失敗傳回None

print(r'a b  c'.split(' '))
print(re.split(r'\s+', 'a b  c'))
           

datetime子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from datetime import datetime  # 導入datetime子產品中的datetime類
now = datetime.now()
print(now)

print(type(now))

# 擷取指定日期和時間
from datetime import datetime
dt = datetime(2015, 4, 19, 12, 20, 00)
print(dt)

# 1970年1月1日 00:00:00 UTC+00:00時區的時刻稱為 epoch time
# 目前時間就是相對于epoch time的秒數,稱為timestamp
# 示例:
# epochtime = 1970-1-1 00:00:00 UTC+0:00
# timestamp = 1970-1-1 09:00:00 UTC+9:00

# datetime 轉換為 timestamp
# 使用timestamp()函數
from datetime import datetime
dt = datetime(2015, 4, 19, 12, 20, 00)
print(dt.timestamp())

# timestamp 轉換為 datetime
# 使用 datetime 提供的 fromtimestamp()方法
from datetime import datetime
t = 1429417200.0
print(datetime.fromtimestamp(t))

# str轉換為datetime
# 很多時候使用者輸入的是字元串,得把str轉換datetime。
# 通過datetime.strptime()實作,需要一個日期和時間的格式化字元串
from datetime import datetime
cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
print(cday)

# datetime轉換為str
# 如果已經有了datetime對象,将它格式化為字元串現實給使用者,就需要轉換str
# 用 strftime() 實作。
from datetime import datetime
now = datetime.now()
print(now.strftime('%a, %b %d %H:%M'))

# 小結
# datetime表示的時間需要時區資訊才能确定一個特定的時間,否則隻能視為本地時間。
# 如果要存儲datetime,最佳方法是将其轉換為timestamp再存儲,因為timestamp的值與時區完全無關。
           

collections子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# collections是Python内建的一個集合子產品,提供了許多有用的集合類。

# namedtuple()函數用來建立自定義 tuple對象,且規定 tuple元素的個數。
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])  # 規定tuple元素為 x, y
p = Point(1, 2)
print(p.x)
print(p.y)


# deque
# 實作高效插入與删除操作的雙向清單,适合于隊列和棧。
# (list是線性存儲,按索引通路元素很快,但插入和删除元素就很慢)
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x')
q.appendleft('y')
print(q)

# defaultdict
# 使用dict時,如果引用的Key不存在,就會抛出KeyError。
# 使用defaultdict如果Key不存在會傳回一個預設值。
from collections import defaultdict
dd = defaultdict(lambda : 'N/A')
dd['key1'] = 'abc'
print(dd['key1'])  # key1存在,正常輸出
print(dd['key2'])  # key2不存在,傳回預設值 N/A

# OrderedDict
# 在對dict做疊代時,key是無序的。OrderedDict會保持按插入的順序輸出。
# 即 FIFO(先進先出),當容量超出限制時,先删除最早添加的Key。
from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)])
print(d)  # 無序輸出
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od)  # 順序輸出,先入先出

# Counter
# Counter是一個簡單的計數器,統計字元出現的次數。
from collections import Counter
c = Counter()
for ch in 'programming':
    c[ch] = c[ch] + 1
print(c)
           

base64子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import base64
print(base64.b64encode(b'testtest'))  # 編碼
print(base64.b64decode(b'dGVzdHRlc3Q='))  # 解碼
           

hashlib子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 摘要算法:
# 摘要算法又稱雜湊演算法、雜湊演算法。它通過一個函數,
# 把任意長度的資料轉換為一個長度固定的資料串(通常用16進制的字元串表示)

# hashlib提供常見的摘要算法,如:md5,sha1.
# case1(md5):
import hashlib
md5 = hashlib.md5()
md5.update('just test'.encode('utf-8'))
print(md5.hexdigest())

# case2(sha1):
import hashlib
sha1 = hashlib.sha1()
sha1.update('just test'.encode('utf-8'))
print(sha1.hexdigest())

# 小結
# 摘要算法在很多地方都有廣泛的應用。要注意 摘要算法 不是 加密算法,
# 不能用于加密(因為無法通過摘要反推明文),隻能用于防篡改。
# 但是它的單向計算特性決定了可以在不存儲明文密碼的情況下驗證使用者密碼。
           

hmac子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# hmac子產品實作标準的Hmac算法,将原始消息message加入随機的key,雜湊演算法生成加salt的密文。
import hmac
message = b'Hello world!'
key = b'secret'
h = hmac.new(key, message, digestmod='MD5')
print(h.hexdigest())  # 加鹽的md5加密

import hashlib
message = hashlib.md5()
message.update('Hello world!'.encode('utf-8'))
print(message.hexdigest())  # 未加鹽,密文結果不同

# 小結
# Python内置的hmac子產品實作了标準的Hmac算法,它利用一個key對message計算“雜湊”後
# 的hash,# 使用hmac算法比标準hash算法更安全,因為針對相同的message,不同的key會
# 産生不同的hash。
           

itertools子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# itertools子產品提供了用于操作疊代對象的函數。
# count()會建立一個無限的疊代器,将資料一直列印出來,必須按ctrl+c退出。
import itertools
natuals = itertools.count(1)
for n in natuals:
    print(n)


# cycle()會将一個序列無限重複下去,同樣停不下來。
import itertools
cs = itertools.cycle('ABC')
for c in cs:
    print(c)


# repeat()可以将一個元素無限重複下去,不過其第二個參數可以控制重複次數
import itertools
ns = itertools.repeat('A', 5)
for n in ns:
    print(n)


# 無限序列雖然可以無限疊代下去,但可以用 takewhile()函數 通過條件判斷來截取有限序列
import itertools
natuals = itertools.count(1)
ns = itertools.takewhile(lambda x: x<=10, natuals)  # 限定隻取10及以内的數
print(list(ns))  # 輸出取的數的清單


# chain()
# 把一組疊代對象串聯起來,形成一個更大的疊代器
import itertools
for c in itertools.chain('ABC','XYZ'):
    print(c)  # 輸出 A B C X Y Z


# groupby()
# 把疊代器中相鄰的重複元素跳出來放在一起
import itertools
for key, group in itertools.groupby('AAABBBCCCAAA'):
    print(key, list(group))
# 輸出:
# A ['A', 'A', 'A']
# B ['B', 'B', 'B']
# C ['C', 'C', 'C']
# A ['A', 'A', 'A']

# 上面字元必須大小寫統一,如果大小寫混雜,可以使用upper(),
# 其會将小寫字元轉變為大寫字元排列,與lower()功能相反。
import itertools
for key, group in itertools.groupby('AAaBbBcCCaAA', lambda x: x.upper()):
    print(key, list(group))
           

contextlib子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Query(object):

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('Begin')
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type:
            print('Error')
        else:
            print('End')

    def query(self):
        print('Query info about %s...' % self.name)

with Query('Bob') as q:
    q.query()
           

urllib子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# urllib提供了一系列用于操作url的功能

# get
from urllib import request

with request.urlopen('https://api.douban.com/v2/book/2129650') as f:
    data = f.read()
    print('Status:', f.status, f.reason)
    for k, v, in f.getheaders():
        print('%s: %s' % (k, v))
    print('Data:', data.decode('utf-8'))
# 可以擷取到http相應頭和json資料
'''
Status: 200 OK
Date: Wed, 06 Dec 2017 03:51:05 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 2058
Connection: close
Vary: Accept-Encoding
X-Ratelimit-Remaining2: 98
X-Ratelimit-Limit2: 100
Expires: Sun, 1 Jan 2006 01:00:00 GMT
Pragma: no-cache
Cache-Control: must-revalidate, no-cache, private
Set-Cookie: bid=ernch-JwbnQ; Expires=Thu, 06-Dec-18 03:51:05 GMT; Domain=.douban.com; Path=/
X-DOUBAN-NEWBID: ernch-JwbnQ
X-DAE-Node: sindar1b
X-DAE-App: book
Server: dae
Data: {"rating":{"max":10,"numRaters":16,"average":"7.4","min":0},"subtitle":"","author":["廖雪峰"],"pubdate":"2007","tags":[{"count":21,"name":"spring","title":"spring"},{"count":13,"name":"Java","title":"Java"},{"count":6,"name":"javaee","title":"javaee"},{"count":5,"name":"j2ee","title":"j2ee"},{"count":4,"name":"計算機","title":"計算機"},{"count":4,"name":"程式設計","title":"程式設計"},{"count":3,"name":"藏書","title":"藏書"},{"count":3,"name":"POJO","title":"POJO"}],"origin_title":"","image":"https://img3.doubanio.com\/mpic\/s2552283.jpg","binding":"平裝","translator":[],"catalog":"","pages":"509","images":{"small":"https://img3.doubanio.com\/spic\/s2552283.jpg","large":"https://img3.doubanio.com\/lpic\/s2552283.jpg","medium":"https://img3.doubanio.com\/mpic\/s2552283.jpg"},"alt":"https:\/\/book.douban.com\/subject\/2129650\/","id":"2129650","publisher":"電子工業出版社","isbn10":"7121042622","isbn13":"9787121042621","title":"Spring 2.0核心技術與最佳實踐","url":"https:\/\/api.douban.com\/v2\/book\/2129650","alt_title":"","author_intro":"","summary":"本書注重實踐而又深入理論,由淺入深且詳細介紹了Spring 2.0架構的幾乎全部的内容,并重點突出2.0版本的新特性。本書将為讀者展示如何應用Spring 2.0架構建立靈活高效的JavaEE應用,并提供了一個真正可直接部署的完整的Web應用程式——Live線上書店(http:\/\/www.livebookstore.net)。\n在介紹Spring架構的同時,本書還介紹了與Spring相關的大量第三方架構,涉及領域全面,實用性強。本書另一大特色是實用性強,易于上手,以實際項目為出發點,介紹項目開發中應遵循的最佳開發模式。\n本書還介紹了大量實踐性極強的例子,并給出了完整的配置步驟,幾乎覆寫了Spring 2.0版本的新特性。\n本書适合有一定Java基礎的讀者,對JavaEE開發人員特别有幫助。本書既可以作為Spring 2.0的學習指南,也可以作為實際項目開發的參考手冊。","price":"59.8"}
'''

# 模拟浏覽器發送請求,需要往 Request對象 添加http頭資訊
from urllib import request

req = request.Request('http://www.douban.com/')
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0')
with request.urlopen(req) as f:
    print('Status:', f.status, f.reason)
    for k, v in f.getheaders():
        print('%s: %s' % (k, v))
    print('Data:', f.read().decode('utf-8'))


# post
# 發送一個post請求,隻需把data以bytes形式傳入。
# 模拟微網誌登陸
from urllib import request,parse

print('Login to weibo.cn...')
email = input('Email:')
passwd = input('Password:')
login_data = parse.urlencode([
    ('username', email),
    ('password', passwd),
    ('entry', 'mweibo'),
    ('client_id', ''),
    ('savestate', '1'),
    ('ec', ''),
    ('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')
])

req = request.Request('https://passport.weibo.cn/sso/login')
req.add_header('Origin', 'https://passport.weibo.cn')
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0')
req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')

with request.urlopen(req, data = login_data.encode('utf-8')) as f:
    print('Status:', f.status, f.reason)
    for k, v in f.getheaders():
        print('%s: %s' % (k, v))
    print('Data:', f.read().decode('utf-8'))


# handler
# 通過一個代理 proxy 去通路網站,需要用 ProxyHandler 來處理。
from urllib import request
proxy_handler = urllib.request.ProxyHandler(['http': 'http://www.example.com:1080/'])
proxy_auth_handler = urllib.request.ProxyBasicAuthHandler()
proxy_auth_handler.add_password('realm', 'host', 'username', 'password')
opener = urllib.request.build_opener(proxy_handler, proxy_auth_handler)
with opener.open('http://www.example.com/login.html') as f:
    pass


# 小結
# urllib提供的功能就是利用程式去執行各種HTTP請求。如果要模拟浏覽器完成特定功能,
# 需要把請求僞裝成浏覽器。僞裝的方法是先監控浏覽器發出的請求,再根據浏覽器的請求
# 頭來僞裝,User-Agent頭就是用來辨別浏覽器的。
           

XML解析

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# python解析xml,準備好三個事件函數 start_element, end_element, char_data 就可以完成。
# 示例: <a href="/">python</a>
# 會産生3個事件:
# 1.start_element事件,讀取:<a href="/">;
# 2.char_data事件,讀取:python;
# 3.end_element事件,讀取:</a>。

from xml.parsers.expat import ParserCreate

class DefaultSaxHandler(object):
    def start_element(self, name, attrs):
        print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))

    def end_element(self, name):
        print('sax:end_element: %s' % name)

    def char_data(self, text):
        print('sax:char_data: %s' % text)

xml = r'''<?xml version="1.0"?>
<ol>
    <li><a href="/python">Python</a></li>
    <li><a href="/ruby">Ruby</a></li>
</ol>
'''

handler = DefaultSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.start_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(xml)
           

HTMLParser子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print('<%s>' % tag)

    def handle_endtag(self, tag):
        print('</%s>' % tag)

    def handle_startendtag(self, tag, attrs):
        print('<%s/>' % tag)

    def handle_data(self, data):
        print(data)

    def handle_comment(self, data):
        print('<!--', data, '-->')

    def handle_entityref(self, name):
        print('&%s;' % name)

    def handle_charref(self, name):
        print('&#%s;' % name)

parser = MyHTMLParser()
parser.feed('''<html>
<head></head>
<body>
<!-- test html parser -->
    <p>Some <a href=\"#\">html</a> HTML&nbsp;tutorial...<br>END</p>
</body></html>''')

# feed()方法可以多次調用,也就是不一定一次把整個HTML字元串都塞進去,
# 可以一部分一部分塞進去。
           

pillow子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PIL import Image
im = Image.open('101010.jpg')
w, h = im.size
print('Original image size: %sx%s' % (w, h))  # 輸出原始圖像大小
im.thumbnail((w//2, h//2))
print('Resize image to: %sx%s' % (w//2, h//2))  # 輸出的圖檔寬高縮小一半
im.save('thumbnail.jpg', 'jpeg')
           

requests子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# requests
# python内置的urllib子產品用于通路網絡資源,但用起來麻煩,缺少很多實用的進階功能。
# requests處理url更友善。

#get
import requests
r = requests.get('https://www.douban.com/')
print(r.status_code)  # 輸出頁面狀态碼 200
print(r.text)  # 輸出内容

# 對于帶參數的url,傳入一個dict作為params參數
import requests
r = requests.get('https://www.douban.com/search', params={'q':'python', 'cat':'1001'})
print(r.url)
# 此代碼實際請求的url是 https://www.douban.com/search?q=python&cat=1001


# requests自動檢測編碼,可以使用 encoding屬性檢視:
print(r.encoding)

# content 擷取響應内容(不管是文本還是二進制内容均以二進制擷取)
print(r.content)

# requests友善在于特定類型的響應,如 JSON,可直接擷取
r = requests.get('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=json')
print(r.json())

# 傳入 HTTP Header 時,可加入 dict 添加headers 參數
r = requests.get('https://www.douban.com/', headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit'})
print(r.text)   # 以文本内容擷取

# post
import requests
r = requests.post('https://accounts.douban.com/login',data={'form_email': '[email protected]', 'form_password': '123456'})
print(r.status_code)
print(r.text)

# requests 預設使用 application/x-www-form-urlencoded 對POST資料編碼
# 如果要傳遞json資料,可直接傳入json參數
params = {'key':'value'}
r = requests.post(url, json=params)  # 替換url和params資料

# requests 還可進行更複雜的編碼格式(如:二進制),檔案上傳
import requests
upload_files = {'file':open('test.xls', 'rb')}
r = requests.post(url, file=upload_files)

# 同理,把 requests.get()、requests.post() 替換為 put、delete,也可進行該方式請求。

# requests 擷取響應資訊
print(r.headers)  # 擷取http頭資訊
print(r.status_code)  # 擷取狀态碼
print(r.text)  # 擷取頁面内容

# cookie
# requests 對 cookie 做了特殊處理,是我們不必解析cookie就可以輕松擷取cookie
print(r.cookies)
# 如果要在請求中傳入cookie,和post一樣,使用dict傳入cookie參數
cookie_params = {'token':12345, 'status':'abc', 'session':'DWEHDWLLKSNDQ2SD2H4'}

# 指定逾時時間
r = requests.get('url', timeout=2.5) # 2.5秒逾時
           

chardet子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# chardet 用于監測編碼
import chardet
print(chardet.detect(b'hello world!'))
# 結果:{'encoding': 'ascii', 'confidence': 1.0, 'language': ''}
# confidence 表示機率,1.0 代表100%


# 檢測GBK編碼的中文:
data = '離離原上草,一歲一枯榮'.encode('gbk')
print(chardet.detect(data))
# 結果:{'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}

# 監測utf-8編碼的中文:
data = '離離原上草,一歲一枯榮'.encode('utf-8')
print(chardet.detect(data))
# 結果:{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

# 檢測日文
data = '最新の主要ニュース'.encode('euc-jp')
print(chardet.detect(data))
# 結果:{'encoding': 'EUC-JP', 'confidence': 0.99, 'language': 'Japanese'}
           

psutil子產品

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# psutil:process and system utilities, 程序和系統實用工具。
# 自動化運維使用普遍。

import psutil
print(psutil.cpu_count())  # 擷取CPU邏輯數量
# 結果:4
print(psutil.cpu_count(logical=False))  # CPU實體核心
# 結果:2
# 因為我的電腦是雙核四線程的。

# 統計cpu的使用者/系統/空閑時間
print(psutil.cpu_times())


# 實作類似 top 指令的cpu使用率,每秒重新整理一次,累計10次:
'''for x in range(10):
    print(psutil.cpu_percent(interval=1, percpu=True))
'''
print('------------------------------')

# 擷取記憶體資訊
print(psutil.virtual_memory())  # 擷取實體記憶體資訊
# svmem(total=8467226624, available=4421910528, percent=47.8, used=4045316096, free=4421910528)
# 總記憶體8G,可用4G多,已用占比47.8%,已使用4G少一點,空餘4G多=前面的可用數額
print(psutil.swap_memory())  # 擷取交換分區資訊
# sswap(total=13087117312, used=5167869952, free=7919247360, percent=39.5, sin=0, sout=0)

print('------------------------------')

# 擷取磁盤資訊
print(psutil.disk_partitions())  # 擷取磁盤分區資訊
print(psutil.disk_usage('/'))  # 磁盤使用情況
print(psutil.disk_io_counters())  # 磁盤IO,輸入輸出吞吐量

print('------------------------------')

# 擷取網絡資訊
print(psutil.net_io_counters())  # 擷取網絡讀寫 位元組/包 的個數

print('------------------------------')

# 擷取網絡接口
print(psutil.net_if_addrs())

print('------------------------------')

# 擷取接口狀态
print(psutil.net_if_stats())

print('------------------------------')

# 擷取目前網絡連接配接資訊
print(psutil.net_connections())

print('------------------------------')

# 擷取程序資訊
'''
>>> psutil.pids() # 所有程序ID
[3865, 3864, 3863, 3856, 3855, 3853, 3776, ..., 45, 44, 1, 0]
>>> p = psutil.Process(3776) # 擷取指定程序ID=3776,其實就是目前Python互動環境
>>> p.name() # 程序名稱
'python3.6'
>>> p.exe() # 程序exe路徑
'/Users/michael/anaconda3/bin/python3.6'
>>> p.cwd() # 程序工作目錄
'/Users/michael'
>>> p.cmdline() # 程序啟動的指令行
['python3']
>>> p.ppid() # 父程序ID
3765
>>> p.parent() # 父程序
<psutil.Process(pid=3765, name='bash') at 4503144040>
>>> p.children() # 子程序清單
[]
>>> p.status() # 程序狀态
'running'
>>> p.username() # 程序使用者名
'michael'
>>> p.create_time() # 程序建立時間
1511052731.120333
>>> p.terminal() # 程序終端
'/dev/ttys002'
>>> p.cpu_times() # 程序使用的CPU時間
pcputimes(user=0.081150144, system=0.053269812, children_user=0.0, children_system=0.0)
>>> p.memory_info() # 程序使用的記憶體
pmem(rss=8310784, vms=2481725440, pfaults=3207, pageins=18)
>>> p.open_files() # 程序打開的檔案
[]
>>> p.connections() # 程序相關網絡連接配接
[]
>>> p.num_threads() # 程序的線程數量
1
>>> p.threads() # 所有線程資訊
[pthread(id=1, user_time=0.090318, system_time=0.062736)]
>>> p.environ() # 程序環境變量
{'SHELL': '/bin/bash', 'PATH': '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:...', 'PWD': '/Users/michael', 'LANG': 'zh_CN.UTF-8', ...}
>>> p.terminate() # 結束程序
Terminated: 15 <-- 自己把自己結束了
'''

print('------------------------------')

# 模拟ps指令效果
print(psutil.test())
           

TCP程式設計

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# TCP 是可靠的,面向連接配接的協定。

# Socket 是網絡程式設計的一個抽象概念,意為“孔、插座”,用于兩個用戶端之間的網絡連接配接,
# 打開一個socket需要指定目标機器的IP和端口。


### 用戶端
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET指定使用ipv4,如果是ipv6使用AF_INET6
s.connect(('www.sina.com.cn', 80))  # socket連結 參數是一個 tuple,包含位址、端口号

# 向新浪伺服器發送請求
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

# 接收伺服器傳回資料
buffer = []
while True:
    d = s.recv(1024)  # recv(max)方法,一次最多接收指定的位元組數
    if d:
        buffer.append(d)
    else:
        break
data = b''.join(buffer)
s.close()  # close()方法關閉Socket,網絡通信結束。

# 輸出http頭資訊
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))

# 把接收的内容資料寫入檔案
with open('sina.html', 'wb') as f:
    f.write(html)


### 伺服器
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 綁定監聽端口
s.bind(('127.0.0.1', 9999))

# 調用listen()開始監聽,并設定最大等待連接配接數量,不是連接配接的用戶端數量
s.listen(5)
print('Waiting for connection...')

# 伺服器通過永久循環來接受來自用戶端的連接配接
while True:
    # 接受一個新連接配接:
    sock, addr = s.accept()
    # 建立新線程來處理TCP連接配接:
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

# 每次連接配接都要建立新的線程或程序
def tcplink(sock, addr):
    print('Accept new connection from %s:%s...' % addr)
    sock.send(b'Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode('utf-8') == 'exit':
            break
        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
    sock.close()
    print('Connection from %s:%s closed.' % addr)


# 用戶端與上面伺服器通信
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接配接:
s.connect(('127.0.0.1', 9999))
# 接收歡迎消息:
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
    # 發送資料:
    s.send(data)
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()
           

UDP程式設計

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# UDP 是面向無連接配接的協定。

### 服務端
import socket
s = socket.socket(socket.AF_INET, sock.SOCK_DGRAM)  # SOCK_DGRAM 指定Socket類型為UDP
# 綁定端口
s.bind(('127.0.0.1'. 9999))

# UDP與TCP相比,不需要調用listen(),直接接收用戶端資料
print('Bind UDP on 9999..')
while True:
    # 接收資料
    data, addr = s.recvfrom(1024)  # recvfrom()傳回資料和用戶端位址、端口
    print('Received from %s: %s.' % addr)
    s.sendto(b'Hello, %s!' % data, addr)
    # 伺服器收到後通過 sendto() 把資料通過UDP發送給用戶端

### 用戶端
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
    # 發送資料
    s.sendto(data, ('127.0.0.1', 9999))
    # 接收資料
    print(s.recv(1024).decode('utf-8'))
s.close()
           

SMTP發送郵件

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Python的 smtplib 和 email 兩個子產品對郵件支援。
# email 負責構造郵件,smtplib 負責發送郵件

from email.mime.text import MIMEText
msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')

# 輸入郵箱位址和密碼
from_addr = input('From:')
password = input('Password:')
# 輸入收件人位址
to_addr = input('To:')
# 輸入SMTP伺服器位址
smtp_server = input('SMTP server:')

import smtplib
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)  # 列印出和SMTP伺服器互動的所有資訊
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

# 完整郵件:
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
import smtplib

def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))

from_addr = input('From:')
password = input('Password:')
to_addr = input('To:')
smtp_server = input('SMTP server:')

msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
msg['From'] = _format_addr('Python愛好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理者 <%s>' % to_addr)
msg['Subject'] = Header('來自SMTP的問候。。。', 'utf-8').encode()

server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()
           

POP3收取郵件

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Python内置的poplib子產品,用于實作郵件接收。

# 收取郵件分為兩步:
# 1.用 poplib 把郵件原始文本下載下傳到本地;
# 2.用 email 解析原始文本,還原郵件對象。

from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
import poplib

# 解析郵件
# 列印出 message 對象的層次結構
# indent用于縮進顯示
def print_info(msg, indent=0):
    if indent == 0:
        for header in ['From', 'To', 'Subject']:
            value = msg.get(header, '')
            if value:
                if header=='Subject':
                    value = decode_str(value)
                else:
                    hdr, addr = parseaddr(value)
                    name = decode_str(hdr)
                    value = u'%s<%s>' % (name, addr)
            print('%s%s: %s' % (' ' * indent, header, value))
    if(msg.is_multipart()):
        parts = msg.get_payload()
        for n, part in enumerate(parts):
            print('%spart %s' % (' ' * indent, n))
            print('%s----------------' % (' ' * indent))
            print_info(part, indent + 1)
    else:
        content_type = msg.get_content_type()
        if content_type=='text/plain' or content_type=='text/html':
            content = msg.get_payload(decode=True)
            charset = guess_charset(msg)
            if charset:
                content = content.decode(charset)
            print('%sText: %s' % (' ' * indent, content + '...'))
        else:
            print('%sAttachment: %s' % (' ' * indent, content_type))
# 郵件中的名字都是編碼後的str,要正常顯示,必須解碼decode
def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value
# 郵件内容也要檢測編碼
def guess_charset(msg):
    charset = msg.get_charset()
    if charset is None:
        content_type = msg.get('Content-Type', '').lower()
        pos = content_type.find('charset=')
        if pos >= 0:
            charset = content_type[pos + 8:].strip()
    return charset

# 輸入郵件位址、密碼和POP3伺服器位址
email = input('Email:')
password = input('Password:')
pop3_server = input('POP3 server:')

#連接配接到POP3伺服器
server = poplib.POP3(pop3_server)
# 打開調試資訊
server.set_debuglevel(1)
# 列印POP3伺服器的歡迎文字
print(server.getwelcome().decode('utf-8'))

# 身份認證
server.user(email)
server.pass_(password)

# stat()傳回郵件數量和占用空間
print('Messages: %s. Size: %s' % server.stat())
# list()傳回所有郵件編号
resp, mails, octets = server.list()
# 檢視傳回清單
print(mails)

# 擷取最新一封郵件
index = len(mails)
resp, lines, octets = server.retr(index)

# lines存儲了郵件的原始文本的每一行,可以獲得整個郵件原始文本
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 稍後解析出郵件
msg = Parser().parsestr(msg_content)
print_info(msg)
# 關閉連接配接
server.quit()
           

使用SQLite

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# SQLite是一種嵌入式資料庫,資料庫就是一個檔案

# 導入sqlite資料庫
import sqlite3
# 連接配接test.db資料庫,如果不存在會自動建立
conn = sqlite3.connect('test.db')
# 建立Cursor,操作資料庫通過Cursor執行SQL語句
cursor = conn.cursor()
# 執行建立表的指令
cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
# 插入一條記錄
cursor.execute('insert into user (id, name) values (\'1\', \'Michael\')')
# 通過rowcount獲得插入的行數
print(cursor.rowcount)
# 關閉Cursor
cursor.close()
# 送出事務
conn.commit()
# 關閉連接配接
conn.close()

# 查詢
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
# 執行查詢語句
cursor.execute('select * from user where id?', ('1',))
# 獲得查詢結果集
values = cursor.fetchall()
print(values)
cursor.close()
conn.close()
           

使用MySQL

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 安裝mysql驅動
# pip3 install mysql-connector-python --allow-external mysql-connector-python
# pip3 install mysql-connector

import mysql.connector
# 連接配接資料庫
conn = mysql.connector.connect(use='root', password='root', database='test')
cursor = conn.cursor()
# 建立user表
cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
# 插入一行記錄,注意mysql的占位符是 %s
cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael'])
print(cursor.rowcount)
# 送出事務
conn.commit()
cursor.close()
# 查詢
cursor = conn.cursor()
cursor.execute('select * from user where id = %s', ('1',))
values = cursor.fetchall()
print(values)
# 關閉cursor和connection
cursor.close()
conn.close()
           

使用SQLAIchemy

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# SQLAlchemy 是python 操作資料庫的一個庫。
# pip3 install sqlalchemy

from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 建立對象的基類
Base = declarative_base()
# 定義user對象
class User(Base):
    # 表的名字
    __tablename__ = 'user'

    # 表的結構
    id = Column(String(20), primary_key=True)
    name = Column(String(20))

# 初始化資料庫連接配接
engine = create_engine('mysql+mysqlconnector://root:password@localhost:3306/test')
# 建立DBSession類型
DBSession = sessionmaker(bind=engine)

# ORM(Object Relational Mapping,對象關系映射)架構的作用就是把資料庫表的一行記錄
# 與一個對象互相做自動轉換。
           

WSGI接口

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# WSGI:Web Server Gateway Interface, web服務網關接口(接口規範)
# 1.浏覽器發送一個HTTP請求;
# 2.伺服器收到請求,生成一個HTML文檔;
# 3.伺服器把HTML文檔作為HTTP響應的Body發送給浏覽器;
# 4.浏覽器收到HTTP響應,從HTTP Body取出HTML文檔并顯示。


# hello.py
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']

# server.py
from wsgiref.simple_server import make_server
from hello import application  # 導入 hello.py 定義的application()

httpd = make_server('', 8000, application)
print('Serving HTTP on port 8000...')
httpd.serve_forever()

# hello.py 與 server.py 在同一目錄下,運作 server.py,
# 浏覽器 輸入 http://localhost:8000,就能顯示處 hello.py 的内容。