Introduction to Python Applications 2025 - Lecture 2
===
###### tags: `Python` `Python and Its Application 2025`
### 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
參考資料:[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
def wait_pic(pic):
while True:
try:
# Check if there is an instance of pic
pyautogui.locateOnScreen(pic)
break
except: # If there is no such instance, it raises ImageNotFoundException
time.sleep(0.1) # wait 0.1 second
def click_all(pic):
for loc_i in pyautogui.locateAllOnScreen(pic):
print(loc_i)
center = pyautogui.center(loc_i)
pyautogui.click(center)
wait_pic('box.png')
click_all('box.png')
```
+ `box.png` 應該長這樣:
+ `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
### 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 nested lists to track the board cells.
+ `board = [[''] * 9 for _ in range(9)]`
+ 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)
```