---
# System prepended metadata

title: '[AIdrifter CS 浮生筆錄](https://hackmd.io/s/rypeUnYSb) <br>     NTU Adavanced Python Programming'

---

# [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 的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這clａssｓ內去找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的ＬＲＵ cache
:::