# [AIdrifter CS 浮生筆錄](https://hackmd.io/s/rypeUnYSb) <br> NTU Adavanced Python Programming # 5/9 :::info 授課老師:李根逸 投影片版權接來自feis.studio Ref : https://feis.studio/ ::: :::warning 強烈推薦去台大上李根逸老師的課! https://feis.studio/#/ ::: ## 從hellow world開始 ### print可以印什麼? - print為built in function ![](https://i.imgur.com/qBaSWZC.png) ```python print(print) # <built-in function print> print(3+3) # 6 print('3' + '3') # 33 ``` ```python print(3 + '3')   # 型別錯誤 python有形態安全 ``` ### '' 和 "" 差異是? 單引號 可以包雙引號印 雙引號 可以包單引號印 或是單引號內 加上escape sequence(跳脫序列)`\`去印單引號(因為太醜 所以設計可以互包 Ref: https://docs.python.org/3/library/functions.html ## type, object and id ### type - type()可以用來查詢型別名稱 ```python print(type([5,8])) # <class 'list'> ``` ### object 會佔據記憶體空間的東西 ``` 3+7 一共會產生3個物件 10 , 7 ,3 ``` 整個python過程就會**一直產生新的物件** 所以有人會覺python記憶體消耗很大 ### id python內可以用`id()` 去檢查object編號 所以這兩個其實會是同一份object 這就是python的優化 基於python把資料設計成read only的前提 注意沒有名子的物件 執行完就死了 ```python print(id('3'+'2')) print(id('32')) ``` ## 變數賦值 python用**賦值運算(=)** 去 **binding**物件 ### bind a = 3 3 是物件 a是標籤 同一個物件 可以有多個標籤(名子) ```python a = '3' a = 8 a = print ``` ![](https://i.imgur.com/p9rUSD3.png) ### del 刪掉名子 如果該object沒人使用 python有自動回收機制 ```python del a print(a) ``` ### KeyWord 不能拿來當變數名稱的 ![](https://i.imgur.com/vWayk0s.png) ## 數值的運算與轉型 ### 真值測試 幾乎所有物件都可以做真值測試 - 非零為真 零為偽 - 非空為零 空為偽 ```python if bool(3 == int(True)) # 3 == True print true else print false ``` ```python if bool(print) print true else print false ``` ```python l = [] if l: print('not empty') else print('empty') ``` ![](https://i.imgur.com/E4ENJ2J.png) ## condition ### while else ### for **迭代型控制流程(iterable)** ```python for i in range(5) for i in [1,2,3] ``` 那要如何知道是不是iterable呢? ```python for i in iter('100') print(i) ``` ```python l = [1,2,3] for i in l i = 0; printf(l) # 1 2 3 ``` ```python l = [1,2,3] for i in range(length(l)) l[i] = 0; printf(l) # 0 0 0 ``` 如何複製一份呢? ```python l1 = [1,2,3] l2 = l1[:] l1[0] = 0 ``` ```python l1 = [1,2,3] l2 = l1 del l2 del l1 ``` 記住python內 **等號不是複製** ## 換行與對齊 ![](https://i.imgur.com/JrIU56g.png) ![](https://i.imgur.com/rxv8Ai9.png) ## function function只是一個名子 程式碼取個名子 用小括號呼叫(執行)他 ```python def hello(): print('hello!') print(hello) xxx = hello xxx() ``` 這邊是綁定外面的3 和 4 不是assign python沒有複製這種東西 所以改不到本尊 這只是新的bind( C++就不是如此 ```python def max2(a = 3, b = 4): if a > b : return a return b print(max2(3,4)) ``` python這邊要怎麼做交換呢? 會變 ```python a = 3 b = 5 print(a,b) # bind (a,b) = (b,a) print(a,b) ``` 不會變 因為只是x y 互換 但是 a b 還是沒變 ```python def swap(x,y): x, y = y, x a = 3 b = 5 # swap swap(a,b); print(a,b) ``` ```python def swap(x,y): x, y = y, x a = 3 b = 5 # swap b,a = swap(a,b); print(a,b) ``` # 5/10 python 萬物皆物件 python的變數比較像是C++的指標 ## 函式定義與呼叫 python物件回傳進去 回傳出來 但是和C++不同 不會複製物件 ### 不回傳的function - pass 表示其內容為空 - 語法限制無法真的什麼都不寫 ```python def f(): pass f() ``` ### type hint - function可以回傳object - 呼叫function後 可以得到其回傳的物件 ```python def max(a: int, b: int): if a>b return a return b print(max(3,5)) ``` ### 內建函式 ![](https://i.imgur.com/1o23eSF.png) ![](https://i.imgur.com/nkZw64O.png) ### 函式參數 ![](https://i.imgur.com/ZV1JDru.png) #### 順序型 / - 有給預設值 一定要放後面 放前面到一半就會compiler error ![](https://i.imgur.com/nWjYLo5.png) #### 具名型 * * 期待用名子去呼叫 ![](https://i.imgur.com/hfIAqoE.png) - 常見sort() ### python什麼都可以當字串印 print會把嘗試把內容轉str()去印 ```python print(str([3,4])) ``` repr()在打印字串 他會幫你家單引號 - repr() 獨一無二 - str() human readable ## 可視範圍 ### swap() ![](https://i.imgur.com/lITTbME.png) 要注意python是類似interpreter的語言 ![](https://i.imgur.com/ewd7DZO.png) ### globals() and locals() python有簡單可以知道local變數 與 global變數 其scope的方法 ```python a = 1 def f(x=2): y = 3 print("Globals in f: ", globals()) print("Locals in f: " , locals()) b = 4 f() ``` ```shell Globals in f: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fb8be543d90>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'main.py', '__cached__': None, 'a': 1, 'f': <function f at 0x7fb8be5473a0>, 'b': 4} Locals in f: {'x': 2, 'y': 3} ``` function有自己的local字典 因為是interpreter 有或沒有 是看執行順序 函式`f()`本身是全域的 因為那裡都可以呼叫 ![](https://i.imgur.com/gOkuNgN.png) 使用global關鍵字,可以讓python在local區域去global字典去拿東西 ![](https://i.imgur.com/9BOZjyI.png) ### Enclosing 在function內定義的function名稱只能在該function內使用 - 函數程式設計的基石 ![](https://i.imgur.com/mHLUP1l.png) - 讀取名稱代表物件跟賦值`(=)` 讓名稱綁定物件是兩種不一樣的規則 - 只能對目前可視範圍內的名稱進行綁定 但可以讀取上一層名稱的內容 ```python x = 3 def func_a(): x = 5 def func_b(): # x = 7 print('func_b', x) func_b() print('func_a:', x) func_a() print('global', x) ``` ```shell func_b 5 func_a: 5 global 3 ``` ### LEGB local -> enclosing -> globals -> built-in ## generator ### yield 如果function內有yield 則該function回傳值的型別為generator - 用在異步生成 **asynchronous** - `type(g)`為 `<class 'generator'>` ```python def f(): print("X") yield 1 print("Y") yield 2 g = f() # 產生一個物件 不是函式 print(next(g)) # 執行到yield 1 print(next(g)) # 執行到yield 2 print(next(g)) # run time error 因為yiled已經沒有了 g = f() # 產生一個物件 不是函式 print(next(g)) # 執行到yield 1 print(next(g)) # 執行到yield 2 ``` ### fibonacci - 一般版 ```python def fib(n): a, b = 1,1 print(a) print(b) k = 3 while k <= n: a, b = b, a+b print(b) k += 1 fib(10) ``` - generator版 ```python def fib(n): if n == 1: return a, b = 1, 1 yield a yield b k = 3 while k <= n: a, b = b, a+b yield b k += 1 for v in fib(10): print(v) ``` ### 用在跑range不需要額外記憶體空間 ![](https://i.imgur.com/MchX61l.png) ## class - create object "()" ``` (type) / \ \ \ (int) (float) (C) (NoneType) | | | | 3 3.2 x None ``` 沒有回傳值是"NoneType" ```python class C: pass x = C() print(type(x)) ``` ```python 3 print(type(3)) print(type(int)) ``` **讀取和等號是不一樣的邏輯** ![](https://i.imgur.com/hDPakFl.png) ## method p = Record('XD', 30) p.__init__('XD', 30) Record.init(p, 'XD', 30) # 5/16 三個 `'''`長字串 可以換行 ```python ''' mutiple line comment python .... ''' print(''' (\(\\ (-.-) o_(")(") ''') print(__doc__) ``` ![](https://i.imgur.com/fsrYigN.png) Ref: https://docs.python.org/zh-tw/3/reference/lexical_analysis.html#literals ## 4.1 類別物件的定義 python : 萬物皆物件 ```python def f(): return 3 def g(): yield 3 class c: pass a = 3 # a is int object # f is function object # c is class object print(type(a)) print(type(b)) print(type(c)) # Callable () f() # get return vaule c() # get instance object ``` ![](https://i.imgur.com/jeRRVG1.png) ![](https://i.imgur.com/vm5qmht.png) ### 讀取和寫入的差異 讀取:會往老爸找 設定:會加在自己身上 ![](https://i.imgur.com/vwMRZM0.png) ### 物件產生物件 `foo().bar` vs `foo.bar` ![](https://i.imgur.com/Uhp5ZSr.png) ### Method name 和 scope會被當作區域變數 函式寫在類別內 資料存在instance上面 ![](https://i.imgur.com/EkLghKw.png) ![](https://i.imgur.com/fti3Tp5.png) ### self function 呼叫`()` 一定要加self :::info 好難喔 再查一下吧 __init__ 一定要加self嗎? https://blog.csdn.net/SmallTankPy/article/details/54945135 ::: ![](https://i.imgur.com/ceWEP48.png) ![](https://i.imgur.com/i39bSgG.png) ### __init__ ![](https://i.imgur.com/PO7WElX.png) ![](https://i.imgur.com/LUHmsS1.png) ![](https://i.imgur.com/Wgi22pg.png) ### __del__ - `__del__` 是刪除名子 不是物件 ![](https://i.imgur.com/OBLM9by.png) ## 座標相加 - lhs: left hand side - rgs: right hand side ```python class Vector2(): def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f'({self.x}, {self.y})' def __add__(self, rhs): return Vector2(self.x +rhs.x, self.y + rhs.y) def length(self): return (self.x**2 + sefl.y**2)**0.5 def __abs__(self): return self.length() def __bool__(self): return self.x != 0 or self.y != 0 # ZeroDivisionError def normalize(self): len = self.length() self.x /= 1 self.y /= 1 def __iadd__(lhs, rhs): # 會省一個obejct 直接改原本物件 不會產生新的 self.x += rhs.x self.y += rhs.y # Vector2.__init__(pos1, 3, 4) pos1 = Vector2(3 ,4) pos2 = Vector2(7, 5) print(pos1) print(pos2) # print(str(pos1)) # print(pos1.__str__()) # print(Vector2.__str__(pos1)) pos3 = pos1 +pos2 # pos1.__add__(pos2) # Vector2.__add__(pos1, pos2) try: p.normalize() expect ZeroDivsionError pass print(p) ``` # 5/17 ## import - 用大家寫好的套件 - 內建函示有哪些啊? 怎麼知道__str__ ?? - Built-in Functions : https://docs.python.org/3/library/functions.html - python所有物件都是obejcts ### f-string PEP 498 帶來了 f-string,它的學名叫作 “Literal String Interpolation”。用法如下: ```python >>> def upper(s): ... return s.upper() ... >>> stock = 'tsmc' >>> close = 217.5 >>> f'{stock} price: {close}' 'tsmc price: 217.5' ``` ref: https://blog.louie.lu/2017/08/08/outdate-python-string-format-and-fstring/ ### 一般函式 vs 成員函式 希望object改變: 成員函式 希望obejct不改變 但是回傳改好的東西: 一般函式 python沒事不會用複製這個東西(搬移記憶體) 但是他有建構 ```python class Vector2 def __init__(self, x, y): self.x = x self.y = y # ZeroDivisionError def normalize(self): len = self.length() self.x /= 1 self.y /= 1 def __getitem__(self, i): if i == 0 : return self.x if i == 1 : return self.y def noramlized(v): l = v.length() return Vector2(v.x/l, v.y/l) v1 = Vector2(3, 4) # return new object, previous object doesn't change,we add + 'd' in tail print(normalized(v)) # we change object directly print(v1.normalize()) # print [] print(v[1]) print(v[0]) ``` ### Python 的等號 '=' - a, b是同一個物件 :::info 所有的等號都是複製ID 只有ID(很像C++的pointer ::: ```python a = 3 b = a print(a) print(b) ``` - 那如果要真的複製一份呢? ```python l1 = [1, 2, 3] l2 = l1.cpoy() l1[0] = 0 print(l1) print(l2) ``` ### 歡樂的for迴圈 - python如果function執行完 又沒東西 會狂噴none....(所以要raise IndexError當結尾) - `__getitem__` python有名的鴨子型別 ```python3 # 提供中括弧的能力 不然打印會噴一堆none def __getitem__(self, i) if i==0 : return self.x if i==1 : return self.y raise IndexError def __iter__(self) yield 0 yield 1 yield 2 v1 = Vector2(3, 4) # for n in iter(v1) for n in v1 print(n) # 3有在v1內嗎? __contains__ print(3 in v1) ``` ![](https://i.imgur.com/SYonBLj.png) ![](https://i.imgur.com/vZ2WJBz.png) ### sequence - python只有sequce和mapping - sequnce : 0 1 2 3 ... - mapping : 用鍵值去數 文件上看到`sequence` 代表支援getitem ![](https://i.imgur.com/1qBgOMI.png) ## tuple 元組 '()' - immutable sequence type (唯讀: read-only) ```python t = (3, 2, 1) print(type(t)) ``` - 語法上的先天限制 優先權的問題 ![](https://i.imgur.com/esPbKeL.png) - tuple 與 sequence 關係 ![](https://i.imgur.com/ibclEFK.png) ### iteralbe vs iterator - iterable: 可以呼叫`__iter__()`` - 有實作`__init__`和`__getitem__`即可 - iterator: iter()的回傳,要可以呼叫`__next__()` - 每一輪都是一個itrator - 用next next去走... - generator(yield)是一種iterator - 所有的sequence都有實作getitem - 所以他們都是iterable ```python l = [1, 2 , 3] g = list(l) print(next(g)) print(next(g)) print(next(g)) # v 是指向next回傳的 generator for v in l: print v ``` ### range ```python print(range(5)) print([range(5)]) print(list(range(5))) p = iter(range(5)) print(next(p)) print(next(p)) print(next(p)) print(next(p)) print(next(p)) for(i in range(5)) print(i) ``` ![](https://i.imgur.com/dyZ3Lym.png) ### iter of iter for in 會無條件對後面object呼叫iter() 為了讓iter自己也可以被for-in 只要是iterable的東西都可以丟到list去建構 ```python for v in l print(v) for v in iter(l) print(v) ``` ![](https://i.imgur.com/WGiMNkz.png) # 5/23 ## class vs function ```python class String(): def __init__(self, s): self.value = s def __str__(self): return self.value def Append(self, b): self.value += b return self # function # we need `assign` it(pointer to label) s = String('Hello') # class s.Append(' world').Append('!') print(s) ``` ### __getitem__ 讀取用`__getitem__ `寫入用`__setitem__` - for v in t ```python class MyTuple: def __init__(self, x, y, z): pass def __getitem__(self, i): pass def __str__(self): t = Mytuple ``` ### read only 一般tuple`()`和 字串`''` 都是read only `[]` we can write it - 唯讀通常都是為了效率 - 語法上的限制 ### sort() 可被`for in` 的就是itreable sort()給tuple回傳list , why ? ![](https://i.imgur.com/gCHr2WV.png) ### rversed(seq) 要可以數0, 1, 2, ,3 ![](https://i.imgur.com/BLWaxxW.png) ![](https://i.imgur.com/z3KgZmC.png) ```python t1 = (1, 2, 3, 4, 5, 6,) print(reversed(t)) ``` ## 寫得很python(潮) ### (右) tuple packing : 省略packing的小括弧 ```python # unpacking t = (a, b , c) (d, e, f) = t ``` ### (左) sequence unpacking - 右邊全部算完 才會給左邊 - 其實給值都是一次一個 - sequence unpacking其實等價於... ```python t = ('Pinkus', 60, 'D') k = iter(t) a = next(k) b = next(k) c = next(k) print(a) print(b) print(c) ``` 所以這樣結果是? ```python a = 0 a, a, a = a+1, a+2 ,a+3 print(a) ``` ```shell 3 ``` ### 單星號運算子Asterisk ```python # 一個名子 一個物件 t = (3, 4, 5) *a, b = t print(a) print(b) ``` ```python def avg(a, b, c): return (a+b+c)/3 t = 3, 4, 5 print(avg(*t)) ``` ```python def avg(*args): print(args) print(type(args)) print(avg(3, 4, 5)) ``` ```python def avg(arg1, *args): print(args) print(type(args)) print(3 , avg()) ``` ```python # failed sum(1, 2, 3) # iterable # we give tuple to work # we only give one object sum((1, 2, 3)) ``` max 有兩種版本 ```python max(iterable, *[, key, default]) max(arg1, arg2, *args[, key]) ``` ```python # ver1: max(iterable, *[, key, default]) # failed max(3) # 一定要加`,` 才會變tuple t = 3, max(t) s1 = 'Hello' s2 = 'World' print(max(s1)) print(max(s1, s2)) # ver2: max(arg1, arg2, *args[, key]) print(max(*s1, *s2)) print(max([s1, s2])) ``` ## import and module - 要怎麼用python做到類似C++ include hedaer動作? ### from 用`from ... import ...` 可以把模組內名稱匯入全域使用 - `globals()` 可以查全域名稱 - `dir()` 可以查屬性名稱 - ```python # from vecotr import * from vector import Vector v1 = Vector(3,4) print(v1) print(v1.x) print(abs(v1)) ``` ### dir() ![](https://i.imgur.com/VTUHZLD.png) ### __builtins__ #### dir ```python print(dir()) ``` ```shell ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__'] ``` #### __builtins__ ```python print(__builtins__) ``` ```shell <module 'builtins' (built-in)> ``` #### type(__bultins__) ```python print(type(__builtins__)) ``` ```shell <class 'module'> ``` #### dir(__builtins__) ``` python print(dir(__builtins__))# ``` ```shell ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'] ``` ### 怎麼看文件? https://docs.python.org/3/py-modindex.html ![](https://i.imgur.com/O0kICt8.png) ![](https://i.imgur.com/E4jsr8F.png) ![](https://i.imgur.com/FOpOlXl.png) ### 怎麼產生文件? - `help()`` ![](https://i.imgur.com/1N7xN3o.png) ![](https://i.imgur.com/yKRKlqy.png) # 5/24 ## 其他python開發環境 [Juipter note](https://jupyter.org/) - 可以嵌入圖片 表格 很多資料科學家喜歡用 [cpython](https://zh.wikipedia.org/wiki/CPython) - 有些人稱之為膠水語言 ### __main__ 主模組 - `import` 其實就是執行 - 匯入很多次 但是其實也只會執行一次 和C++ include很像 又稱singleton ```python import ggg print(__main__) print(__name__) print(ggg.__nam__) # 有時候我們會改變起點 會用這個方法 if __name__ == '__main__' print('XD') ``` - 其實一般標準庫 **都是用C寫的** 所以盡量用標準庫才快!! - 自己用python寫`max()`,一定不會比較快。 ![](https://i.imgur.com/7YTU4r7.png) ### tuple可以變大嗎? - iterable - 代表你其實一開始不知道元素個數 才需要一個一個讀 和sequence不一樣 ![](https://i.imgur.com/vQiTbNh.png) - 用python寫tuple其實不連續的,長得很像指標陣列(可以存字串,數字...等等),為了加速所以其實改用C語言,改成真正的連續記憶體間。 - 所以python一定要用內建的才快 ### HW3 find middle num ```python def min(a, b, c): result = sorted((a, b, c))[1] ``` ```python def min(a, b, c): t = (a, b, c) return ( sum(t) - max(t) - min(t) ) ``` ```python def mid(a, b, c): if a <= b <= c : return b if a <= c <= b : return c ... ... ... ``` ## 範圍與其常見操作 ### range - 會印兩輪嗎? ```python r = range(5) for i in r: print(i) for i in r: print(i) ``` ```python r = range(1, 10 ,2) print(r) print(tuple(r)) print(r[3]) print(len(r)) ``` ```python a , *b = range(5) print(b) ``` ![](https://i.imgur.com/jNmTMMJ.png) ![](https://i.imgur.com/1D86KdG.png) ### Def Range - 切記 有預設值的一定要放右邊 ```python class Range: def __init__(self, *args): # Range(5) if len(args) == 1: self.start = 0 self.stop = args[0] # Range(5, 10) elif len(args) == 2: self.start = args[0] self.stop = args[1] else: raise TypeError def __iter__(self): i = 0 while i < self.stop yield i i += 1 def __getitem__(self, i): #sequence 0, 1 ,2 ... if i + self.start >= self.stop raise IndexError return i + self.start for v in Range(5): print(v) for v in iter(Range(5)): print(v) for v in iter(Range(5, 10)): print(v) ``` ### slice [:]  dir可以看屬性 ```python v = slice(5) print(v.start) print(v.stop) print(v.step) ``` - slice 其實就是去產生一個物件 ```python t = (1, 2, 3) print(t[1]) # t.__getitem__(1) print(t[1:3]) # t.__getitem__(slice(1,3)) print(t[slice(1,3)]) ``` ![](https://i.imgur.com/fPgo61q.png) ### 省略符 ... ![](https://i.imgur.com/3OVXRMO.png) 可以吃tuple嗎? ![](https://i.imgur.com/hISezut.png) ## String - 四天王之一(tuple, range ...) - 吃object* - python 沒有字元char的概念,只有長度為1的字串 - 他其實每"格" 都是一個unicode阿 @皿@ ### ord - 可以用來打印unicode編號 ### chr ### string sequence ![](https://i.imgur.com/gN4MiDD.png) ### enumerate ```python s = '這是一段字串' for k in enumerate(s): print(k) for (i, c) in enumerate(s): print(i ,c) # index and s[index] for i, c in enumerate(s): print(i ,c) ``` ```python def Enumeration(iterable): p = iter(iterable) i = 0 try: while True: yield (i, next(p)) i += 1 expect: raise ``` ### 串接 - 為了效率 我們要用`join()` ```python t = ('T', 'h', 'e', '豬', '爸', '爸') print(''.join(t)) ``` ### 格式化 ![](https://i.imgur.com/As2JkUY.png) ## 列表[] list - tuple不能改內容,但是list可以。 - 他可以改變大小(tuple不行??) - 這邊不是dereference 其實還是拆包 ```python l = [1, 2, 3, 4, 5] print(reversed(l)) print(*reversed(l)) # 這邊不是dereference 其實還是拆包 ```python l.reverse() ``` ### append() ### extend() ### 切片賦值 ```python # [1, 2, 3, 4, 5] # [3, 4, 5] # [0, 0, 0, 0, 3, 4, 5] l = [1, 2, 3, 4, 5] l[0:2] = [0, 0, 0, 0] l = [1, 2, 3, 4, 5] l[2:3] = [0, 0, 0, 0] ``` # 5/30 ## Review ### Squence and Iterator ```python l = [3, 9, 1, 7, 6] # Sequence print(l[2]) # l.__getitem__(2) print(len(l)) # l.__len__() print(4 in l) # l.contaions__(4) # Iterator p = iter(l) print(next(p)) print(next(p)) print(next(p)) print(next(p)) print(next(p)) print(next(p)) # 和上面等價 for v in l: print(v) # Slice print(l[2]) # l.__getitem__(2) print(l[2:4]) # l.__getitem__(slice(2, 4)) print(l[2:4:1]) # l.__getitem__(slice(2, 4, 1)) # delete element form 2 to 3 # [2, 3) l[2:4] = [] # # 通常寫一行是比較快的 有機會優化 # l[2] = 0 # l[3] = 0 # l.insert(4, 0) # l.insert(5, 0) # # l = [1, 2, 3, 4, 5, 6 ,7] # l = [1, 2, 0, 0, 0, 0 ,5 ,6, 7 ] # l[2:4] = [0, 0, 0, 0] ``` - 優化 ```python a = 3 b = 5 # 不會有新物件 快 a += b # 會產生新物件 比較慢 a = a + b ``` ### * #### read(等號右邊 拆包) unpacking - iter, next, next, next ```python def f(): yield 1 yield 3 yield 5 print([*f, *f]) ``` ```python l = [3, 9, 1, 7 ,6] print(l) # Same print(*l) print(l[0], l[1], l[2], l[3], l[4]) print([*l]) ``` #### write(左邊 打包) packing - 讀的時候很彈性 但是寫的時候 只能寫在列表 ```python x = [1, 2, 1] y = [4, 5] *a, b = [*x, *y] print(a) # 1 2 1 4 ``` #### * 的限制 ```python def f(): yield 1 yield 3 yield 5 print(f) print(f()) print(*f()) print(tuple(f())) ``` ## functino 間參數傳遞 ![](https://i.imgur.com/NHnxEzl.png) ![](https://i.imgur.com/fyLFdxL.png) - 會幫你用getitem拿東西出來 所以值會變 ![](https://i.imgur.com/sfbc5iB.png) ![](https://i.imgur.com/7k7nfvJ.png) ## Generator - 有copy的負擔 然後l還會被改寫 ```python def addOne(l): n = l.copy() for i in range(len(n)): n[i] += 1 return n l = [3, 9, 1, 7, 5] for x in addOne(l): print(x) print(l) ``` - 用generator可以解決這個問題 ### generation expression ```python def addOne(l): # return (v+1 for v in l) for v in l: yield v+1 l = [3, 9, 1, 7, 5] for x in addOne(l): print(x) for x in (v+1 for v in l): print(x) # gerneration expression n = tuple[v+1 for v in l] n1 = [v+1 for v in l] print(n) print(n1) print(l) ``` ```python import random def even(l): for v in l: if v%2 == 0 yield v n = [v for v in l if v%2 == 0 and v<5] ``` ```python tuple(v for v in [3,9,1]) list(v for v in [3,9,1]) (*(v for v in [3,9,1]), ) [*(v for v in [3,9,1])] ``` ![](https://i.imgur.com/JUnm6e2.png) - python因為沒有括號 - 沒有像C++ 爽快幫忙修縮排的工具 ### list vs [] vs [*] ```python l = [range(5)] # 等價 m = list(range(5)) n = [*range(5)] print(l) print(m) print(n) ``` ## Hash Function 其實也可以實作 `__hash__` ```python print(hash("Mary")) print(hash("Mary")) print(hash("John")) ``` - collision - cpython實作有改良 ### dict 1. 來源寫死 2. 來源是iterable ![](https://i.imgur.com/EPCoO1L.png) ![](https://i.imgur.com/IdDkvvQ.png) - python的key 可以隨便取 ```pythpm m = { 'Mary': 60, 'Jphn': 90, (6,7): "129" } for k,v in m.items print(k , v) print(m[6,7]) ``` - python的hash其實用object的id去算的 - list不能當健值 因為list值可以改(mutable) - 會變得內容 不適合當健值 ```python print() ``` ![](https://i.imgur.com/AYzjef6.png) ![](https://i.imgur.com/zJhNMOG.png) ### 雙星號運算子 ![](https://i.imgur.com/gCwnj1Y.png) ```python def f(**kwargs): print(kwargs) f(y=1, x=2) ``` ```python def f(*args, **kwargs): print(args) print(kwargs) f(3, 4, y=1, x =2) ``` ![](https://i.imgur.com/c5IMq3m.png) # 5/31 每個語言都有自己的動機 有不同的生命週期 能力成長後 原本的工具不一定適合你 The Zen of Python python不是拿來設計大專案的程式語言 - 那些型態? ```python # ... 3000 lines print(type(a) ``` ## HW 5 ```shell __iter__ -> __getitem__ ``` for迴圈會先呼叫iter 所以__getitem__會被呼叫五次 tuple如果是`-1`就是倒過來數 ```python a = (1, 2 , 3, 4) print(a[-1]) # 4 print(a[1]) # 2 # 0 1 2 3 4 => Indexerror # Index Error <= -5 -4 -3 -2 -1 # final answer reurn _t[-1 - i] ``` ```python # Sequence [-len ~0 ~ len -1] s = '12' # str t = (1,2) # tuple l = [1,2, 'string', (1,2)] # list 可以多混 但是這樣就慢了 # Mapping [hashable] # 無法保證"順序" e = {'1', '2'} # set d = {'1':'1', '2':2} # dict for v in s:print(v) for v in t:print(v) for v in l:print(v) for v in e:print(v) for v in d:print(v) ``` 語法糖 ```python print(tuple(i for i in range(5))) print(list(i for i in range(5))) print([i for i in range(5)]) print(set(i for i in range(5))) print({i for i in range(5)}) ``` - generator is belong to iterator ```python ``` - forzenset - 一般set is mutable, we can not get hash from it ```python ``` ![](https://i.imgur.com/66TpnTw.png) ![](https://i.imgur.com/hRoRYiU.png) - python 通常會分會丟ERROR和不會丟error 的function - ![](https://i.imgur.com/N7Ff4Dp.png) ## 物件導向 ### dic 每個物件都有字典 去描述物件屬性(只有整數沒有 通常可以新增刪除的都有 ![](https://i.imgur.com/bB1Gphx.png) ### type class instance https://docs.google.com/presentation/d/e/2PACX-1vRZZlJVubAIV66tsLSmN2AADtINx0DnBnltR9Lh7lhZFpRDMpwmryAkHdNeiGUSMf8mgliBJozwSUtj/pub?slide=id.g8172421c63_0_311 ![](https://i.imgur.com/BxiPeCv.png) ![](https://i.imgur.com/JMtWZG4.png) ![](https://i.imgur.com/rPshXIR.png) ### Duck Typing 鴨子型別 - 類似C++的用法阿@@ :::info 只要有`draw()`,我就把他當`shape` 所以如果多function 就要全部都去寫ex: transalte() ::: - iterator就是可以支援next next next... ```python # class Rectangle: def __init__(self, x, y, w, h): self.x, self.y = x, y self.w, self.h = w, h def draw(self): print(f'[Rectangle]') print(f' x: {self.x}, y: {self.y}') print(f' w: {self.w}, h: {self.h}') class Triangle: def __init__(self, x, y, b, h): self.x, self.y = x, y self.b, self.h = b, h def draw(self): print(f'[Triangle]') print(f' x: {self.x}, y: {self.y}') print(f' b: {self.b}, h: {self.h}') r = Rectangle(0, 0, 50, 50) r.draw() t = Triangle(10, 10, 30, 40) t.draw() def draw_all(shapes): for s in shapes: s.draw() draw_all([r, t]) ``` ### 繼承 - 不想重複寫程式碼 不同class 作相同操作要怎麼做 - `__base__` ![](https://i.imgur.com/lFIWk0Y.png) ![](https://i.imgur.com/UnoIigB.png) - 世界是兩個維度 - object是所有人的爸爸 - 預設版本的`__getitem__` - `__init__` ... - 一路往右邊問看看function有沒有實作 ![](https://i.imgur.com/RO8x6yg.png) ```python class Record(object): pass print(Record.__str__) print(object.__str__) ``` :::info 要切記 讀和寫是不一樣的 ::: ![](https://i.imgur.com/JI30D34.png) ### 類別和實例的關係 對實例物件呼叫 等於對class 物件呼叫 ![](https://i.imgur.com/BKdoKoh.png) ![](https://i.imgur.com/syLAhwH.png) #### getattribute ![](https://i.imgur.com/K0v8Z7b.png) :::info type幫你找繼承關係(找爸爸這個動作 => 找"屬性"有沒有 oject內實作fucntion ::: ### 運用 ![](https://i.imgur.com/z1iynEd.png) # 6/6 ## 函數式程式設計 python沒有overloading `=` : 打包列表 `[]` `*` : tuple `()` class : 你可以做什麼事 functino() object : 你的資料實體 - duke typing 其實是很隱諱的 你根本不知道人家有沒有寫   要看才了才知道draw()有沒有實作 - C++ : generic - python: protovol ### 類別間的繼承 - ABC: Abstract Based Class - 這個東西很少人用 :::info object vs type vs inehritance ::: ## HW review ```python # Homework 6-1 class Range(): def __init__(self, *args): if len(args) == 1: args = 0, *args, # args (0 ,1) elif len(args) == 2: args = *args, 1 # args (1 ,5 ,1) self.start, self.stop, self.step = args ) def __len__(self): # 完成此函式 return (self.stop - se;f.start)//self.step print(len(Range(5))) print(len(Range(1, 5))) print(len(Range(1, 5, 2))) ``` ```python # Homework 6-2 class Range(): def __init__(self, *args): if len(args) == 1: args = 0, *args, 1 elif len(args) == 2: args = *args, 1 self.start, self.stop, self.step = args # 如果沒寫iter就會先call get item def __getitem__(slef, i): # 期待你用next() 去完成這件事 就是用yield def __iter__(self): # 試著完成這個函式 i = self.start if self.stop >0: while i < self.stop: yield i i += self.step else if self.stop <0: while i > self.stop: yield i i += self.step print(*Range(1, 5, 1)) print(*Range(5, 1, -1)) ``` ### cmp python怎麼做到類似c++ function pointer這件事 ![](https://i.imgur.com/MoyJO4U.png) ![](https://i.imgur.com/Ct9b0ZI.png) ```python r = list(range(10)) print(r) def f(x): if x % 2 == 0: return (True, -x) else return (False, x) print(sorted(r, key = f)) # fasle是0 他是小於true 1的 # python的sort是stable的 # tuple比較是兩個維度 先true 在 -x # [0, 1, 2 ,3 ,4 ,5, 6, 7, 8 ,9] # f: T F T F T F T F T F # 0 1 -2 3 -4 5 -6 7 # # # 一維如果一樣 在跳第二維 # if not (tuye, x) < (true ,- x) # ``` - 把它當字串比 ```python print( [1,2] > [1, 2, 3]) print( 'abc' > 'cba' ) ``` ### lambda ![](https://i.imgur.com/36wVb6X.png) ### map , filter, reduce functional比較容易平行 ![](https://i.imgur.com/ShNqkLz.png) ```python l1 = [('Taipei', 30), ('Tayuan', 40), ('Yieland', 50) ] print(l1) a = map(lambda x: x[1], l1) b = map(lambda x: -x, a) c = filter(lambda x: x < 30 ,b) # (只留True, 忽略false # 真正執行在l2 l2 = list(b) print(l2) ``` - 其實很少有人這樣寫 大家都用generator expression - reduce ![](https://i.imgur.com/g9trS4M.png) ![](https://i.imgur.com/4aiF33L.png) ```python ``` ### 讓function return function This is decorator 用visualize python看看 ![](https://i.imgur.com/cJt3WtX.png) ### 裝飾模式 processA()升級了!! 在原本process的前後多做了一些事情 ![](https://i.imgur.com/IUUM1wZ.png) ![](https://i.imgur.com/wkGsc9f.png) ```python def addOne(f): def func(): return f()+1 return func ''' f = addOne(f) f = addOne(f) f = addOne(f) f = addOne(f) f = addOne(f) ''' @addone @addone @addone @addone @addone def f(): return 0 print(f()) ``` # 6/7 ## HW ### HW7 Generator - 用for迴圈,產生列表 ```python for x in a: for y in b: if x == y: ans.append(x) return ans ``` - 用for迴圈,產生generator(並不是列表) - 資料沒有一次生出來 在next執行前 都沒有發動 - 用generator expression也是丟yiled回去(被包起來 看不到 ```python return (x for x in a for y in b if x == y) ``` 兩次交集,過程中不用產生真的列表。 要產生列表`[]` 請用單星號運算子 ```python for v in set_join(set_join(l1, l2), l3): print(v) ``` ```python return set(a) & set(b) ``` ### HW8 繼承 用繼承就不用寫translate 要注意self變數是誰,所以都要用super去呼叫老爸的init 如果把x 改成 xx會發生什麼事呢? ```python class Circle(Shape): def __init__(self, x, y, r) super().__init__(x,y) self.r = r def draw(self): print(f' {self.x} {self.y}') ``` ### HW9 套件 模組(Module): 一個檔案 套件(Package): 一個目錄 也可以是一個檔案 google的結果 有可能很高興寫完 發現版本不一樣 - 先google看一下範例 把範例的key word記起來 - 把randint()丟去文件庫找,例如有沒有包含end() - return a random interger N such that a <= N - 結果發現randomrange()沒有包括終點,randint()有 ## fcuntion回傳function物件 - Interpreter: 沒執行 要用print去呼叫f()瞬間 才會去執行f()的程式碼 - hello是區域變數 只會活在f()內 - 沒有東西 預設就是return NONE ```python def f(): def hello(): print("Hello World") return hello print(f()) print(f()()) ``` ```shell <function f.<locals>.hello at 0x7f1b61d12820> Hello World None ``` ```python def f(name): def hello(): print("Hello ", name) return hello # 丟Bob物件進去 拿到加了hello的Bob物件 hBob = f('Bob') hBob() hJohn = f('John') hJohn() ``` - 不想要改原本的fucntion功能,但是又想擴充它 - 這就是decaraotr ```python # 擴充功能 def john(func): def f(): func() print('john') return f # 原本程式碼在這 @john def hello(): print('hello') # 上架時 就把這一行拿掉 # hello = john(hello) hello() ``` 如果還想要給參數呢? ```python # 擴充功能 def john(func): def f(): func() print('john') return f # 原本程式碼在這 @john def hello(): print('hello') # 上架時 就把這一行拿掉 # hello = john(hello) hello() ``` ### __call__ - 其實`()` 也是一個特殊方法 - 這是`callable()`嗎? - C會去type這classs內去找call ```python calss C: pass # type.__call__(C) obj = C() ``` - 會失敗 因為obj所屬的class是C,class c 沒有實作`__call__` ```python calss C: pass # type.__call__(C) obj = C() obj() ``` ```python def f(name): def __init__(self, name): self.name = name def __call__(self) print("Hello ", self.name) # 丟Bob物件進去 拿到加了hello的Bob物件 hBob = f('Bob') hBob() hJohn = f('John') hJohn() ``` ![](https://i.imgur.com/73KsHNQ.png) ```python def f(name): cnt = 0 def hello(): nonlocal cnt # s要往上一層找 讀的時候會自動網上一層找 但是寫的時候不會 print('hello', name, cnt) cnt += 1 return hello # 丟Bob物件進去 拿到加了hello的Bob物件 hBob = f('Bob') # __init__ hBob() # __call__ hBob() # __call__ hBob() # __call__ hBob() # __call__ hBob() # __call__ hJohn = f('John') hJohn() hJohn() hJohn() hJohn() hJohn() ``` ## Closure 閉包 - 狀態存在這函式內 https://docs.google.com/presentation/d/e/2PACX-1vSP46uYkPNIGtJY2bkbzhqiUPxb1qELpNV_bBSZJmr7mWC9nmviLYl_icnXu3AdW72-mxAcN-mNcflq/pub?slide=id.g81b5307b85_0_93 用這技巧來加速 每次cache都是一份新的 ![](https://i.imgur.com/qIQBWPH.png) :::info - TenserFlow的`@tf.function` - 看看你是哪種function 去判斷他要怎麼優化 - python的LRU cache :::