Scratch and Python 2018 Spring - Python Lecture 2 === ### Flow Control + [Flow chart](https://automatetheboringstuff.com/chapter2/#calibre_link-1903) + Boolean Values + `True` + `False` + `true` and `false` are legal names for variables. + Comparison operators + `==` + `float` is not accurate. + `!=` + `<` + `<=` + `>` + `>=` + 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: the results of the above operations might not be one of `True` and `False`. They may result in truthy value and falsey value. + 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('謝謝誇獎') ``` + `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 ```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) ### 作業四 + Draw [Rokumonsen](https://www.google.com.tw/search?q=Rokumonsen) + Draw Ichimonsen (一文銭) + Draw Sanjurokumonsen (三十六文銭) ### `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`。 + 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 + 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) ``` + `locateOnScreen(pic)` + 在螢幕上尋找與 `pic` 相同的圖片,若找到一個,則回傳位置(4-element `tuple` `(left,top,width,height)`)並停止尋找。 + 若都沒有找到,則回傳 `None`。 + `locateAllOnScreen(pic)` + 在螢幕上尋找 **全部** 與 `pic` 相同的圖片,吐出一個`generator`。 ### 作業五 Check all boxes! Practice [here](https://goo.gl/forms/dr5mkE7Z9dKiJ3gI3) ### Issues on `screenshot` + `screenshot` is slow + `locateOnScreen` is very slow + Hard to recognize objects ### Fast Screenshot Save the following code as `fastscreenshot.py` at your desktop. ```python3= import time from PIL import Image from mss import mss # Fast screenshot # mss 3.0 ver # Usage just like pyautogui # Parameter: # region: optional, four-integer tuple (left, top, width, height) def screenshot(region=None, **kwargs): im = None monitors = None if region == None: region = kwargs.get('region') with mss() as sct: # Region to capture monitor = sct.monitors[1] if region != None: monitor['left'] = int(region[0]) monitor['top'] = int(region[1]) monitor['width'] = int(region[2]) monitor['height'] = int(region[3]) # Get pixels on image sct_img = sct.grab(monitor) im = Image.frombytes('RGBA', sct_img.size, bytes(sct_img.raw), 'raw', 'BGRA') im = im.convert('RGB') return im # Evaluate screenshot time in seconds def evaluate_screenshot_time(region=None): t = time.time() im = screenshot(region) return time.time() - t ``` Then, save the following as `test.py` at your desktop. ```python3= import time import pyautogui from pyautogui import screenshot # from fastscreenshot 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() ``` If `fastscreenshot.py` does not work, use the following. ```python3= import time from PIL import Image from mss import mss # Fast screenshot # Usage just like pyautogui # Parameter: # region: optional, four-integer tuple (left, top, width, height) def screenshot(region=None,**kwargs): im = None monitors = None if region == None: region = kwargs.get('region') with mss() as sct: # Retrieve monitors informations: monitors = sct.enum_display_monitors() # Region to capture monitor = dict(monitors[1]) if region != None: monitor['left'] = int(region[0]) monitor['top'] = int(region[1]) monitor['width'] = (int(region[2]) // 80 + int(region[2] % 80 != 0)) * 80 monitor['height'] = int(region[3]) # Get pixels on image sct.get_pixels(monitor) im = Image.frombytes('RGB', (sct.width, sct.height), sct.image) if region != None: if monitor.get('width') != region[2]: im = im.crop((0, 0, region[2], region[3])) return im # Evaluate screenshot time in seconds def evaluate_screenshot_time(region=None): t = time.time() im = screenshot(region) return time.time() - t ``` ### Type + All values have their own types. + Use `type(v)` to check the type of value `v`. + All defined name (identifier) have their values. + Use `type(name)` to check the type of `name`. ### 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) local = 'A local variable' print('local:',local) print('a_global_var:',a_global_var) f('hi','hi','ni',hihi='hihihi') ``` + 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 local 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 `(139,0,0)` 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 == (139, 0, 0): break ``` There are several approaches to do this. Each of the following hints will leads to an answer. + Return value might be useful. + `global` ### 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) ```