python技術手冊筆記

這篇算是念書的重點筆記
主要是基礎語法
書目是林信良撰寫的python 3.5技術手冊
條例出來內容有點多
建議可以用關鍵字找
 

1.cmd或powershell 輸入python可以進入REPL模式

可以直接打指令

Ex print('hello world')

 

2.help() 可以進去說明頁面

前面會變成help>

想知道有那些模組或關鍵字可以輸入modules.keywords

要結束可以輸入quit

 

3.REPL也可以使用help()查詢

Ex: help(print)

要離開REPL環境可以使用quit()

 

4.只是要執行小程式片段可以在命令提示字元輸入

Python –c “print(‘hello world’)”

 

5.

程式最上面編碼設定

# coding=<encoding name>

# coding:<encoding name>

可以指定要用的編碼,避免錯誤

Python 3之後的直譯器預設編碼是UTF-8

原始是ASCII

 

6.匯入模組可以用,區隔

Import sys,email

常用模組都存在__builtins__

可以在REPL模式使用dir(__builtins__)查看

 

7.如果要匯入別人的模組或不同資料夾的檔案

可以SET PYTHONPATH=路徑1;路徑2

可以檢查

>>Import sys

>>sys.path

 

或是直接用append也可以
>>sys.path.append(‘路徑’)

 

8.重新命名模組

Import 模組名稱as 自訂名稱

但是還要加上前置詞

 

也可以直接指定模組中的名稱

Ex:

From sys import argv

From sys import argv,path (多個)

From sys import * (全部 不建議用 會覆蓋前面名稱)

 

也可以從套件匯入模組

 

9.pycharm的Run點開可以用Edit設定命令列引數

不用自己打指令

10.整數型態為int,不區分整數與長整數

可以用Int()轉換

浮點數float()

Ex取得字串3.14的整數部位要 int(float(‘3.14’))

 

布林bool()

0,None,False,0.0,0j(複數),’’,()(空Tuple),[](空清單),{}(空字典)傳給bool()都回傳False

其他傳進去都會是True

 

複數 :Complex類別的實例

可以直接運算

>>a = 3+2j

>>b=5+3j

>>a+b

>>8+5j

 

11.溢出字元 跟C#差不多

Print(‘\\t’)

==print(r‘\t’) >>r表示raw string

 

12.如果字串需要換行 要用’’’表示

Print(‘’’ aaa

Bbb‘’’)

 

12.print()預設會換行

Print(‘hello’,end=’’) 在指定的字串之後,end參數指定的字串就會輸出

不想換行就把end指定空字串

 

Print('hello','kuan')預設會用空白分隔字串

Print('hello','kuan',sep='')設定sep可以用指定的來分隔

 

13.字串格式化符號

String % data

String % (data1,data2,…)

 

%5d 保留5個欄位 不足用空白表示

%-5d表示向左對齊

%.2f表示小數後保留2位數

 

Python 3之後版本

‘{ }’.format()的格式

{0:d}

{0:.2f}

Format()也可以進行簡單運算

EX

Names = ['Kuan','Lin','Fu']

'All names :{n[0]},{n[1]},{n[2]}'.format(n = names)

或是格式化單一數值

Format(3.14159,'.2f')

 

14.List

用[ ]建立

有以下方法append(),

Pop(),取出元素

Remove(), 移除指定元素值

Reverse(),

Sort()

Del listname[index] 刪除指定位置元素

附加多個元素 extend() ex.numbers.extend([10,20,30])

可以存任何型態的物件

 

List()可以把iterable的物件轉成list

 

15.Set 是無序不重複的集合

可以使用{}包括元素,元素用,區隔

如果要建立空集合 必須要用set()

 

List跟set不能放到集合中 因為要保證元素不重複(unhashable物件)

Set()可以把iterable的物件轉成Set (字串,list,Tuple)

 

16.Dictionary

每個key不能重複

可以用in檢查是否存在字典裡

可以用Get(‘key name’)取得值

預設傳回None

也可以指定預設值

Dict.get(‘key’,9999)

 

Items():可取得鍵值 回傳dict_items物件

Keys():取得鍵 回傳dict_keys物件

Values():取得值 回傳dict_values物件

以上三個是惰性求值,需要的時候才進行運算轉成list使用

 

也可以使用dict()建立字典

Ex.

Accounts = dict(kuan=123456,momor=642132,hamimi=970221)

Accounts2 = dict([('kuan',123465),('momor',642132),('hamimi',970221)])

 

17.Tuple

有序結構,且建立後就不能改變

用,就可以建立

最後一個,可以省略

() 沒任何元素的Tuple

建立單一個元素要寫成(elem,) 或elem, 不可以寫成(elem)

 

如果不想定義一個型態,只是想傳回相關的值就可以用

Tuple的元素可以Unpack,逐一分配給每個變數

Data = (1, 'kuan', True)

Id, name, verified = data

 

更方便的用法是置換變數

X = 10

Y = 20

X, y = y, x

 

Extended lterable unpacking

A, * b = (1, 2, 3, 4, 5)

>>>a =1

>>>b =[2,3,4,5]

 

 

18.變數都是存參考

而且變數本身沒有型態

可以前後指定不同的型別

 

可以看變數有幾個參考

Import sys

Sys.getrefcount(x)

 

不使用變數的話可以用del 刪除

 

19.運算

1.0 - 0.8 = 0.19999999999999996

算錢用浮點 遲早被人扁XD

要用decimal型別

Ex.

Import decimal

D1 = decimal.Decimal(sys.argv[1])

D2 = decimal.Decimal(sys.argv[2])

 

指數運算 **

2**3 =8

9**0.5=3

 

除法

/ :一定會產生浮點數

// : 整數跟整數除會產生整數,整數跟浮點數除會產生浮點數

print(10 / 3)  #3.3333333333333335

print(10 // 3)  #3

print(10 / 3.0)  #3.3333333333333335

print(10 // 3.0)  #3.0

 

 

20.字串運算

+可以串聯字串

*可以讓字串重複出現

 

但是python是強型別

字串跟數字不能串接,需要透過轉型str()

 

21.比較運算子

>, >=, <, <=, ==, !=(<>沒在用)

他們可以串接在一起

Ex. X <y <= z

W == x == y ==z

  • ==  , !=是用來比較物件實際的值

   Is ,is not 是用來比較物件參考

 

22.邏輯運算

And or not

 

0,None,False,0.0,0j(複數),’’,()(空Tuple),[](空清單),{}(空字典)

以上這些都是false的結果

其他都會是True

 

23.位元運算

And or not xor

二進位逐位元運算

10010001 and

01000001

=

00000001

補數運算是所有位元0變1,1變0

 

也可以用在set型態上

&交集,|聯集,^互斥 –差集

 

24.切片運算slice

[start:end:step]

Ex name = ‘Justin’

Name[0:4:2]=’Js’

Name[2::2]=’si’

Name[:5:2]=’Jsi’

Name[::-1]=’nitsuj’ 負偏移 等於反轉了

 

[:]只是做淺層複製,只複製參考

也可以用來做元素取代

Lt = ['one','two','three','four']

Lt[1:3] = [2, 3]

Print(lt)

Lt[1:3] = ['ohoh']

 

25.while迴圈

有else的用法,但不管true或false都會執行,除非用break中斷

 

26.range函數

Range(start,stop[,step])

Step是遞增值 預設1

 

27. Enumerate()可以用來做迭代

Name = 'Justin'

List(enumerate(name))

>>> 

[(0, 'J'), (1, 'u'), (2, 's'), (3, 't'), (4, 'i'), (5, 'n')]

預設會從0開始記數

如果要從其他開始可以指定2個參數enumerate(arrayname,index)

 

28.

Pass:不做任何動作 只是用來維持程式完整性

Continue:執行下一次迴圈

Break:中斷執行 跳出迴圈

 

29.for Comprehension

把迴圈語法壓縮,但是可讀性會下降

Ex.

Squares = []

For arg in sys.argv[1:]:

    Squares.append(int(arg) ** 2)

Print(squares)

可以簡化為

Squares = [int(arg) ** 2 for arg in sys.argv[1:]]

Print(squares)

 

另一個例子

For arg in sys.argv[1:]:

    If int(arg) % 2:

        Odds.append(arg)

改成

Odds = [arg for arg in sys.argv[1:] if int(arg) % 2]

 

兩邊放上[]表示會幫忙轉成list,如果資料來源很多或是有惰性求值特性的產生器時候,可以放上() 效能比較好

 

30.函式裡面可以再寫一個函式

Local function

不支援overload 因為後面的函式會蓋掉前面的(同樣名稱的)

 

參數預設

def prepend(elem,lt=[])

這樣寫法不好 因為沒指定的話lt會一直用同一個(參考)
要改成def prepend(elem,lt=None)然後在程式裡判斷

lt = lt if lt else []

 

31.unpack的應用

第一種用法:

函式定義

def account(name, number, balance)

可以用這樣給(轉成tuple分配)
account(*('Kuan', '123-4567', 1000))

或是(字典自動對應)

Params = {'name': 'Jenny', 'number': '123-987654', 'balance': 1000}

Account(**params)

 

第二種用法:

反過來也可以
def sum(*numbers)

用這樣給

Sum(1, 2, 3)

 

第三種用法:

Def ajax(url, **user_settings):

    Settings = {

        'Method': user_settings.get('method', 'GET'),

        'Contents': user_settings.get('contents', ''),

        'Datatype': user_settings.get('datatype', 'text/plain')

    }

    Print(f'請求:{url}')

    Print(f'設定:{settings}')

Ajax('http://www.com.tw', method='POST', contents='book=python')

My_settings = {'method': 'POST', 'contents': 'book=python'}

Ajax('http://www.com.tw', **my_settings)

 

*是轉成tuple分配給值

**是轉成dictionary對應

 

32.一級函式

函式可以當作物件傳遞,指定給其他變數

可以傳入list跟一個function 有點類似C#的delegate

具有惰性求值的特性

 

Filter()

Map()

Sorted():可以傳入list或tuple 有key 跟 reverse參數

Ex: sorted(('Justin', 'openhome', 'momor'), key=len, reverse=True)

 

List本身有sort()方法,會直接本身排序不像sorted()回傳一個新的list

 

也可以使用lambda建立簡單函式

語法:lambda 參數:函式本體

運算結果會作為回傳值,不用寫return

Ex:

def filter_lt(predicate, lt):

    result = []

    for elem in lt:

        if predicate(elem):

            result.append(elem)

    return result

lt = ['justin', 'caterpillar', 'openhome']

寫法一

filter_lt(lambda elem: len(elem) > 6, lt)

寫法二

list(filter(lambda elem:len(elem) > 6,lt))

 

如果不用參數,直接在lambda後面加上冒號

要兩個以上參數中間要用,區隔

 

33.lambda也可以模擬switch,因為python本身沒有

score = int(input('請輸入分數:'))

level = score // 10

#dictionary

{

    10 : lambda: print('Perfect'),

    9  : lambda: print('A'),

    8  : lambda: print('B'),

    7  : lambda: print('C'),

    6  : lambda: print('D')

}.get(level, lambda: print('E'))()

 

如果get取不到key值,就傳回預設,跟default一樣

Lambda沒辦法寫太複雜的邏輯,要就還是用def

 

34.python的變數可以在區域函式、外包函式、全域、內建的順序尋找或建立

取變數的時候會從最內層往外尋找

全域是以模組檔案為界

 

可以用locals()取得所有區域變數

globals() 可以取得全域變數

 

在函式內定義跟全域變數一樣名稱的變數不會改變全域變數的值

但是如果在變數前宣告global就是取用全域變數

或是宣告成nonlocal 他就會依照順序往外找這個變數

 

35.yield會把流程控制權讓給函式呼叫者

會傳回一個產生器有__next__()方法,可以用next()函式呼叫取出下個產生值

yield也可以透過send()給值

 

Asyncio模組可以去搜尋

 

36.from import 會將被匯入模組中之名稱參考的值,指定給目前模組中建立的新名稱

簡單說就是在現在模組中建立新變數,只是一開始參考同一個值

 

如果變數不想被from import *建立同名變數,可以用底線開頭命名

Ex._y = 20

 

或是定義一個__all__清單,限定可以被import的變數

但是用 import modulename 這種形式的不限

還是可以用[modulename.變數]存取

 

37.可以用del刪除模組名稱

>>>import foo

>>>qoo = foo

>>> del foo

>>>foo.x會出錯

 

是刪除指定的名稱,而不是刪除名稱參考的物件本身

所以參考那個物件的變數還會有資料

如果想知道目前已載入的module名稱與實例有哪些,可以透過sys.modules

 

38.import也可以出現在函式裡面,作用範圍就只會在函式中

39.尋找模組時的路徑

執行python直譯器時的資料夾

PYTHONPATH環境變數

PYTHON安裝中標準程式庫等資料夾

 

PTH檔案列出的資料夾(可以在.pth檔案中列出模組搜尋路徑,一行一個)

例如:建一個.pth檔案
列出模組搜尋路徑

C:\workspace\libs
C:\workspace\third-party
C:\workspace\devs

 

PTH檔案的存放位置不同作業系統不相同,可以如下取得位置

>>>import site

>>>site.getsitepackages()

 

如果確實建立.pth中列出的資料夾,而且將檔案放在上面列出的位置

Sys.path就會加入那些路徑

 

或是想要把PTH檔案放在其他資料夾

可以使用site.addsitedir(‘路徑’)

 

40.python的類別也是宣告class

初始化寫法是 __init__(self)方法
要設置屬性可以在後面直接加參數

這個方法是實例建構後初始化,不是建構類別實例

 

Ex

Class Account:

    Def __init__(self,name, number, balance):

        Self.name = name

        Self.number = number

        Self.balance = balance

建立實體

Act =Account(‘Justin’,’123456’,1000)

 

建立方法的第一個參數也都是放self(物件本身)

 

41.__str__()方法

可以傳回物件描述字串的方法,第一個參數是self

不要直接呼叫

可以用print()或str(物件)

 

42.如果要避免使用者直接誤用屬性

可以使用self.__XXX定義內部值

Ex.

Class Account:

    Def __init__(self, name, number, balance):

        Self.__name = name

        Self.__number = number

        Self.__balance = balance

 

但有一招可以破解就是用 ’_類別名稱__屬性名稱’

Ex._Account__balance

但不建議使用

因為__XXX是定義內部流程使用的

 

.43.外部屬性

如果是想可以讓外部取得的屬性

可以用@property宣告 方法

但是只允許取值,不能給值

Ex.

@property

    Def name(self):

        Return self.__name

 

44.外部屬性設定值

@property的用法只能給值

@屬性名稱.setter 加在方法上可以用來設定值(對應@property的方法名稱做前綴)

 

Ex.

@name.setter

    Def name(self,name):

        Self.__name = name

45.可以取得方法綁定的物件

class some:

    def me(self):

        return self

s = some()

print(s.me() is s.me.__self__)

 

s.me()叫做bound method

 

45.如果定義類別的時候,希望某個方法不被拿來作為綁定方法,可以使用     @staticmethod 標註

Ex.

@staticmethod

    def default(name, number):

        return account(name, number, 100)

可以直接用類別名稱呼叫方法

bank.account.default('kuan','123456')

 

也可以用類別實例呼叫 但不建議

acct.default('kuan','123456')

這樣acct也不會被傳入當default的第一個參數

 

46.上面的方法不好,因為寫死類別名稱

萬一要改名稱的話要改很多地方

對於這個狀況 可以在方法上標註 @classmethod

第一個參數一定是接受所在類別的type實例

 

@classmethod

    def default2(cls,name, number):

        return cls(name, number, 100)

 

47.可以用__dict取得類別或實例的特性

但是不建議直接呼叫__的方法

可以改用vars(物件)

 

48.可以在類別實例建構後,直接再建立新方法

也可以直接建一個變數在實例上

 

49.類別實例的建構是__new()__方法定義

第一個參數是類別本身

之後可定義任意參數建構物件

 

如果傳回的物件是第一個參數的類別實例,才會接著執行__init()__方法

後面的參數會繼續使用

 

50.如果想再物件被刪除的時候,自行定義清除相關資源的行為,可以實作__del__()方法

不被任何名稱參考就會被回收,等於刪除

 

51.類別的繼承寫法

class 子類別(父類別)

 

52.鴨子定型

如果他走路像個鴨子,游泳像個鴨子,叫聲像個鴨子,那他就是鴨子

 

要思考物件的行為,而不是物件的種類

 

53.繼承類別後打算基於父類的方法來重新定義某個方法,可以使用super()來呼叫父類別方法

 

54.如果希望子類別在繼承後一定要實作的方法,可以在父類別中指定metaclass為abc模組的abcmeta類別,並在指定的方法上標註abc模組的@abstractmethod

Ex

from abc import abcmeta, abstractmethod

class role(metaclass=abcmeta):

方法

@abstractmethod

    def fight(self):

        pass

這時候就不能用父類來建構物件了

如果類別繼承但沒實作方法會發生TYPEERROR

 

55.python中若沒有定義的方法,某些狀況下必須呼叫時,就會看父類別中是否有定義,如果定義了自己的方法,就會以你定義的為主

 

56.繼承的時候需要父類別的初始化方法

那可以在子類的初始化方法用super().__init__()叫用父類的初始化流程

 

Super()相當於super(__class__,<first argument>)

__class__代表所在類別

<first argument>代表目前所在方法第一個引數

 

呼叫init當法就等同於super(__class__,self)

 

57.rich comparison方法

自定義比較運算子

__lt__():<

__le__():<=

__gt__():>

__ge__():>=

__eq__():==

通常是用is比較,但只有比較物件參考,沒有比較物件的值

可以用hasattr(object,’屬性名稱’)來比較屬性值是否相等

或是可以檢查型態isinstance(other,__class__)

__ne__():!=

 

如果需要定義完整的比較方法

可以用functools. Total_ordering

然後在class上掛@total_ordering

當被標註後必須實作__eq__()方法

__lt__(),__le__(),__gt__(),__ge__()選一個方法實作

Ex.

From functools import total_ordering

 

@total_ordering

class some:

    def __init__(self,value):

        self.value = value

    def __eq__(self, other):

        return self.value == other.value

    def __gt__(self, other):

        return self.value > other.value

 

58.列舉模組

from enum import intenum,Enum,unique

繼承Enum的話列舉值可以是各型態

Intenum值只能是int

Ex.

@unique

class Action(intenum):

    Stop = 1

    Right = 2

    Left = 3

    Up = 4

    Down = 5

 

Action()是用來指定列舉值然後傳回列舉物件

Ex Action(3)

>> Action.left

 

列舉物件有name跟value可以取得名稱跟值

Ex.

Temp = Action(3)

Print(temp.name)

Print(temp.value)

 

或是用[]指定列舉名稱舉得列舉物件

Ex.

>>> Action['right']

>>>Action.right

 

也可以用for in來迭代

 

列舉名稱不能重複,但值可以重複(但是後者會是前者的別名)

如果不想要值重複可以在class上加標註 @unique(要import)

 

59.繼承多個父類時,如果父類有相同方法名稱

搜尋順序是 子類>父類由左至右 > ….

 

事實上是依據類別的__mro__屬性的tuple元素順序來找

(method resolution order)

 

如果要直接知道父類別可以用__bases__來得知

也可以直接 Class. __bases__ = (Class1, Class2)來更改繼承順序

 

 

60.如果繼承的父類定義抽象方法,另一個繼承的也定義了同名的

會依照繼承順序決定抽象方法是否得到實作

 

61. Help(super)看super的用法

Super() -> same as super(__class__, <first argument>)

  Super(type) -> unbound super object

  Super(type, obj) -> bound super object; requires isinstance(obj, type)

會從obj的__mro__清單裡面type的下個元素開始尋找相同的方法

 

  Super(type, type2) -> bound super object; requires issubclass(type2, type)

會使用type2的__mro__清單,從指定的type之下各類別開始尋找指定的方法,有的話把type2當作是呼叫方法的第一個引數

 

62.help()函式會取出__doc__字串

稱作docstrings,可以自己在函式內定義 用’’’把字串包起來

 

63.python文件查詢

Https://docs.python.org/3/

Indices and tables裡面

 

或是用cmd 輸入

>python -m pydoc -p 8080

>b

開啟一個瀏覽器(http://localhost:8080/)

 

第三方函式庫

https://pypi.org/

找到後可以用pip install ‘套件名稱’ 安裝

 

也可以把要的套件放在requirements.txt

然後pip –r install requirements.txt

 

64.錯誤捕捉語法

try

except typeerror as err:

else (try沒出錯才執行)

finally (不管怎樣都會執行)

for in語法遇到stopiteration的時候會直接pass

不會引發例外錯誤

 

Except之後可以使用tuple指定多個物件,也可以有多個except

如果沒有指定except後的物件型態,表示捕捉所有引發的物件

 

 

如果一個例外在except的過程中就符合某個例外的父型態,後續即使有定義也不會執行到

Ex arithmeticerror > zerodivisionerror

所有例外都是baseexception的子類別

 

例外的繼承架構可以搜尋build-in Exceptions

Https://docs.python.org/3/library/exceptions.html

 

65.想要自訂例外,要繼承Exception或是相關子類別

如果自定義__init__() ,建議把傳入的參數透過super().__init__(arg1,arg2…)

來呼叫Exception的__init__(),因為這些參數可透過args屬性已tuple取得

 

66.要丟出一個例外錯誤要使用raise

 

如果在except已經記錄錯誤訊息但要重新引發例外錯誤時

可以用

try

except exception as err:

raise 自訂例外 from err

 

67.如果進一步使用except處理重新引發的例外,可以透過例外實例的__cause__

來取得raise from時的來源例外

就算沒有使用到raise from,原本的例外也會自動被設定給被引發例外的__context__屬性

Ex.

try:

    try:

        raise eoferror('xd')

    except eoferror:

        raise indexerror('orz')

except indexerror as e:

    print(e.__cause__)

    print(e.__context__)

 

68.在python中,就算例外是個錯誤,只要程式碼能明確表達出意圖的情況下,也常會當程式流程的一部分

 

69.堆疊追蹤

可以用traceback module得知發生例外的根源跟執行過程

Ex.

import traceback

       traceback.print_exc()

 

print_exc()可以指定開啟的檔案

file=open(‘xxx.txt’)

limit參數 預設none 不限制堆疊追蹤個數 指定正數就是顯示最近幾次

         負數就是顯示最初幾次

 

 

70.sys.exe_info()

可以取得一個tuple物件 包括例外類型、實例、traceback物件

Ex

(<class 'Exception'>, Exception('shit happens!'), <traceback object at 0x00000252ff980380>)

 

Traceback物件代表每一層次的追蹤,可以用tb_next取得更深層的呼叫堆疊

Ex.

try:

    test()

except:

    type, value, traceback = sys.exc_info()

    print('例外型態:', type)

    print('例外物件:', value)

 

    while traceback:

        print('..........')

        code = traceback.tb_frame.f_code

        print('檔案名稱:', code.co_filename)

        print('函式或模組名稱:', code.co_name)

 

        traceback = traceback.tb_next

 

71.未被比對到的例外,直譯器最後會呼叫sys.excepthook()並傳入三個引數

也就是sys.exc_info()傳回的三個物件

 

72.提出警告訊息

通常是一種提示,用來告知程式有潛在問題

比方使用被棄用的功能,以不適當的方式存取資源

透過warnings模組的warn()函式提出警告

Ex.

Import warnings

warnings.warn('orz方法已棄用', deprecationwarning)

warnings.warn('xd使用者權限不足', userwarning)

 

預設是不會有任何結果

要起作用要在直譯器下指令

>>>Python –W always

-W接受的格式是action:message:category:module:leneno

Always是action指定

Error

將警告訊息轉為例外(引發)

Ignore

不顯示警告訊息

Always

總是顯示警告訊息

Default

只顯示每個位置第一個符合的警告訊息

Module

只顯示每個模組第一個符合的警告訊息

Once

只顯示第一個符合的警告訊息(無論位置為何)

 

Message是正規表達 用來比對想顯示的警告訊息

Category可以指定wrning的子類別 預設是userwarning

Module也是正規 比對模組名稱

Lineno是int 指定發出訊息的程式碼行號

 

73.try ,except ,else 是一組的,盡量讓可能引發錯誤的來源相關

Ex.

import sys

for arg in sys.argv[1:]:

    try:

        f = open(arg,'r')

    except filenotfounderror:

        print('file not found')

    else:

        try:

            print(arg,'有', len(f.readline()), '行')

        finally:

            f.close()

如果檔案開啟失敗,就不會建立f變數

如果finally寫在外層反而會引發錯誤

 

74.with [資源] …as 變數 語法

With之後的資源實例可以透過as指定給一個變數,之後就可以在區塊中進行資源的處理,當離開with as 區塊之後,就會自動做清除資源的動作

但as 可以省略

等同於C#的using

Ex.

for arg in sys.argv[1:]:

    try:

        with open(arg, 'r') as f:

            print(arg, ' 有 ', len(f.readlines()), ' 行 ')

    except filenotfounderror:

        print('找不到檔案', arg)

 

75.with as 不限使用於檔案,只要物件支援情境管理協定(Context Management Protocol)就可以用

支援情境管理協定的物件必須實作__enter__()、__exit__()兩個方法

With一開始執行就會跑__enter__()方法,回傳的物件可以使用as指定給變數

 

如果with區塊發生例外就會跑進__exit__()方法,並傳入三個引數

這三個就是sys.exe_info()回傳的物件

這邊回傳False 例外會重新被throw出去

但要是沒發生例外正常執行,也是會執行__exit__()方法,只是都傳入None

class resource:

    def __init__(self, name):

        self.name = name

 

    def __enter__(self):

        print(self.name, ' __enter__')

        return self

 

    def __exit__(self, type, value, traceback):

        print(self.name, ' __exit__')

        return false

 

with resource('res') as resource:

    print(resource.name)

 

 

以上的方式如果不夠直覺,也可以使用模組

Contextlib模組的@contextmanager來實作,讓資源的設定跟清除更直覺

引用>>

From contextlib import contextmanager

 

Ex.

@contextmanager

def file_reader(filename):

    try:

        f = open(filename, 'r')

        yield f

    finally:

        f.close()

 

yield的物件會做為as的值

 

with as語法是用來表示那個區塊是處於某個特殊情境中,自動關閉檔案的情境只是一種狀況

 

或是不回傳東西只是要處理error的部分

有更簡單的寫法 suppress()

ex.

from contextlib import suppress

 

with suppress(filenotfounderror):

    for line in open(sys.argv[1]):

        print(line, end='')

 

 

如果是有實作close但沒有情境管理的

可以使用closing()

 

76.補充supress()

實作情境管理器的,可以指定想抑制的例外類型

(1)

class suppress:

    def __init__(self, ex_type):

        self.ex_type = ex_type

 

    def __enter__(self):

        return none

 

    def __exit__(self, ex_type, msg, traceback):

        if ex_type == self.ex_type:

            return true

        return false

(2)

from contextlib import contextmanager

 

@contextmanager

def suppress(ex_type):

    try:

        yield

    except ex_type:

        pass

 

77.檔案讀寫可以使用open()函式

比較常用到前兩個參數

  1. File

檔案的路徑

可指定相對路徑或絕對路徑

  1. Mode

用字串來指定檔案開啟模式

R

讀取模式(預設)

W

寫入模式,會先清空檔案內容

X

只在檔案不存在的時候才建立新檔案並開啟為寫入模式,如果檔案已存在會引發fileexisterror

A

附加模式,若檔案已存在,寫入的內容會附加至檔案尾端

B

二進位模式

T

文字模式(預設)

+

更新模式(讀取&寫入)

 

Mode預設是’r’,只指定 ‘r’ ‘w’ ‘x’ ‘a’的情況相當於已文字模式開啟

如果要以二進位模式開啟 要指定 ‘rb’ ‘wb’ ‘xb’ ‘ab’

  1. Buffering:用來設置緩衝策略

預設是會自行決定

設成0的話,不用flush()就可以馬上看到檔案內容變化

Flush可以把緩衝內容出清

  1. Encoding跟errors

指定文字模式的檔案編碼

預設會用locale.getpreferredencoding()的傳回值

 

78.read()方法在未指定參數的情況下,會讀全部檔案的內容,文字模式會回傳Str()實例,二進位模式回傳byte實例

 

如果有指定整數參數,會讀取指定字元數或位元組數

 

79.readline(),readlines(),writelines()

文字模式預設是讀到 \n ,\r 或\r\n都可以被判定為一行

而readline(),readlines()讀到的每一行換行字元都一律為\n

二進位模式的行的判斷標準是b’\n’

 

文字模式在寫入的時候,任何’\n’都會被置換成os.linesep

 

80.

Tell()方法可以告知目前代檔案的位移值,單位是位元組值,檔案開頭的位移值是0

Seek()方法可以指定跳到哪個位移值

 

81.readinto()方法可以把二進位模式的檔案轉成bytearray

Ex.

Import os.path

B_arr = bytearray(os.path.getsize('test.txt'))

With open('test.txt','rb') as f:

    F.readinto(b_arr)

 

81.

sys.stdin代表標準輸入

sys.stdout代表標準輸出

可以模擬print()跟input()

也可以類似open()函式

 

如果要以二進位讀取或寫入

可以用

sys,stdin.buffer或sts.stdout.buffer

ex.

def console_input(prompt):

    sys.stdout.write(prompt)

    sys.stdout.flush()

    return sys.stdin.readline()

 

82.python的i/o分為三個主要類型

文字i/o

二進位i/o

原始i/o

iobase是所有i/o類別的基礎類別

 

 

83.set的內容無序且不重複,但只有hashable的物件可以放進去

list、dict、set本身都不行

 

hashable的物件:必須有hash值,這個值在執行期都不會變化,而且必須可以進行相等比較,較具體一點,物件必須實作__hash__()跟__eq_()方法

 

set在加入物件的時候,會呼叫__hash__()取得hash值,看看是否跟現有物件都不相同,然後再用__eq_()比較相等性才確定要不要加入

 

python的內建型態,只要是建立後狀態無法變動的(immutable)的,都是hashable,可變動的型態之實例都是unhashable

 

自定義類別的實例,__hash__()是根據id()計算的,__eq_()是用is比較,所以兩個分別建立的實例一定不同

 

84.具有__iter__()方法的就是iterable物件

可以用iter()方法從物件取得迭代器,不用直接呼叫__iter__()方法

傳回的迭代器具有__next__()方法,可以逐一迭代物件,如果無法進一步迭代會引發stopiteration,迭代器也會具有__iter__()方法,傳回本身(也是iterable物件)

 

產生器也是一種迭代器(yield語法)

 

85.itertools模組

import itertools

有cycle(),repeat()函式

 

count():可以指定起始值跟步進值

accumulate():累加運算

chain()或chain.from_iterable():可以將指定的序列攤平逐一迭代

dropwhile():會在指定函式傳回true的情況下,持續丟棄元素

takewhile():相反持續保留元素,值到有元素讓函式回傳false

 

groupby():分類 回傳值是itertools.groupby物件(iterable),迭代的時候會傳回一個tuple,第一個值是指定的分類值,第二個值是itertools._grouper,包含所有同個分類的物件

ex.

names = ['justin', 'monica', 'irene', 'pika', 'caterpillar']

group_by_name = itertools.groupby(names, lambda name: len(name))

for length, group in group_by_name:

    print(length,list(group))

 

86.排序可以使用sort()排序

list才有sort()方法

可以放reverse設定 ex. list.sort(reverse = true)

或是針對key排序 ex.list.sort(key = lambda x : x[0])

 

其他iterable物件,可以使用sorted()

sorted(iterable,key,reverse)

 

如果是自訂類別要排序,必須實作__lt__()方法

ex.

    def __lt__(self, other):

        return self.name < other.name

 

87.使用operator模組幫助排序

itemgetter是針對索引結構

ex.

customers = [

    ('justin', 'a', 40),

    ('irene', 'c', 8),

    ('monica', 'b', 37)

]

from operator import itemgetter

sorted(customers, key = itemgetter(0))

 

 

attrgetter是針對物件屬性

ex.

 

customers = [

    customer('justin', 'a', 40),

    customer('irene', 'c', 8),

    customer('monica', 'b', 37)

]

from operator import attrgetter

print(sorted(customers, key = attrgetter('name')))

 

88.python的群集架構有三個類型

循序類型sequences type

集合類型set type

映射類型mapping type

 

89.循序類型

有序、具備索引的資料結構,都是iterable物件

list, tuple,str,bytes,bytearray

tuple,str,bytes是不可變動的循序類型,有預設的hash()實作,可作為set的元素或dict的key

 

90.集合類型

無序而且元素必須都是hashable物件且不會重複,他們是iterable物件

可以用in ,not in,len(set) 跟交集聯集差集操作

 

本身是可變動,如果想要不可變動的集合類型

可以使用frozenset()來建立(有實作__hash__()方法,為hashable物件)

 

91.映射類型

可以把hashable物件映射至一個任意值

內建是dict

 

92.deque類型

有佇列或雙向佇列的需求 先進先出

可以用collections模組的deque類別

在兩端可以做插入跟移除

o(1)時間複雜度

append(),pop(),insert(),appendleft(),popleft()

 

rotate還可以指定要轉幾個元素

ex.

from collections import deque

deque = deque([1, 2, 3])

deque.appendleft(0)

deque.appendleft(-1)

print(deque)

print(deque.pop())

print(deque.popleft())

print(deque)

 

>>>deque([0, 1, 2])

deque.rotate(1)

>>>deque([2, 0, 1])

 

93.namedtuple函式

tuple狀態不可變,比較省記憶體,而且是hashable可以當set的元素或dict的鍵

namedtuple可以有欄位名稱

namedtuple(typename,field collection…)第一個參數是要建立的型態名稱,第二個是欄位名稱

ex.

from collections import namedtuple

point = namedtuple('point',['x', 'y'])

p1 = point(10, 20)

print(p1.x)

print(p1.y)

 

p2 = point(11, y=22)

print(p2)

 

x, y = p1

print(x)

print(y)

 

#如果來源是iterable物件

lt = [30, 40]

print(point(*lt))

#這樣也可以

print(point._make(lt))

#傳回欄位名稱與值

print(p1._asdict())

print(p1._replace(x=20))

#傳回全部欄位名稱

print(point._fields)

 

point3d = namedtuple('point3d',point._fields + tuple('z'))

print(point3d(10, 20, 30))

 

#docstrings

point.__doc__ = 'coordinate system (x, y)'

point.x.__doc__ = 'coordinate system x'

point.y.__doc__ = 'coordinate system y'

 

 

94.ordereddict類別

dict沒辦法直接排序,除非拿keys() 然後用for迴圈

可以用collections模組的ordereddict 建立的時候會保有dict最初鍵值加入的順序

ex.

from collections import ordereddict

from operator import itemgetter

origin = {'a':85, 'b':90, 'c':70}

#依值排序

print(ordereddict(sorted(origin.items(), key = itemgetter(1))))

 

95.defaultdict類別

collections模組的

接受一個函式,建立的實例在當指定的鍵不存在,就會使用指定的函式來產生,並直接設定為鍵的對應值

 

96.counter類別

collections模組

可以用來計算字串中出現的字元數

ex

from collections import counter

c = counter('your right brain has nothing left.')

print(c)

print(list(c.elements()))

 

也可以指定dict給counter,會依照dict中值的指定建立對應數量的鍵

 

97.

如果想合併兩個dict可以用update

ex.

cust1 = {'a':'justin', 'b':'monica'}

cust2 = {'c':'irene', 'd':'kuan'}

custs = {}

#合併

custs.update(cust1)

custs.update(cust2)

 

或是使用collections模組的chainmap

ex. temp = chainmap(cust1,cust2)

 

底層是使用list來維護指定的dict,也比較有效率

如果要更新某個鍵值,會在底層第一個找到key的dict更新,但是都找不到的話會直接在第一個dict新增key-value

 

底層的list可以透過maps屬性取得

ex.temp.maps

 

如果想在既有的chainmap新增dict,可以在maps屬性上使用append()方法

或是用new_child()指定

 

想建立一個新的chainmap但不包含來源的第一個dicy,可以使用parents屬性(個人覺得用不到)

 

98.如果要根據自己需求實作群集

想用[]取值可以實作__getitem__

想用[]設值可以實作__setitem__

用del 或[]刪除可以實作__delitem__

 

也可以直接繼承abc模組,裡面有定義好該實作的方法

from collections.abc import mutablemapping

mutablesequence,mutableset,

 

mapping/sequence/set 並不是dict/list/set的子類別,只是擁有行為

 

99.如果只是要基於str,list,dict等行為增加自己的方法定義

也可以使用collections模組的userstring、userlist、userdict

 

100.序列化python物件可以使用pickle模組

直接import pickle

序列化的時候會把物件轉為bytes,稱為pickling,可以用dumps()函式

相反的操作稱為unpickling,可以用loads()函式

ex.

cus = {'a':123456, "b":789456}

pickled = pickle.dumps(cus)

print(pickled)

print(pickle.loads(pickled))

 

如果無法pickling或unpickling就會引發picklingerror或unpicklingerror

(父類別是pickleerror)

 

如果是想把bytes保存在檔案,dumps()跟loads()都有file參數可以用,檔案物件必須是二進位模式

 

ex.

 with open(self.filename, 'wb') as fh:

     pickle.dump(self, fh)

 

with open(filename, 'rb') as fh:

      return pickle.load(fh)

 

101.shelve模組

shelve物件行為像是字典的物件,key必須是字串,值可以是pickle模組可處理的python物件,他直接與一個檔案關聯,因此使用上就像一個簡單的資料庫

ex.

import shelve

dvd = shelve.open(‘dvdlib.shelve’)

dvd[‘birds’] = (2016, 1, ‘justin’)

dvd.close()

 

dvd = shelve.open(‘dvdlib.shelve’)

dvd.sync()

close()或async()資料就會儲存到檔案

 

102.資料庫連線db-api 2.0

connection的基本方法

close()

關閉料庫連線

commit()

將尚未完成的交易提交

rollback()

將尚未完成的交易撤回

cursor([cursorclass])

傳回一個cursor物件,代表基於目前連線的資料庫游標,所有跟資料庫溝通都是透過cursor物件

cursor物件的基本方法

close()

關閉目前cursor物件

execute(sql,[,params])

執行一次sql指令,可以是query或command

executemany(sql,seq_of_params)

針對seq_of_params序列或映射中每個項目執行一次sql語句

fetechone()

從查詢的結果集取得下一筆資料

fetchmany([size])

從查詢的結果集取得多筆資料

fetchall()

從查詢的結果集取得全部資料

 

如果只是要測試用可以用sqlite3模組,本身的資料可以存在檔案或記憶體

 

103.sqlite資料庫操作

(1)建立連線

conn = sqlite3.connect(‘xxx.sqlite3’)

conn.close()

 

第一次執行會建立一個檔案,也可以傳一個’:memory:’字串,就會在記憶體建立資料庫

(2)建立表格

c = conn.cursor()

c.execute('''create table messages (

 id integer primary key autoincrement unique not null ,

 name text not null ,

 email text not null ,

 msg text not null

 )''')

#預設不會自動提交變更 要用commit

conn.commit()

conn.close()

 

以上可以用情境管理器自動commit&close 發生例外會rollback

with sqlite3.connect('db.sqlite3') as conn:

    c = conn.cursor()

    c.execute("insert into messages values (1, 'justin','kuan@gmail.com','message...')")

(3)查詢

c = conn.cursor()

c.execute('select * from messages')

print(c.fetchall())

cursor本身是迭代器,每一次迭代會呼叫cursor的fetchone()方法

  1. 更新或刪除

 

c.execute("update messages set name = 'kuanfu lin' where id = 1")

print(list(c.execute('select * from messages')))

 

104.sql cmd參數化,避免sql injection

第一種可以用”?”

ex.

querystr = “select * from messages where name =? and id=?”

c.execute(querystr),(name, id)

第一個參數放查詢字串,第二個指定一個tuple,元素順序對應?的順序

 

第二種可以用具名佔位符號,要加上冒號當前導字元

querystr = “select * from messages where name =:name and id=:id”

c.execute(querystr),{‘name’:’kuan’, ‘id’:4})

 

第二個參數是用dict指定實際資料

 

如果有多筆資料要操作的話,可以用executemany()

 

 

105.交易的特性

原子性atomicity:交易是一個單元工作,裡面每個步驟(sql指令)必須全部成功(commit)不然就是全部失敗(rollback),需要定義一個交易邊界(begin)

 

一致性consistency:所有資料集合在交易前後必須前後一致

 

隔離行為isolation behavior:交易跟交易之間不互相干擾

預設會鎖定資料,timeout時間是5秒

connection有isolation_level屬性,可以設定目前的隔離設定,預設是’’,設成none表示不做任何隔離

持續性durability:交易一但成功,變更必須保留下來

 

106.sqlite3的模組預設不會自動commit,要自己呼叫commit跟rollback

 

或是直接把程式包在情境管理器裡面

 

107.csv模組

可以用reader()讀取csv檔案

可以接受iterable物件

ex.

with open('mi_5mins_hist10505.csv', encoding='utf-8') as f:

    with open('test.csv', 'w', encoding='utf-8', newline='') as wf:

        for row in csv.reader(f):

            print(row)

        rows = csv.reader(f)

        csv.writer(wf).writerows(rows)

預設的csv偏好格式是['excel', 'excel-tab', 'unix']

可以用csv.list_dialects()來得知有哪些

使用reader可以指定dialect參數指定偏好格式

delimiter可以指定分隔符號csv.reader(file, delimiter =’ ’)

quotechar指定引號字元

 

csv.writer()可以轉成csv檔,裡面放list,每個元素就是一列

 

108.dictreader,dicrwriter

可以把csv以dict的方式處理

ex.

import csv

custs = [

    'first,last',

    'justin,lin',

    'monica,huang',

    'irene,lin'

]

for row in csv.dictreader(custs):

    print(row)

>>> 

{'first': 'justin', 'last': 'lin'}

{'first': 'monica', 'last': 'huang'}

{'first': 'irene', 'last': 'lin'}

指定欄位名稱的方式

for row in csv.dictreader(custs,fieldnames=['firstname','lastname']):

    print(row)

>>> 

{'firstname': 'first', 'lastname': 'last'}

{'firstname': 'justin', 'lastname': 'lin'}

{'firstname': 'monica', 'lastname': 'huang'}

{'firstname': 'irene', 'lastname': 'lin'}

 

把dict寫進csv檔

ex.

temp = [

{'firstname': 'first', 'lastname': 'last'},

{'firstname': 'justin', 'lastname': 'lin'},

{'firstname': 'monica', 'lastname': 'huang'},

{'firstname': 'irene', 'lastname': 'lin'}

]

with open('sample.csv', 'w', newline='') as f:

    writer = csv.dictwriter(f, fieldnames=['firstname','lastname'])

    writer.writeheader()

    writer.writerows(temp)

 

109.json的序列化

json.dumps()

參數:

sort_keys :可以指定true或false 根據鍵排序

indent :可以加上指定數字的空白數量計行縮排

separators:分隔符號 可以指定 (',', ':'),預設是(', ', ': ')

 

ex.

import json

obj = {

    'name': 'kuan',

    'age': 40,

    'childs': [

        {'name': 'irene',

         'age': 8}

    ]

}

print(json.dumps(obj, sort_keys= true, indent = 4,separators= (',', ':')))

 

以上是針對內建型態才可以轉,如果是自訂類別要實作轉換函式

加上default參數,否則會有typeerror

 

如果是要把json字串轉成內建型態,可以用json.loads()

轉成自訂型態要指定一個轉換函式給object_hook參數

 

110.處理xml

模組:

xml.dom:基於w3c規範

需要把整個文件載入進行剖析

 

xml.sax :基於sax 是非正式的規範,不會一次讀取整份文件,是基於事件的api,一邊讀一邊剖析

 

xml.etree.elementtree:建議使用,以上兩個的折衷,iterparse()也可以做到一邊讀一邊處理

 

可以把xml檔案用parse()載入,他會傳回elementtree實例,可以用getroot()取得根節點,這會傳回一個element實例,一個element就代表一個標籤元素

ex.

import xml.etree.celementtree as et

 

#取得全部標籤名稱

def show_tags(elem, ident = ' '):

    print(ident + elem.tag)

    for child in elem:

        show_tags(child, ident + ' ')

tree = et.parse('country_data.xml')

show_tags(tree.getroot())

 

取得標籤上的屬性值 可以用attrib ,回傳一個dict,key value分別是屬性名稱跟值

element的text屬性可以取得標籤中的文字

 

fromstring()方法可以把xml字串轉成element實例

find(),findall(),iterfind()可以指定xpath取得想要的標籤

 

et.tostring()可以直接取得xml字串的bytes資料

 

修改xml

append():附加元素

insert():插入元素

remove():移除元素

set():設定元素屬性

 

 

如果想要一邊讀取一邊剖析,可以使用iterparse()

針對標籤的’start’,’end’,’start-ns’,’end-ns’事件發生時進行相對應的處裡

 

111.

utc時間 >世界協調時間

unix時間 > utc時間1970年1月1日 00:00:00為起點經過的秒數

目前的時間即使是gmt或api日期時間描述都是指utc時間

秒的單位定義是基於tai(銫原子幅射震動次數)

eposh:某個特定時間的起點,時間軸上某一瞬間

ex unix epoch選為utc時間1970/1/1 00:00:00

 

一年的秒數不是用365*24*60*60

ex.可以用time模組取得時間

tm_isdst目前時區是否處於日光節約時間 1是0否-1未知

import time

print(time.gmtime(0)) #utc時間1970年

print(time.time())

回傳struct_time實例

 

time.gmtime()是取得目前時間

time.localtime()可以取得目前所在時區時間

 

112.剖析時間字串

可以用strptime()函式

第一個參數放時間字串

第二個放格式設定

回傳一個struct_time物件

可用格式可參考time.strftime(()說明

strftime()接收一個strust_time實例,可以自訂日期格式

ex.

d = time.strptime('2016-05-26', '%y-%m-%d')

print(d)

如果有struct_time物件想轉換成秒數可以用mktime()

print(time.mktime(d))

自訂格式

print(time.strftime('%d-%m-%y', d))

113.時間字串格式

ctime():不指定數字會用time()取得的值(秒數)

ctime(secs)是asctime(localtime(secs))的封裝

 

asctime():可取得struct_time實例,不指定就使用localtime()回傳值

取得一個簡單的時間字串描述

ex.

print(time.ctime())

print(time.asctime())

>>> mon dec 16 17:33:18 2019

114.datetime模組

datetime():日期&時間

date():只有日期

time():只有時間

預設沒有時區資訊,單純用來表示日期或時間概念

會判斷基本範圍,有錯會拋出valueerror例外

ex.

import datetime

d = datetime.date(1975, 5, 26)

print(d.year, d.month, d.day)

 

t = datetime.time(11, 41, 35)

print(t.hour, t.minute, t.second, t.microsecond)

 

dt = datetime.datetime(1975, 5, 26, 11, 41, 35)

print(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)

 

使用今天日期建立datetime或date實例

from datetime import datetime, date

 

print(datetime.today())

print(date.today())

print(datetime.now())

print(datetime.utcnow())

 

>>> 

2019-12-16 11:16:53.171571

2019-12-16

2019-12-16 11:16:53.171570

2019-12-16 03:16:53.171570

 

時間字串描述跟剖析

date跟time會用T隔開

datetime.now().isoformat()

>>> 2019-12-16T11:35:40.502158

也可以自行指定

datetime.now().isoformat(' ')

>>> 2019-12-16 11:35:40.502158

 

115.時間運算

可以用datetime類別的timedelta方法

有很多時間單位可以選,用整數或浮點數

 

ex.

print(datetime.now() + timedelta(weeks = 3, days= 5, hours = 8,minutes= 35))

 

如果要考慮日光節約時間

可以使用pytz模組

再用normalize()修正時間

或是建議用utc來進行時間的儲存或操作,因為utc是絕對時間,不考量日光節約時間,要使用當地時區的場合時,再用datetime實例的astimezone做轉換

 

 

116.logger

可以直接建立logging.logger類別實例,但是建議用logging.getlogger()取得

一般來說一個模組只需要一個logger實例

呼叫的時候可以指定名稱,相同名稱下取得的logger會是同一個實體

通常會使用__name__(在模組中就是模組名稱)

 

log層級

debug

10

info

20

warning

30

error

40

critical

50

notset

0

預設只有大於30的才會輸出,所以只看得到warning以上的

logger本身有setlevel()可以設定,但是logger有階層關係,每個logger處理完自己的日誌動作後,會再委託父logger處理,所以不改變父logger的組態情況下,只能設定更嚴格的層級

 

 

要調整根logger的組態可以用logging.basicconfig()

ex.

import logging

logging.basicconfig(level = logging.debug)

logger = logging.getlogger(__name__)

logger.log(logging.debug, 'debug 訊息')

logger.log(logging.info, 'info 訊息')

logger.log(logging.warning, 'warning 訊息')

logger.log(logging.error, 'error 訊息')

logger.log(logging.critical, 'critical 訊息')

 

除了log()方法指定等級

還可以用debug(),info(),warning(),error(),critical(),exception()

 

117.log輸出至檔案

預設會輸出到sys.stderr

父logger指定檔案名稱,子logger如果沒有設定處理器就會輸出到這個

logging.basicconfig(filename ='xxx.log')

 

子logger處理器

logger.addhandler(logging.filehandler(bbb.log'))

 

子logger處理器>父logger處理器

處理器可參考logging.handlers模組

 

118.格式化錯誤文字

可以使用logging.formatter搭配logging.streamhandler()自訂想顯示的錯誤訊息

119.過濾log錯誤訊息

可以繼承logging.filter並定義filter(record)方法

 

120.正規表達式

import re模組

ex. import re

re.split(r'\d','justin1monica2irene')

>>> ['justin', 'monica', 'irene']

 

前面記得加r表示式原始字串,就不用溢出字元

 

分組&參考

((a)(b(c)))

有4個分組,遇到的左括號來計數

1. ((a)(b(c)))

2.(a)

3.(b(c))

4.(c)

分組回頭參考時在\加上分組計數,表示參考第幾個分組的比對結果

 

ex.(\d\d)\1 表示要輸入4個數字,前2後2必須相同 >>1212

 

建立pattern物件,可重複使用

regex = re,compile(‘正規表達’)

可以指定flags參數 例如不分大小寫

ex.regex = re.compile(r’dog’,re.ignorcase)

 

也可以嵌入旗標表示法embeded flag expression

re.ignorcase的是(?i)

以上寫法等同於regex = re.compile(r’(?i)dog’)

 

比對方法

finditer():傳回一個iterable物件,每次迭代都得到一個match物件,可以用group()來取得符合的字串

 

search():找第一個符合的字串

match():只會在字串開頭比對

findall():會以清單找到各分組

sub():做字串取代

 

121.os模組

取得目錄資訊

import os

取得目前工作目錄os.getcwd()

切換工作目錄os.chdir(‘路徑’)

取得指定目錄下檔案或目錄os.listdir()

或os.scandir()傳回iterable的os.direntry實例 (is_dir(),is_file()方法)

建立目錄os.mkdir()

對目錄改名os.rename()

移除目錄os.rmdir()

移除檔案os.remove()

路徑的合併 os.path.join() 自動判斷要用\或/來做路徑區隔

 

122.debugger指令

指定想除錯的檔案

>>>python –m pdb xxx.py

l:列出程式碼

b 行數:要下中斷點的地方 再執行c (continue)

p 變數名稱:查看變數

n :step over

s:step into

r:step out

b:查看中斷點

cl 中斷點號碼:清除中斷點

l start, end:指定顯示哪一行

unt: until 直接執行程式到指定行數

q:離開pdb

restart:重新運行

 

可以把pdb.set_trace()直接寫在原始碼中

用repl執行程式的時候,可以除錯
發生錯誤的時候可以用pdb.pm()回到上一步

 

123.測試工具

assert陳述

doctest模組:找尋類似互動環境的文字片段,執行並驗證程式

unittest模組:pyunit,junit的實現

第三方測試工具(nose,pytest)

124.assert

語法

assert_stmt ::=”assert” expression [“,” expression]

相當於

if __debug__ :

    if not expression: raise assertionerror

__debug__是內建變數,一般情況下會是true,如果執行需要最佳化時(python -o)則會是false

 

125.doctest

一方面是測試程式碼,也可以用來確認docstrings的內容沒有過期

只要撰寫repl形式的文件就可以了

 

用法:在’’’區塊中寫呼叫方法 跟結果

ex.

 

if __name__ == '__main__':

    import doctest

    doctest.testmod()

只有執行模組的時候會測試

 

要顯示細節可以用>>>python xxx.py –v

也可以用讀文字檔的形式

ex.

if __name__ == '__main__':

    import doctest

    doctest.testfile("util_doctest.txt")

 

126.unittest單元測試模組

或稱”pyunit” 是junit的python語言實現

 

要繼承unittest.testcase類別

單元測試要定義在test開頭的方法中

python執行的時候會自動找出方法並執行

ex.python –m unittest calc_test

 

名詞解釋

測試案例:測試的最小單元

測試設備:執行一個或多個測試前必要的預備資源,以及相關的清除資源動作

測試套件:一組測試案例、測試套件或兩者的結合

測試執行器:負責執行測試並提供測試結果的元件

 

 

如果有定義setup()方法>>執行每個test開頭方法前都會呼叫

如果有定義teardown()方法>>執行每個test開頭方法後都會呼叫

可以用來做每次單元測試前後的資源建立與銷毀

 

 

測試套件可以把不同測試案例組在一起

ex.

testcase中的某些測試方法

suite = unittest.testsuite()

suite.addtest(testcase(‘test_plus’))

suite.addtest(testcase(‘test_minus’))

 

或是用list

tests = [‘test_plus’, ‘test_minus’]

suite = unittest.ttestsuite(map(testcase, tests))

 

或是自動載入

suite = unittest.testloader().loadtestfromtestcase(xxx)

 

執行測試

suite = …….

unittest.texttestrunner(verbosity=2).run(suite)

verbosity可以顯示更詳細資料

 

127.timeit模組 測試效能

 

timeit.timeit(字串表示的程式片段,準備測試的材料)

會回傳執行秒數

 

效能是整體程式結合後的執行考量,並不是單一元素快慢的問題,也不是憑空猜測,而是要有實際的評測做依據

 

timeit預設是執行100萬次取平均,可以透過number參數控制

ex.

>>>timeit.timeit('strs=[str(n) for n in range(99)]', number = 10000)

>>> timeit.timeit('strs=(str(n) for n in range(99))', number = 10000)

>>> timeit.timeit('",".join([str(n) for n in range(99)])', number = 10000)

>>> timeit.timeit('",".join((str(n) for n in range(99)))', number = 10000)

 

128.執行緒模組

import threading

 

ex.threading.thread(target = 要執行的函式, args = (xx,))

target指定start()方法要執行的函式

args表示指定給函式的引數,使用tuple

 

必要的話可以繼承threading.thread,在__init__()呼叫super. __init__()

並在類別中定義run()方法實作執行緒功能,不過不建議這作法

 

python直譯器同時間只允許一個執行緒,所以不是真的平行處理

只是有時候切換速度快到感覺像同時處理

也就是目前的thread需要等待某個阻斷作業完成時,會先執行另一個執行緒

 

129.

執行緒適用的場合就是非計算密集的場合,與其等待阻斷作業完成不如先執行其他的

對於計算密集的任務,使用執行緒不見得會提高處理效率,反而因為必須切換執行緒耗費不必要的成本,使效率變差

 

130.如果一個thread被建立時,指定daemon參數是true,在所有非daemon執行緒都結束時程式就會直接終止

適用背景執行的任務

 

131.插入執行緒

可以用join(),會先等被加入的執行緒完成才會執行原本的

也可以加上時間指定最多處理幾秒

 

132.執行緒鎖定

threading.lock只有兩種狀態:鎖定與未鎖定

非鎖定狀態下可以用acquire()方法進入鎖定狀態

此時再呼叫就會被阻斷,直到其他地方呼叫release()使lock物件成為未鎖定

如果不是在鎖定狀態呼叫release()會拋出error

ex.

lock = threading.lock()

lock.acquire()

try:

程式區塊

finally:

        lock.release()

 

或是搭配情境管理器

with lock:

        程式區塊

 

其他鎖定機制

threading.rlock可重入鎖

可以重複呼叫同一個實例的acquire()方法

但是release()也要同樣次數才會釋放

 

133.condition

threading.condition()

透過aquire()取得鎖定後,需要在特定條件符合之前等待,可以呼叫wait()方法,會釋放鎖定,如果其他執行緒完成條件可以呼叫notify()方法,通知等待條件的thread可以取得鎖定,會從上次wait的地方繼續執行


notify()不能預期是哪個會被通知,或是可以呼叫notifyall(),會通知全部在等態的爭取鎖定

 

134.如果需要一進一出,在執行緒之間交換資料的方式,可以用queue.queue

建立實例可以指定容量

put():插入資料

get()取得資料

 

135.subprocess模組可以讓你在執行python的過程中產生新的子行程subprocess.run()

 

stdout=subprocess.pipe可以讓執行結果轉到程式內部

透過p.stdout讀取

ex.

p = subprocess.run(['python', 'hi.py'], input = b'justin\n', stdout = subprocess.pipe)

 p.stdout

 

run()底層是透過popen()實作的

會傳回一個popen()實例,可以透過communite()指定input

communite()回傳一個tuple,分別是標準輸出與錯誤的結果

 

 

136.想要以子行程執行函式,使用類似threading的api可以使用multiprocessing模組

 

multiprocessing.process()

也有target參數跟args

 

如果需要共享狀態,可以用multiprocessing.queue()

執行結果用put()方法置入

get()取得結果

 

multiprocessing 可以建立worker pool

利用pool實例派送任務,取得multiprocessing.pool.asyncresult實例,完成後取得結果

ex.

with multiprocessing.pool(2) as pool:

        #派送任務

        results = [pool.apply_async(foo, (filename,))

                       for filename in filenames]

        #取得結果

        count = sum(result.get() for result in results)

        print(count)

 

137.

一個物件可以被稱為描述器(descriptor)要有三個方法

def __get__(self, instance, owner):

def __set__(self, instance, value):

def __delete__(self, instance):

 

描述屬性的取得、設定、刪除要怎麼處理

 

三個方法都具備稱為資料描述器

若只有__get__()方法的稱為非資料描述器

 

資料描述器可以攔截對實例的屬性取得、設定跟刪除行為

非資料描述器是用來攔截透過實例取得類別屬性時的行為

 

138.定義__slots__

如果想控制可以指定給物件的屬性名稱,可以定義類別時指定__slots__

這屬性要是字串清單,列出可指定給物件的屬性名稱

 

這些屬性會存在類別的__dict__中,但要先指定值才能存取

但是要注意類別建構的實例不會有__dict__屬性

或是你自己把__dict__包含在__slots__中

這樣如果指定的屬性不在__slots__就會被放到__dict__裡面

 

__slots__裡面的屬性會被實作成描述器

最好被當成類別屬性來使用

父類別的__slots__只能透過父類取得

子類相同

 

139.取得屬性的順序

實例的__getattribute__()>資料描述器的__get__()>實例的__dict__>非資料描述器的__get__()>實例的__getattr__()

__getattribute__()會攔截所有對屬性的尋找

__getattr__()是作為尋找屬性的最後一個機會

 

140.設定屬性的順序

實例的__setattr__()>資料描述器的__set__()>實例的__dict__

__setattr__()會攔截所有對屬性的設定

 

141.刪除屬性的順序

實例的__delattr__()>資料描述器的__delete__()>實例的__dict__

 

142.函式裝飾器本質就是一個函式可接受函式並回傳函式

python的裝飾器可以用@標註

ex.

def sidedish1(meal):

    return lambda: meal() + 30

@sidedish1

def friedchicken():

    return 49.0

 

也可以堆疊裝飾器,會從下往上執行

 

如果裝飾器需要帶有參數,用來作為裝飾器的函式,必須先以指定的參數執行一次,傳回一個函式物件再來裝飾指定的函式

ex.

@deco(‘param’)

def func():

        pass

實際執行時

func = deco(‘param’)(func)

 

143.類別裝飾器

同函式裝飾器,但是標註在class上,接受類別並傳回類別

要注意裝飾器跟被裝飾的類別要定義相同方法

 

如果類別上定義__call__()方法,建構的實例可以用()傳入引數,此時會呼叫__call__()方法

ex.

class sidedish1:

    def __init__(self, func):

        self.func = func

    def __call__(self):

        return self.func() + 30

 

@sidedish1

def friedchicken():

    return 49.0

>>> friedchicken()

>>>79

 

144.方法裝飾器

可以對類別上的方法進行裝飾

選擇使用函式或類別來實作

方法的第一個參數總是類別的實例本身

 

 

145.物件是類別的實例,類別是type的實例,如果有方法能介入type建立實例與初始化的過程,就可以改變類別的行為>>meta類別

 

一個繼承type的類別可以作為meta類別

metaclass是個協定,python剖析完類別定義後會使用指定的metaclass來進來類別的建構與初始化

 

__call__()方法預測會呼叫__new__()跟__init__()方法

 

藉由metaclass = metaclass的協定,可在類別定義剖析完後繞送至指定的meta類別,可以定義meta類別的__new__()方法,決定類別如何建立,定義meta類別的__init__(),則可以決定類別如何初始,定義__call__()方法決定使用類別來建構物件時該如何進行物件的建立與初始

 

146.相對匯入套件

同個package有abc.py跟def模組

要在def匯入abc可以寫from . import abc

 

如果想在import pkg1 時直接使用pkg1.abc模組或套件中其他模組

可以在pkg1的__init__.py中寫

from . import abc