# Stanford Python程式課 - 第三週

## 課程第三週學習目標

• Lesson 7: Functions Revisited 複習Functions
• Lesson 8: Core Complete 完整核心
• Lesson 9: Images 影像處理
 Code in Place第三週課程 Lesson 7: Functions

## Lesson 7: Functions Revisited

• 如何定義function
• 透過參數輸入資料
• 回傳資料

### Functions are Like Toasters!

Function的運用是最容易被忽略的重要觀念，因為它影響程式的簡潔程度 (教授用語是elegent)、也決定能夠寫出複雜程式的能力！

Lecture 7-1: Recap and Show

Lecture 7-2: Toaster are Fns

Lecture 7-3: Anatomy of a Fn

Lecture 7-4: Many Examples

Lecture 7-5: Decomp Show

## Lesson 8: Core Complete完整核心

### 學習重點

• Trace control flow 追蹤控制流程
• Doctest 除錯驗證
• Practice practice practice 不斷練習
 Code in Place Lesson 8: Core Complete學習目標

Lecture 8-1: Intro

Lecture 8-2: Sentiment

Lecture 8-3: Factorial

Lecture 8-5: Doctests

Lecture 8-6: Rock Paper Scissors

### Lecture Examples

Sentiment Analysis程式

```import nltk.sentiment
analyzer = nltk.sentiment.SentimentIntensityAnalyzer()

def main():
while True:
user_text = input('? ')
score = get_sentiment(user_text)
reaction = get_reaction(score)
print(reaction)
print(score)
print('')

def get_reaction(score):
"""
Parameter score: a float between -1 and +1
Return: An emoji as a string!
"""
if score > 0.5:
return "🥰"
if score > 0:
return "🙂"
if score == 0:
return "😶"
if score < -0.5:
return "😢"
if score < 0:
return "😟"

def get_sentiment(user_text):
"""
Parameter user_text: any text (string)
Return: a sentiment score between -1 and +1 (float)
"""
# 1. pass the text into the analyzer.polarity_scores function, part of the nltk package
scores = analyzer.polarity_scores(user_text)
# 2. extract the sentiment score. Scores is a "dictionary" (covered on May 17th)
sentiment_score = scores['compound']

return sentiment_score

if __name__ == '__main__':
main()
```

Factorial程式

```# Constant – visible to all functions
MAX_NUM = 9

def main():
# repeat for several values!
for i in range(MAX_NUM+1):
print(i, factorial(i))

def factorial(n):
"""
Calculates n factorial.
5 factorial is 5 * 4 * 3 * 2 * 1
>>> factorial(6)
720
>>> factorial(5)
120
>>> factorial(4)
24
>>> factorial(3)
6
>>> factorial(1)
1
>>> factorial(0)
1
"""
for i in range(1,n+1):

if __name__ == '__main__':
main()
```

Doctest程式

```"""
Example of Doctest
"""

def main():
year = int(input("Enter a year to check leap year: "))
if is_leap_year(year):
print("Year "+str(year)+" is a leap year")
else:
print("Year "+str(year)+" is NOT a leap year")

def is_divisible(a, b):
"""
>>> is_divisible(20, 4)
True
>>> is_divisible(12, 7)
False
>>> is_divisible(10, 10)
True
"""
return a % b == 0

def is_leap_year(year):
"""
Returns Boolean indicating if given year is a leap year.
It is a leap year if the year is:
* divisible by 4, but not divisible by 100
OR
* divisible by 400
Doctests:
>>> is_leap_year(2001)
False
>>> is_leap_year(2020)
True
>>> is_leap_year(2000)
True
>>> is_leap_year(1900)
False
"""

# if the year is divisible by 400, it is a leap year!
if is_divisible(year, 400):
return True

# other wise its a leap year if its divisible by 4 and not 100
return is_divisible(year, 4) and not is_divisible(year, 100)

if __name__ == '__main__':
main()
```

## Lesson 9: Images

"It is no surprise then that it was this same class, that inspired Kevin Systrom and Mike Krieger, co-founders of Instagram , to develop image filters that are at the heart of every popular social media platform today."

Instagram其實是創辦人在CS106課程中學習了程式處理影像，才激發了創辦Instagram的動機！

### 學習重點

• 了解影像如何在程式中呈現
• 學習SimpleImage Library
• 撰寫操控影像的程式

Lecture 9-1: Intro

Lecture 9-2: SimpleImage

Lecture 9-3: Image Examples

Lecture 9-4: Greenscreen

Lecture 9-5: Mirror

Lecture 9-6: Wrap

Lecture 9-7: Responsibility

### 安裝Pillow、下載 simpleimage.py

1. 安裝Pillow library
2. 將 simpleimage.py 檔案放在PyCharm (IDE) 的project目錄內

### 延伸閱讀：從Stanford學習免費、但值一萬美元的程式設計課

 Pillow安裝方式，資料來源：Code in Place Lesson 9 Lecture Slides

simpleimage.py:

```#!/usr/bin/env python3

"""
Stanford CS106AP SimpleImage

Written by Nick Parlante, Sonja Johnson-Yu, and Nick Bowman.
-7/2019  version, has file reading, pix, foreach, hidden get/setpix

SimpleImage Features:
Create image:
image = SimpleImage.blank(400, 200)   # create new image of size
image = SimpleImage('foo.jpg')        # create from file

Access size
image.width, image.height

Get pixel at x,y
pix = image.get_pixel(x, y)
# pix is RGB tuple like (100, 200, 0)

Set pixel at x,y
image.set_pixel(x, y, pix)   # set data by tuple also

Get Pixel object at x,y
pixel = image.get_pixel(x, y)
pixel.red = 0
pixel.blue = 255

Show image on screen
image.show()

The main() function below demonstrates the above functions as a test.
"""

import sys
# If the following line fails, "Pillow" needs to be installed
from PIL import Image

def clamp(num):
"""
Return a "clamped" version of the given num,
converted to be an int limited to the range 0..255 for 1 byte.
"""
num = int(num)
if num < 0:
return 0
if num >= 256:
return 255
return num

class Pixel(object):
"""
A pixel at an x,y in a SimpleImage.
Supports set/get .red .green .blue
and get .x .y
"""
def __init__(self, image, x, y):
self.image = image
self._x = x
self._y = y

def __str__(self):
return 'r:' + str(self.red) + ' g:' + str(self.green) + ' b:' + str(self.blue)

# Pillow image stores each pixel color as a (red, green, blue) tuple.
# So the functions below have to unpack/repack the tuple to change anything.

@property
def red(self):
return self.image.px[self._x, self._y][0]

@red.setter
def red(self, value):
rgb = self.image.px[self._x, self._y]
self.image.px[self._x, self._y] = (clamp(value), rgb[1], rgb[2])

@property
def green(self):
return self.image.px[self._x, self._y][1]

@green.setter
def green(self, value):
rgb = self.image.px[self._x, self._y]
self.image.px[self._x, self._y] = (rgb[0], clamp(value), rgb[2])

@property
def blue(self):
return self.image.px[self._x, self._y][2]

@blue.setter
def blue(self, value):
rgb = self.image.px[self._x, self._y]
self.image.px[self._x, self._y] = (rgb[0], rgb[1], clamp(value))

@property
def x(self):
return self._x

@property
def y(self):
return self._y

# color tuples for background color names 'red' 'white' etc.
BACK_COLORS = {
'white': (255, 255, 255),
'black': (0, 0, 0),
'red': (255, 0, 0),
'green': (0, 255, 0),
'blue': (0, 0, 255),
}

class SimpleImage(object):
def __init__(self, filename, width=0, height=0, back_color=None):
"""
Create a new image. This case works: SimpleImage('foo.jpg')
To create a blank image use SimpleImage.blank(500, 300)
The other parameters here are for internal/experimental use.
"""
# Create pil_image either from file, or making blank
if filename:
self.pil_image = Image.open(filename).convert("RGB")
if self.pil_image.mode != 'RGB':
raise Exception('Image file is not RGB')
self._filename = filename  # hold onto
else:
if not back_color:
back_color = 'white'
color_tuple = BACK_COLORS[back_color]
if width == 0 or height == 0:
raise Exception('Creating blank image requires width/height but got {} {}'
.format(width, height))
self.pil_image = Image.new('RGB', (width, height), color_tuple)
size = self.pil_image.size
self._width = size[0]
self._height = size[1]
self.curr_x = 0
self.curr_y = 0

def __iter__(self):
return self

def __next__(self):
if self.curr_x < self.width and self.curr_y < self.height:
x = self.curr_x
y = self.curr_y
self.increment_curr_counters()
return Pixel(self, x, y)
else:
self.curr_x = 0
self.curr_y = 0
raise StopIteration()

def increment_curr_counters(self):
self.curr_x += 1
if self.curr_x == self.width:
self.curr_x = 0
self.curr_y += 1

@classmethod
def blank(cls, width, height, back_color=None):
"""Create a new blank image of the given width and height, optional back_color."""
return SimpleImage('', width, height, back_color=back_color)

@classmethod
def file(cls, filename):
"""Create a new image based on a file, alternative to raw constructor."""
return SimpleImage(filename)

@property
def width(self):
"""Width of image in pixels."""
return self._width

@property
def height(self):
"""Height of image in pixels."""
return self._height

def get_pixel(self, x, y):
"""
Returns a Pixel at the given x,y, suitable for getting/setting
.red .green .blue values.
"""
if x < 0 or x >= self._width or y < 0 or y >= self.height:
e = Exception('get_pixel bad coordinate x %d y %d (vs. image width %d height %d)' %
(x, y, self._width, self.height))
raise e
return Pixel(self, x, y)

def set_pixel(self, x, y, pixel):
if x < 0 or x >= self._width or y < 0 or y >= self.height:
e = Exception('set_pixel bad coordinate x %d y %d (vs. image width %d height %d)' %
(x, y, self._width, self.height))
raise e
self.px[x, y] = (pixel.red, pixel.green, pixel.blue)

def set_rgb(self, x, y, red, green, blue):
"""
Set the pixel at the given x,y to have
the given red/green/blue values without
requiring a separate pixel object.
"""
self.px[x, y] = (red, green, blue)

def _get_pix_(self, x, y):
"""Get pix RGB tuple (200, 100, 50) for the given x,y."""
return self.px[x, y]

def _set_pix_(self, x, y, pix):
"""Set the given pix RGB tuple into the image at the given x,y."""
self.px[x, y] = pix

def show(self):
"""Displays the image using an external utility."""
self.pil_image.show()

def make_as_big_as(self, image):
"""Resizes image to the shape of the given image"""
self.pil_image = self.pil_image.resize((image.width, image.height))
size = self.pil_image.size
self._width = size[0]
self._height = size[1]

def main():
"""
main() exercises the features as a test.
1. With 1 arg like flowers.jpg - opens it
2. With 0 args, creates a yellow square with
a green stripe at the right edge.
"""
args = sys.argv[1:]
if len(args) == 1:
image = SimpleImage.file(args[0])
image.show()
return

# Create yellow rectangle, using foreach iterator
image = SimpleImage.blank(400, 200)
for pixel in image:
pixel.red = 255
pixel.green = 255
pixel.blue = 0

# for pixel in image:
#     print(pixel)

# Set green stripe using pix access.
pix = image._get_pix_(0, 0)
green = (0, pix[1], 0)
for x in range(image.width - 10, image.width):
for y in range(image.height):
image._set_pix_(x, y, green)
image.show()

if __name__ == '__main__':
main()
```

### Lecture Examples

Imageexampless.py

```"""
This program contains several examples of functions that
manipulate an image to show how the SimpleImage library works.
"""

from simpleimage import SimpleImage

def darker(image):
"""
Makes image passed in darker by halving red, green, blue values.
Note: changes in image persist after function ends.
"""
# Demonstrate looping over all the pixels of an image,
# changing each pixel to be half its original intensity.
for pixel in image:
pixel.red = pixel.red // 2
pixel.green = pixel.green // 2
pixel.blue = pixel.blue // 2

def red_channel(filename):
"""
Reads image from file specified by filename.
Changes the image as follows:
For every pixel, set green and blue values to 0
yielding the red channel.
Return the changed image.
"""
image = SimpleImage(filename)
for pixel in image:
pixel.green = 0
pixel.blue = 0
return image

def compute_luminosity(red, green, blue):
"""
Calculates the luminosity of a pixel using NTSC formula
to weight red, green, and blue values appropriately.
"""
return (0.299 * red) + (0.587 * green) + (0.114 * blue)

def grayscale(filename):
"""
Reads image from file specified by filename.
Change the image to be grayscale using the NTSC
luminosity formula and return it.
"""
image = SimpleImage(filename)
for pixel in image:
luminosity = compute_luminosity(pixel.red, pixel.green, pixel.blue)
pixel.red = luminosity
pixel.green = luminosity
pixel.blue = luminosity
return image

def main():
"""
Run your desired image manipulation functions here.
You should store the return value (image) and then
call .show() to visualize the output of your program.
"""
flower = SimpleImage('flower.png')
flower.show()

darker(flower)
flower.show()

red_flower = red_channel('flower.png')
red_flower.show()

grayscale_flower = grayscale('flower.png')
grayscale_flower.show()

if __name__ == '__main__':
main()
```

greenscreen.py

```"""
This program shows an example of "greenscreening" (actually
"redscreening" in this case).  This is where we replace the
pixels of a certain color intensity in a particular channel
(here, we use red) with the pixels from another image.
"""

from simpleimage import SimpleImage

INTENSITY_THRESHOLD = 1.6

def redscreen(main_filename, back_filename):
"""
Implements the notion of "redscreening".  That is,
the image in the main_filename has its "sufficiently red"
pixels replaced with pixel from the corresponding x,y
location in the image in the file back_filename.
Returns the resulting "redscreened" image.
"""
image = SimpleImage(main_filename)
back = SimpleImage(back_filename)
for pixel in image:
average = (pixel.red + pixel.green + pixel.blue) // 3
# See if this pixel is "sufficiently" red
if pixel.red >= average * INTENSITY_THRESHOLD:
# If so, we get the corresponding pixel from the
# back image and overwrite the pixel in
# the main image with that from the back image.
x = pixel.x
y = pixel.y
image.set_pixel(x, y, back.get_pixel(x, y))
return image

def main():
"""
Run your desired image manipulation functions here.
You should store the return value (image) and then
call .show() to visualize the output of your program.
"""
original_stop = SimpleImage('stop.png')
original_stop.show()

original_leaves = SimpleImage('leaves.png')
original_leaves.show()

stop_leaves_replaced = redscreen('stop.png', 'leaves.png')
#    stop_leaves_replaced = redscreen(original_stop, original_leaves)
stop_leaves_replaced.show()

if __name__ == '__main__':
main()
```

mirror.py

```"""
File: mirror.py
---------------
This program shows an example of creating an image
that shows an original image and its mirror reflection
in a new image.
"""

from simpleimage import SimpleImage

def mirror_image(filename):
"""
Read an image from the file specified by filename.
Returns a new images that includes the original image
and its mirror reflection.
Returns the resulting "redscreened" image.
"""
image = SimpleImage(filename)
width = image.width
height = image.height

# Create new image to contain mirror reflection
mirror = SimpleImage.blank(width * 2, height)

for y in range(height):
for x in range(width):
pixel = image.get_pixel(x, y)
mirror.set_pixel(x, y, pixel)
mirror.set_pixel((width * 2) - (x + 1), y, pixel)
return mirror

def main():
"""
Run your desired image manipulation functions here.
You should store the return value (image) and then
call .show() to visualize the output of your program.
"""
original = SimpleImage('burrito.jpg')
original.show()

mirrored = mirror_image('burrito.jpg')
mirrored.show()

if __name__ == '__main__':
main()
```