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)
```