早いものでもう第6週目に突入。待ちに待ったPythonの講義。講義の内容は、CとPythonの基本的な構文を比較しながら、C言語で一度書いたプログラムをPythonに書き換えながら進んでいく。そして、最後に応用編ということで、Pythonでは音声認識、画像認識、QRコードの生成など、非常に簡単にできてしまうことが紹介される。今回の講義だけで、Pythonが自由自在に使えるようにはならないが、C言語との比較で教わるため、何が違うのかというのが非常に分かりやすかった。

 

パイソンの基本(Python basics)

まずは、Pythonの基本的な記述方法をC言語と比較しながら。

hello.c -> hello.py

printf の代わりに print 。最後の ; セミコロンも必要ない。

printf("hello, world");
print("hello, world")

hello_name.c -> hello_name.py

CS50 library等のライブラリーからインポートする場合は、#include <cs50.h> ではなく、from cs50 import と書く。また、from cs50 import get_string のように、特定の関数を指定することも可能。また、複数の関数を同じライブラリーからインポートする場合は、from cs50 import get_string, get_int, get_float のように一行で書くこともできる。

 

C(hello_name.c)

// get_string and printf with %s
#include <cs50.h>
#include <stdio.h>
int main(void)
{
    string answer = get_string("What's your name? ");
    printf("hello, %sn", answer);
}

Python(hello_name.py)

# get_string and print, with concatenation
from cs50 import get_string
answer = get_string("What's your name? ")
print("hello, " + answer)

C言語では、%s のようにplaceholderが必要だったが、Pythonではそれが必要ない。上記の例以外にも、print(f"hello, {answer})f"..." と指定できる。

 

if 分などの条件分岐は、次のようになる。C言語のように、括弧を使う必要はなく、代わりに : コロンを最後に付け、次の行からは必ずインデントすることによって、条件分岐後のプログラムであることが分かる。また、else if ではなく、elif と書く。

if x < y:
    print("x is less than y")
elif x > y:
    print("x is greater than y")
else:
    print("x is equal to y")

Boolean(ブーリアン)型も記述が異なる。TrueFalse の先頭文字は大文字となる。

while True:
    print("hello, world")

ループwhile)の記述も異なる。

i = 0
while i < 3:
    print("hello, world")
    i += 1

ループfor)の記述も異なる。Pythonでは、予め指定したリストに入っている数字を実行できる。以下の構文では、初期値が i = 0 となり、次に、2つ目の要素の 1 が実行され、次に3つ目と続いていく。 

for i in [0, 1, 2]:
    print("cough")

Python独特の記述方法で、range 関数がある。例えば、次のように記述すると、range(3) は、012 を意味する。range() 違う引数を取ることもできる。Pythonの公式の文書にはを参照したらたくさんあることがわかる。

for i in range(3):
    print("cough")

例えば、range(0, 101, 2) は、0 から 100 (101は含まないため)まで、2 刻みで増えていく。i を出力したい場合も、print(i) のみと非常にシンプル。このように、パイソンでは多くの記述方法があるため、最も広く使われる方法は、Pythonic(パイソニック)などと表現される。

Python(パイソン)のデータ型には次がある。

  • bool(真偽値)、 True (真)or False (偽)
  • float(小数点)
  • int(整数)
  • str(文字列)
  • range (レンジ、範囲) 連続した数字。正しくはデータ型ではなく、class 型。
  • list (リスト) 一覧になったデータ。Cの配列に似ているが、配列のサイズを動的に変えられるところがPythonの特徴
  • tuple(タプル)XとY軸、経度と緯度のような整理されたデータ
  • dict (辞書)ハッシュ表に似たもの
  • set (セット)重複していないデータ

 

Examples

Pythonの便利なところは数多くのライブラリーが既に存在していて、C言語のようにLow levelなプログラムは意識せずに、High levelなプログラムに集中できるところである。

例えば、過去の課題である画像をぼかすというプログラムを書いたが、ピクセルごとの操作が必要で非常に複雑だった。でもそれが、Pythonだと次のようになる。ImageImageFilter とい関数を PIL ライブラリーから import すれば、画像をぼかす機能が使えるようになる。

from PIL import Image, ImageFilter
before = Image.open("bridge.bmp")
after = before.filter(ImageFilter.BoxBlur(1))
after.save("out.bmp")

Image.open で画像ファイルを開き、before 変数に代入。そのファイルに .filter 関数を使って、ボケ処理を加える。このとき、BoxBlur(1)(1) は、上下左右 1 ピクセルずつ参照してぼかすということ。ボケの度合いを増やしたい場合は、この数字を増やせば良い。

さらに、dictionary(辞書)は次のように作ることができる。

words = set()
def load(dictionary):
  file = open(dictionary, "r")
  for line in file:
      words.add(line.rstrip())
  file.close()
  return True
def check(word):
    if word.lower() in words:
        return True
    else:
        return False
def size():
    return len(words)
def unload():
    return True

まず、words という set 型を準備する。Pythonは、main 関数は必ず必要なわけではないので、いきなり関数を宣言することができる。 def load() で関数を宣言でき、今回は 引数に dictionary を、return には型を宣言する必要がないため何も記述していない。open でファイルを開き、for line in file: でファイルの中のすべての行を for 文の条件として実行できる。wordsset 型)というデータの一覧が入っているセット型に、add 関数でデータを追加し、最後に .rstrip 関数を使って改行を取り除く処理をする。

C言語のプログラムでは、既に辞書にデータが入っている場合は、check 関数を使って、確認する必要があった。Pythonでも同じだが、大文字小文字を揃えるために、全て小文字にするためには、 if word.lower() in words で統一することができる。

辞書に追加されたデータの数を知るためには、len(words)words に入っているデータの長さを知ることができ、結果が返される。

ただし、プログラムの実行スピードは、C言語の方がPythonで書いた上のプログラムより圧倒的に速い。何故か?C言語は、メモリのサイズを定義したり、配列処理をしたりをしていたが、Pythonにはそれをしない。Pythonは、その分Python言語をコンピュータが分かる言語に変換する必要があり、つまり翻訳が必要で、その分時間がかかることになる。

 

インプット、条件分岐(Input, conditions

  • CS50 library の get_int を使わずにユーザから input を求めるには、input 関数を使う
answer = input("What's your name? ")
print(f"hello, {answer}")
  • 2つの整数の足し算をするためには(コメントは、// ではなく、# で指定する。):
from cs50 import get_int
# Prompt user for x
x = get_int("x: ")
# Prompt user for y
y = get_int("y: ")
# Perform addition
print(x + y)
  • CS50 library を使わずに、input 関数を使った場合は、文字列として認識される:
# Prompt user for x
x = input("x: ")
# Prompt user for y
y = input("y: ")
# Perform addition
print(x + y)
  • よって、これを整数型に変換するためにはキャストを使う。が、もしユーザが整数を入力しなかった場合はエラー処理が必要なため、ライブラリーを使うことをオススメする。
# Prompt user for x
x = int(input("x: "))
# Prompt user for y
y = int(input("y: "))
# Perform addition
print(x + y)
  • 割り算をするには:
# Prompt user for x
x = int(input("x: "))
# Prompt user for y
y = int(input("y: "))
# Perform division
print(x / y)
  • 条件分岐文を表現するには:
from cs50 import get_int
x = get_int("x: ")
y = get_int("y: ")
if x < y:
    print("x is less than y")
elif x > y:
    print("x is greater than y")
else:
    print("x is equal to y")
  • 特定の関数ではなくCS50 libraryのすべてを import する場合は、プログラムは以下のように表現する。これは、CS50以外にもライブラリーを import していて、その中に get_int が存在した場合に、コンピュータがどちらを使って良いか判断できないからである。よって、cs50.get_int() と指定する必要がある。
import cs50
x = cs50.get_int("x: ")
y = cs50.get_int("y: ")
  • 文字列を比較するには == で可能:
from cs50 import get_string
s = get_string("Do you agree? ")
if s == "Y" or s == "y":
    print("Agreed.")
elif s == "N" or s == "n":
    print("Not agreed.")
  • if s.lower() in ["y", "yes"]: と宣言することも可能
from cs50 import get_string
s = get_string("Do you agree? ")
if s.lower() in ["y", "yes"]:
    print("Agreed.")
elif s.lower() in ["n", "no"]:
    print("Not agreed.")

 

ミャオmeow

C言語では、main 関数の宣言が必要だった、meow.cもPythonにかかれば次のようになる。

// Opportunity for better design
#include <stdio.h>
int main(void)
{
    printf("meown");
    printf("meown");
    printf("meown");
}
# Opportunity for better design
print("meow")
print("meow")
print("meow")

また、for 文を使った場合は次のようにループ文にできる。ここでもPythonは行数が2行で実現できる。

// Better design
#include <stdio.h>
int main(void)
{
    for (int i = 0; i < 3; i++)
    {
        printf("meown");
    }
}
# Better design
for i in range(3):
    print("meow")

meow 関数を作って呼び出すこともできる。

// Abstraction
#include <stdio.h>
void meow(void);
int main(void)
{
    for (int i = 0; i < 3; i++)
    {
        meow();
    }
}
// Meow once
void meow(void)
{
    printf("meown");
}

ここでは、main 関数がないと NameError: name 'meow' is not defined というエラーになってしまう。これは、プログラムは上から順番に処理されるからである。C言語では、Prototypeを使って、関数を先に宣言したが、Pythonにはそれがない。そのため、Pythonでは、一般的には main 関数を最初に宣言しプログラムの最後に main() を宣言する必要がある。これは、Pythonの独特なところだろうか。

# Abstraction
def main():
    for i in range(3):
        meow()
# Meow once
def meow():
    print("meow")
main()
// Abstraction with parameterization
#include <stdio.h>
void meow(int n);
int main(void)
{
    meow(3);
}
// Meow some number of times
void meow(int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("meown");
    }
}
# Abstraction with parameterization
def main():
    meow(3)
# Meow some number of times
def meow(n):
    for i in range(n):
        print("meow")
main()

 

get_positive_int

C言語で存在した do while loop が Python では存在しない。代わりに、無限ループ(while True:)を作り、条件に合致した場合は、break する。break する場所は、while 文と同じ場所(インデント)にある return 文となる。

from cs50 import get_int
def main():
    i = get_positive_int()
    print(i)
def get_positive_int():
    while True:
        n = get_int("Positive Integer: ")
        if n > 0:
            break
    return n
main()

マリオMario

横に並んだ?を4つ出力したい場合は、print 関数に第2引数を次のように(end="")指定する。また、最後の行は改行したいため、最後にprint() を指示する。Pythonの print 関数はデフォルトで改行されるのは、第2引数に end="n" となっているためであり、こういった処理が必要。

for i in range(4):
    print("?", end="")
print()

もしくは、ループを書く必要もなく、4を掛け算してあげれば、4回出力できる。

print("?" * 4)

ループの中にループを書くことも可能。

for i in range(3):
    for j in range(3):
        print("#", end="")
    print()

  

オーバーフロー(Overflow, imprecision

Pythonでは、自動的にメモリ領域を増やしていくため、整数型はoverflowしない(float ではする)。よって、次のプログラムは無限に実行される。

i = 1
while True:
    print(i)
    i *= 2

 

リスト、文字列(Lists, strings)

リストを作る際に予めサイズを宣言する必要はなく、score = [72, 73, 33] のように [] でデータを囲めば勝手に処理してくれる。

scores = [72, 73, 33]
print("Average: " + str(sum(scores) / len(scores))

Python では、sum 関数(合計)とlen 関数 (長さ)がデフォルトで用意されている。両方とも引数にリストを取る。最後に float 型を str 型にキャスト(変換)してあげれば、完了。

もしくは、float 型のまま出力をしたい場合は、 print 関数の中全部を f{] でFormatted stringに変更する。

print(f"Average: {sum(scores) / len(scores)}")

リストにデータを追加するためには、append を使って実装可能。

from cs50 import get_int
scores = []
for i in range(3):
    scores.append(get_int("Score: "))
...

文字列を全て大文字にするプログラムには、s.upper() で指定可能だが、全ての文字を一つずつ操作することもできる。for c in s で、文字列 S を一つずつ c でアクセスしている。

from cs50 import get_string
s = get_string("Before:  ")
print("After: ", end="")
for c in s:
    print(c.upper(), end="")
print()

 

コマンドライン引数、エグジット(Command-line arguments, exit codes

コマンドラインから複数の引数を取るためには、sys ライブラリーから argv をインポートし、引数の数に応じて処理を実行できる。argv はリストなので、argv[1] と指定すれば、2つ目の引数にアクセスできる。よって、次のプログラムは、コマンドラインに python argv.py David と入力すれば、hello, David と出力される。

from sys import argv
if len(argv) == 2:
    print(f"hello, {argv[1]}")
else:
    print("hello, world")

また、次のプログラムを実行したら、コマンドラインにに入力した引数すべてが出力される。

from sys import argv
for arg in argv:
    print(arg)

期待していた引数の数とは違う分だけ入力された場合の処理も可能。sys.exit() 関数は、main から出ることを意味し、エラーコード(番号)と共に exit する。

import sys
if len(sys.argv) != 2:
    print("missing command-line argument")
    sys.exit(1)
print(f"hello, {sys.argv[1]}")
sys.exit(0)

 

アルゴリズムAlgorithms

線形探索も非常に簡単に実装できる。

import sys
numbers = [4, 6, 8, 2, 7, 5, 0]
if 0 in numbers:
    print("Found")
    sys.exit(0)
print("Not found")
sys.exit(1)

if 0 in numbers: でリストの中身をすべて確認している。

文字列のリストも探索可能。

names = ["Bill", "Charlie", "Fred", "George", "Ginny", "Percy", "Ron"]
if "Ron" in names:
    print("Found")
else:
    print("Not found")

辞書型があれば、key に対応する value を返す。

from cs50 import get_string
people = {
    "Brian": "+1-617-495-1000",
    "David": "+1-949-468-2750"
}
name = get_string("Name: ")
if name in people:
    print(f"Number: {people[name]}")

まず、peoplekey の名前に対応する value 電話番号を保存している。次に、if name in people: で辞書の中身を名前で探索し、それに対する value である 電話番号を返す。 C言語の配列のように people[name] と指定できるが、[] の中身は数字でなくても文字列でも指定できる。

 

2つの数値を入れ替えることも簡単にできる。C言語では、ポインタを使って実装する必要があったが… 実はPythonでは、ユーザがポインタを使って操作できないようになっているため、このような機能が備わっている。

x = 1
y = 2
print(f"x is {x}, y is {y}")
x, y = y, x
print(f"x is {x}, y is {y}")

 

ファイルFiles

CSVファイルの操作をしてみよう。

import csv
from cs50 import get_string
file = open("phonebook.csv", "a")
name = get_string("Name: ")
number = get_string("Number: ")
writer = csv.writer(file)
writer.writerow([name, number])
file.close()

Python には、csv ライブラリーも用意されていて、ファイルを開けた後は、csv.writer 関数を呼び、writer.writerow を使って行を追加することができる。

with を使えば、データ追加が終了した後、自動的にファイルを閉じてくれる。

...
with open("phonebook.csv", "a") as file:
    writer = csv.writer(file)
    writer.writerow((name, number))

Google formを使って集めたハリーポッターのデータを CSV ファイルにダウンロードし、操作する例も紹介された。

import csv
houses = {
    "Gryffindor": 0,
    "Hufflepuff": 0,
    "Ravenclaw": 0,
    "Slytherin": 0
}
with open("Sorting Hat (Responses) - Form Responses 1.csv", "r") as file:
    reader = csv.reader(file)
    next(reader)
    for row in reader:
        house = row[1]
        houses[house] += 1
for house in houses:
    print(f"{house}: {houses[house]}")

CSV ライブラリーに用意されている reader 関数を使い、また、最初の行はヘッダーなので、 next (reader) を使ってスキップする。2つ目の列に入っているデータ row[1] にアクセスし、house に代入し、辞書 houses にデータを追加していく。

 

その他のライブラリー(More libraries

文字の読みおこし(Text to speech)

speechtotext

ここから更にPython がパワフルになっていく。

import pyttsx3
engine = pyttsx3.init()
engine.say("hello, world")
engine.runAndWait()

engine.say(f"hello, {name}") と指定してあげればユーザが入力した名前を読み上げてくれる。

import pyttsx3
engine = pyttsx3.init()
name = input("What's your name? ")
engine.say(f"hello, {name}")
engine.runAndWait()

 

顔認証(Facial recognition)

faceid

Pythonには、顔認証ライブラリー face_recognition も用意されており、簡単に使うことができる。

# Find faces in picture
# https://github.com/ageitgey/face_recognition/blob/master/examples/find_faces_in_picture.py
from PIL import Image
import face_recognition
# Load the jpg file into a numpy array
image = face_recognition.load_image_file("office.jpg")
# Find all the faces in the image using the default HOG-based model.
# This method is fairly accurate, but not as accurate as the CNN model and not GPU accelerated.
# See also: find_faces_in_picture_cnn.py
face_locations = face_recognition.face_locations(image)
for face_location in face_locations:
    # Print the location of each face in this image
    top, right, bottom, left = face_location
    # You can access the actual face itself like this:
    face_image = image[top:bottom, left:right]
    pil_image = Image.fromarray(face_image)
    pil_image.show()

 

QRコードを作る

qrcode

QRコードを生成するのも数行のコードで実装できる。qrcode ライブラリー

import os
import qrcode
img = qrcode.make("https://youtu.be/oHg5SJYRHA0")
img.save("qr.png", "PNG")
os.system("open qr.png")

 

音声認識Speech recognition

speechtotext

speech_recognition ライブラリーを使えば、マイクに話した内容をテキストに変換してくれる。

import speech_recognition
# Obtain audio from the microphone
recognizer = speech_recognition.Recognizer()
with speech_recognition.Microphone() as source:
    print("Say something:")
    audio = recognizer.listen(source)
# Recognize speech using Google Speech Recognition
print("You said:")
print(recognizer.recognize_google(audio))

これを少し応用すれば、会話をすることもできる。

...
words = recognizer.recognize_google(audio)
# Respond to speech
if "hello" in words:
    print("Hello to you too!")
elif "how are you" in words:
    print("I am well, thanks!")
elif "goodbye" in words:
    print("Goodbye to you too!")
else:
    print("Huh?")

 

感想

AI

Artificial Intelligence (AI・人工知能)技術の需要がこれからもどんどん増えていくなかで、人工知能のプログラムを簡単に実装できるPythonを学ぶことはもはや必要不可欠に感じる。今回の講義では、Pythonの触りだけだったので、もう少し深堀りできる講義も受けてみようと思う。