Introduction to Python Applications 2023 - Lecture 2 === ###### tags: `Python` `Python and Its Application 2023` ### Flow Control + [Flow chart](https://automatetheboringstuff.com/2e/chapter2/#calibre_link-1533) + Boolean Values (布林值、真假值) + `True` + `False` + 大小寫有差! + `true` and `false` are legal names for variables. + Comparison operators + `==` + `float` is not accurate. + `0.1 + 0.2 == 0.3` is `False` + `!=` + `<` + `<=` + `>` + `>=` + Compare values of different types + `string` versus `int` + `int` versus `float` + Truthy and falsey values (替用真值、替用假值) + Truthy values + Non-zero integers + Non-zero `float` + Non-empty `string` + Falsey values + `0` + `0.0` + `''` + The are more truthy and falsey values. + Boolean operators + `and` + `or` + `not` + Precedence: `not` > `and` > `or` + Note 1: the results of the above operations might not be one of `True` and `False`. They may result in truthy value and falsey value. + `5 and 3` is `3` + `'666' or 123` is `'666'` + `0.0 and 'xyz'` is `0.0` + Note 2: Shortcut + `5 or print("hello")` prints nothing + `0 or print("hello")` prints `hello` + `5 and print("hello")` prints `hello` + `0 and print("hello")` prints nothing + Conditions: a Boolean expression + Block of code [Colored structure](https://github.com/mzshieh/snp2017/raw/master/lecture%202%20--%20structure.docx) + Selection + `if` + `if`-`else` + `if`-`elif` + `if`-`elif`-`else` + example ```python3= x = int(input("請輸入成績(0~100) : ")) if x > 100 or x < 0: print('不要騙喔! OAO') elif x >= 90: print('Nice!!') elif x >= 60: print('Pass') else: print('好課值得一修再修3修重修畢業後繼續修') ``` + Iteration + `while` loop + Similar to `if` but go back to `while` for the next iteration after the loop body executed, not for the next statement. + `break` statement + Skip the following codes in the same block and end the loop. + Sample code 1 ```python3= ## Sample 1 while True: x = input('助教帥不帥? (y/n)') if x != 'y': print('你確定?再給你一次機會') else: break print('謝謝誇獎') ``` + Sample code 1b ```python3= ## Sample 1b cnt = 0 while cnt < 3: x = input('助教帥不帥? (y/n)') if x != 'y': print('你確定?再給你一次機會') cnt = cnt + 1 else: break if cnt >= 3: print('助教這麼不帥嗎? QQ') else: print('謝謝誇獎') ``` + `for` loop + `for item in bag:` is to pull the next `item` out of the `bag`. + `bag` can be a string, a tuple, a list, and other iterables. + `'a'`,`'b'`,`'c'` are items in the bag `'abc'`. + `x,y` are items in the bag `(x,y)`. + `1`,`2`,`987` are items in the bag `[1,2,987]`. + `0`,`1`,`2`, ..., `n-1` are items in the bag `range(n)`. + `m`,`m+1`,`m+2`, ..., `n-1` are items in the bag `range(m,n)`. + `continue` statement + Skip the following codes in the same block and start the next iteration. + Sample code 2 ```python3= ## Sample 2 print('數質數 1 ~ 20') prime = [2, 3, 5, 7, 11, 13, 17, 19] for num in range(1, 21): is_prime = False for p in prime: if num == p: is_prime = True break if is_prime == False: continue print(num) ``` + Function definition + `def`: define a custom function + `return` + `return x`: the value of the result is `x` + `return`: the value of the result is `None` + `None` 是一個 `NoneType` 型態的值,而 `NoneType` 只有 `None` 一種值,代表沒有。`None`是個 falsey value。 + Sample code 1 ```python3= # 只能算不太大的 x 且 x>1,精確度很差 def my_log(x): x = 1-1/x ret = 0 for n in range(1,1001): ret = ret + x**n/n return ret ``` + Sample code 2 ```python3= # 只能處理不太大的正整數 x 跟 y def my_gcd(x, y): ret = 1 for d in range(1,min(x,y)+1): if x % d == 0 and y % d == 0: ret = d return ret ``` + Sample code 3 ```python3= #========Function========# def findmax(arr): ans = arr[0] for tmp in arr: if ans < tmp: ans = tmp return ans #========Function========# array = [0, 2, 9, 3, 5, 6, 7] print(findmax(array)) ``` + Exception handling + `try`-`except` + example ```python3= x = input("做除法, 請輸入2個數字,用空白隔開\n").split() try: print(int(x[0]) / int(x[1])) except: print('除數不得為0') ``` + [Python 3 language reference](https://docs.python.org/3/reference/index.html) ### Debugger 參考資料:[Spyder Debugger Tutorial](https://hackmd.io/65mcTrq4Tji-HXfWIliRXw?view) 參考資料:[PyCharm Debugger Tutorial](https://hackmd.io/4uleh03ESLKi1T8ufdDj0A) ### 作業四 + Draw [Rokumonsen](https://www.google.com.tw/search?q=Rokumonsen) + Draw Ichimonsen (一文銭) + Draw Sanjurokumonsen (三十六文銭) ### 作業五 + Draw [US flag](https://www.google.com.tw/search?q=US%20Flag) + Draw [EU flag](https://www.google.com.tw/search?q=EU%20Flag) ### `pyautogui`: open your eyes! + Sample code 1 ```python3= import pyautogui, time def report(): pos = pyautogui.position() color = pyautogui.screenshot().getpixel(pos) print('Mouse position:',pos,'Color:',color) while True: report() time.sleep(0.1) ``` + `screenshot()` + Pixel + `screenshot().getpixel()` + `(R,G,B)` + `screenshot().im` is `iterable` + Pixels are balls, and `screenshot()` is a bag containing balls. + `screenshot().save('pic.png')` 存檔到 `pic.png`。 + 透過 `region` 來限縮搜尋範圍:`screenshot(region=(100,111,200,222)).save('pic.png')`可取得左上角座標 `(100,111)` 寬 `200` 高 `222` 的截圖。 + How to take a screen shot + Windows: print screen key + Hold alt, then press print screen + This gives you a picture of window on focus + Use Microsoft Paint to save the screenshot into png file + Mac: Ask TA + With code: ```python= import pyautogui, time for i in range(300): pyautogui.screenshot().save(f'pic-{i}.png') time.sleep(0.1) print(i+1, 'images are taken.') ``` + Sample code ```python3= import pyautogui, time pyautogui.FAILSAFE = True loc = pyautogui.locateOnScreen('box.png') # 偵測螢幕上是否有與 box.png 相同的圖片,若有 loc 將會被指派相同於圖片的地方的位置,若沒有 loc 會被指派為 None while loc == None: # 如果 loc 是 None (剛剛螢幕上沒找到相同的圖片) / 如果有找到,會直接跳過這個迴圈 time.sleep(0.1) # 停 0.1 秒 loc = pyautogui.locateOnScreen('box.png') # 再找一次 for loc_i in pyautogui.locateAllOnScreen('box.png'): print(loc_i) center = pyautogui.center(loc_i) pyautogui.click(center) ``` + `box.png` 應該長這樣:![](https://i.imgur.com/scpq06O.png) + ~~如果使用上有困難,請下載[snp.py](https://github.com/mzshieh/snp2018/blob/master/project/snp.py)到程式碼相同目錄,並把上面範例程式碼中第一行改為`import pyautogui, time, snp`,其餘的 `pyautogui.locate`... 改為 `snp.locate`...。~~ + `locateOnScreen(pic)` + 在螢幕上尋找與 `pic` 相同的圖片,若找到一個,則回傳位置(e.g. `Box(left=175, top=505, width=24, height=25)`)並停止尋找。 + 若都沒有找到,則回傳 `None`。([文件上](https://pyautogui.readthedocs.io/en/latest/screenshot.html#the-locate-functions)寫特定版本會給 Exception) + `locateAllOnScreen(pic)` + 在螢幕上尋找 **全部** 與 `pic` 相同的圖片,吐出一個`generator`(一種袋子,裝位置)。 + 可透過 `confidence` 來提供模糊比對,例如 `pyautogui.locateOnScreen('box.png',confidence=0.9)`可以理解成在畫面上找 9 成像的 `box.png` + 也可透過 `region` 來限縮搜尋範圍,例如 `pyautogui.locateOnScreen('box.png',region=(100,111,200,222))`可以理解成在畫面上,左上角 `(100,111)` 起,寬度 `200` 高度 `222` 的區域找 `box.png` 。 ### 作業六 Check all boxes! (In class assignment) Practice [here](https://goo.gl/forms/dr5mkE7Z9dKiJ3gI3) ### Issues on `screenshot` + `screenshot` can be slow + `locateOnScreen` can be very slow + Performance test: Save the following as `test.py` at your desktop. ```python3= import time import pyautogui from pyautogui import screenshot def report_time(): t = time.time() left, top, width, height = 5, 66, 77, 8 screenshot(region=(left,top,width,height)) print(time.time()-t) for i in range(100): report_time() ``` + Hard to recognize objects + [ToolKit manual](https://hackmd.io/s/HJs_LEC2z) + Replace the corresponding code in the previous sample code. ### Type + All values have their own types. + Use `type(v)` to check the type of value `v`. + All defined names (identifiers) have their values. + Use `type(name)` to check the type of `name`. ### List + A mutable sequence + Example: `[1,2,'c','D','EFG',print]` + Assignment: + Assign a variable to a list: `a_list = [1,234,567]` + We can reassign individual variable to a new value + `a_list[0] = 8` + `a_list[1] = '234567'` + Might be useful for doing your term project + Use list to maintain customers' orders, time remaining + Use list to track the BBQ sticks on the stove + Convinient to iterate: using `for`-loop ### Function + `def your_function():` + `return` + The function is terminated either by `return` or by the end of the block. + `None`: if there is no `return` or just `return` nothing + `def your_function_with_parameter(parameter)` + Argument ```python3= def square(x): return x**2 def square_root(x): return x**0.5 ``` + `def your_function_with_parameters(parameter1, parameter2)` + More arguments ```python3= def distance(x1,y1,x2,y2): delta_x = x1-x2 delta_y = y1-y2 dx2 = square(delta_x) dy2 = square(delta_y) return square_root(dx2+dy2) ``` + `def your_function_with_parameters(*args)` + Variable length list of arguments can be passed by `args` which is a `tuple`. + More flexible ```python3= def square_var_len(*x): print(type(x)) print(len(x)) ret = tuple() for v in x: ret = ret + (v**2,) return ret ``` + Access data via index `i`: `args[i]` + `def your_function_with_parameters(**kwargs)` + Keyword arguments + `print(some_str, end='')` + `print(some_str_1, some_str_2, sep=',')` + Access data via key string + `kwargs['end']` + `kwargs['sep']` + ```python3= def square_kw(x,**kwargs): if kwargs.get('test'): return 'test' return x**2 ``` + Please do not overwrite the arguments in your function now. We will discuss what will happen if you do so later. + Scope + Sample code ```python3= a_global_var = 'A global variable' def f(arg,*args,**kwargs): print(arg) print('args:',args) print('kwargs:',kwargs) a_local_var = 'A local variable' # global a_global_var # a_global_var = 5 print('a_local_var:',a_local_var) print('a_global_var:',a_global_var) f('hi','hi','ni',hihi='hihihi') print(a_global_var) # print(a_local_var) ``` + Variables are defined by assignment statements + Global + Defined in global scope + `global` + `global some_var`: `some_var` in this function is the global `some_var` + You must do this if you're going to write global variables + Local + Defined in a local scope + An invocation a function creates a scope, even two invocations of the same function create two different scope. + Principles + Local variables cannot be used globally. + A local scope cannot access local variables in other scopes. + A local variable and a global variable may have the same name, but only local variable can be accessed. + You may still read the global variable correctly if no local variable is using the same name. #### Task - Debug it! MZ wants to write a program which will report the color under the mouse pointer until a special color `(181, 71, 71)` is touched. But the following does not work, please fix it. ```python3= import pyautogui, time color = None def report(): loc = pyautogui.position() color = pyautogui.screenshot().getpixel(loc) print("position is:",loc,"color is",color) while True: report() time.sleep(0.5) if color == (181, 71, 71): break ``` There are several approaches to do this. Each of the following hints will leads to an answer. + Return value might be useful. + `global` ### Recursive calls + Sample code: ```python= def hanoi(n, from_peg, to_peg, buf_peg): if n == 0: return hanoi(n - 1, from_peg, buf_peg, to_peg) print('move the disc from', from_peg, 'to', to_peg) hanoi(n - 1, buf_peg, to_peg, from_peg) print('Moving 3 disks from A to B') hanoi(3, 'A', 'B', 'C') print('Moving 2 disks from A to C') hanoi(2, 'A', 'C', 'B') ``` ### Generator + Defined by a `def` block containing `yield` + Override `return` + Sample code ```python3= def squares(n): for i in range(n+1): yield i**2 print('squares is',type(squares)) print('squares() is',type(squares(5))) for i in squares(5): print(i) ``` + Sample code 2 ```python3= def f(): yield 1 yield 2 yield 3 return 5 for i in f(): print(i) ``` + Sample code 3 ```python3= def primes(n): if n < 2: return n_is_prime = True for i in primes(n-1): yield i if n % i == 0: n_is_prime = False if n_is_prime: yield n for i in primes(100): print(i) ```