﻿GeminiとはじめるPythonプログラミング




リスト1-1: メッセージを表示する


print("Hello, Colaboratory!")


リスト2-1: 整数を表示する


print(123)
print(-45)
print(0)


リスト2-2: 浮動小数点数を表示する


print(3.14)
print(-0.05)




リスト2-3: 文字列を表示する


print("こんにちは、Pythonの世界へ！")
print('今日の天気は晴れです。')


リスト2-4: 引用符を忘れた場合


print(こんにちは)


リスト2-5: 変数に値を代入して利用する


# "name"という名前の変数に、文字列"鈴木さん"を代入
name = "鈴木さん"


# "age"という名前の変数に、数値28を代入
age = 28


# 変数の中身を表示
print(name)
print(age)


リスト2-6: 変数の値を上書きする


# xという変数に10を代入
x = 10
print(x)


# xに25を再代入（中身が上書きされる）
x = 25
print(x)


リスト2-7: 算術演算子を使った計算


# 足し算
print(10 + 5)


# 引き算
print(100 - 35)


# 掛け算と割り算
price = 120
count = 3
print(price * count) # 120 × 3
print(price / 10)  # 120 ÷ 10


リスト2-8: 少し特殊な計算


# 7を3で割った商（切り捨て）
print(7 // 3)


# 7を3で割った余り
print(7 % 3)


# 3の2乗
print(3 ** 2)


リスト2-9: 文字列を連結する


last_name = "山田"
first_name = "太郎"


# + で文字列を連結
full_name = last_name + first_name
print(full_name)


リスト2-10: 変数を繰り返し利用する


player_name = "勇者アルス"


print(player_name + "は、旅に出た。")
print(player_name + "は、スライムと遭遇した！")
print("おや？ " + player_name + "の様子が……？")


リスト2-11: 数値と文字列を + でつなぐ（エラーになります）


age = 30
message = "私の年齢は" + age + "歳です。"
print(message)


リスト2-12: str() を使って数値を文字列に変換する


age = 30


# str(age)で、数値の30を文字列の"30"に変換
message = "私の年齢は" + str(age) + "歳です。"
print(message)


リスト2-13: Geminiが生成するコード例


# 名前、年齢、出身地を変数に代入
name = "山田太郎"
age = 35
origin = "東京"


# 自己紹介文を作成して表示
introduction = "私の名前は" + name + "です。年齢は" + str(age) + \
  "歳で、出身は" + origin + "です。"
print(introduction)


リスト2-14: フォーマット文字列を利用するコード例


# 名前、年齢、出身地を変数に代入
name = "山田太郎"
age = 35
origin = "東京"


# 自己紹介文を作成して表示
introduction = f"私の名前は{name}です。年齢は{age}歳で、出身は{origin}です。"
print(introduction)


リスト2-15: if文の簡単な例


age = 20


# もし、ageが18以上ならば
if age >= 18:
  print("あなたは成人です。")
  print("選挙権があります。")


print("プログラムを終了します。")


リスト2-16: フォームを利用する


age = 15 # @param {"type":"integer"}


if age >= 18:
  print("あなたは成人です。")
  print("選挙権があります。")


print("プログラムを終了します。")


リスト2-17: if-else文の例
`
score = 75 # @param {type:"integer"}


# もし、scoreが80点以上ならば
if score >= 80:
  print("合格です！おめでとうございます！")
# そうでなければ
else:
  print("残念ながら不合格です。")


print("採点を終了します。")


リスト2-18: elifの利用例


time = 14 # @param {type:"integer"}


if time < 12:
  print("おはようございます")
elif time < 18:
  print("こんにちは")
else:
  print("こんばんは")


リスト2-19: and を使った条件分岐


# 総授業日数
total_classes = 30
# 出席日数
attendance_days = 25 # @param {type:"integer"}


# テストの点数
score = 70 # @param {type:"integer"}


# 出席率を計算
attendance_rate = attendance_days / total_classes


# 出席率が0.8以上、かつ、点数が60点以上か判定
if attendance_rate >= 0.8 and score >= 60:
  print("合格です。単位を認定します。")
else:
  print("不合格です。")


リスト2-20: or を使った条件分岐


# 曜日（0:日, 1:月, ... 6:土）
day_of_week = 6 # @param {"type":"slider","min":0,"max":6,"step":1}


# 天気
weather = "雨" # @param ["晴","雨"]


# 土曜日または日曜日か？
is_weekend = (day_of_week == 6 or day_of_week == 0)


# 休日か、または雨が降っているか？
if is_weekend or weather == "雨":
  print("今日は家でゆっくりしましょう。")
else:
  print("お出かけ日和ですね！")


リスト2-21: 年齢によって料金が変わる例


# ユーザーに年齢を入力してもらう
input_age = input("あなたの年齢を入力してください: ")


# 入力された文字列を数値（整数）に変換
age = int(input_age)


# 料金を格納する変数
fee = 0


# if-elif-elseで料金を判定
if age <= 3:
  fee = 0
elif age <= 12:
  fee = 500
elif age <= 17:
  fee = 1000
else:
  fee = 2000


# 結果を表示
if fee == 0:
  print("あなたの入場料金は無料です。")
else:
  print(f"あなたの入場料金は{fee}円です。")


リスト2-22: for文とrange()の基本的な使い方


# 0から4まで、5回繰り返す
for i in range(5):
  print("こんにちは！")


リスト2-23: ループ変数を処理で利用する


print("カウントダウンを開始します！")
for i in range(5, 0, -1): # 5から1まで1ずつ減らす
  print(i)


print("発射！")


リスト2-24: 1から10までの合計を計算する


# 合計を保存しておくための変数
total = 0


# 1から10までの数値を順番に取り出す
for number in range(1, 11): # 1から10まで繰り返す
  total = total + number # totalにnumberを足して上書き


print("1から10までの合計は、", total, "です。")


リスト2-25: while文の簡単な例


# 繰り返しの回数を数えるための変数
count = 1


# countが5以下の間、処理を繰り返す
while count <= 5:
  print(f"{count}回目のループです。")
  count = count + 1 # countの値を1増やす（重要！）


print("ループが終了しました。")


リスト2-26: 簡単な数当てゲーム


# 答えの値を用意
answer = 6


# ユーザーの推測を保存する変数
guess = 0


print("数当てゲーム！ 1から10の数字を当ててね。")


# 答えと推測が違う間、ループを続ける
while guess != answer:
  input_text = input("あなたの予想は？: ")
  guess = int(input_text)


  if guess != answer:
    if guess < answer:
      print("残念、はずれ！ もっと大きいです。")
    else:
      print("残念、はずれ！ もっと小さいです。")


# ループを抜けたら正解したということ
print("おめでとう！ 正解です！")


リスト2-27: breakの例


# 探したい数字
target_number = 7 # @param {type:"integer"}


print(f"{target_number}を探します...")


# 1から100までの数字を調べる
for i in range(1, 101):
  print(i, end=", ") # end=", "は改行しないための指定
  if i == target_number:
    print(f"\n{target_number}を見つけました！")
    break # ループを中断


print("探索を終了します。")


リスト2-28: continueの例


print("1から10のうち、3の倍数以外を表示します。")


for i in range(1, 11):
  # もし i が3で割り切れるなら
  if i % 3 == 0:
    continue # 処理をスキップして次のループへ


  print(i)


リスト2-29: 九九を一覧表示する例


# 1の段から9の段までを繰り返す（外側のループ）
for i in range(1, 10):
  print(f"--- {i}の段 ---")
  # 各段で、1から9までを掛ける（内側のループ）
  for j in range(1, 10):
    result = i * j
    # f-stringを使って表示を整える
    print(f"{i} x {j} = {result:2d}") # :2dは2桁で揃えるための指定
  print() # 段の終わりに改行を入れる


リスト2-30: 最もシンプルな関数の例


# あいさつを表示する関数を定義する
def say_hello():
  print("--------------------")
  print("こんにちは！")
  print("Pythonの世界へようこそ。")
  print("--------------------")


# 関数を呼び出して実行する
print("プログラムを開始します。")
say_hello()
print("プログラムを終了します。")


リスト2-31: 関数を複数回呼び出す


# 上で定義したsay_hello()を再度利用
say_hello()
say_hello()


リスト2-32: 引数を持つ関数の例


# nameという引数を受け取る関数を定義
def greet(name):
  print(f"こんにちは、{name}さん！")


# 関数を呼び出すときに、引数として具体的な名前を渡す
greet("鈴木")
greet("佐藤")


リスト2-33: 複数の引数を持つ関数


# 四角形の面積を計算する関数
# widthとheightの2つの引数を受け取る
def calculate_rectangle_area(width, height):
  area = width * height
  print(f"幅{width}、高さ{height}の四角形の面積は{area}です。")


calculate_rectangle_area(10, 5)
calculate_rectangle_area(7, 3)


リスト2-34: 戻り値を持つ関数の例


# 税込価格を計算して「返す」関数
def calculate_tax_included_price(price):
  tax_rate = 0.1
  tax_included = price * (1 + tax_rate)
  return tax_included # 計算結果を返す


# 関数を呼び出し、戻り値を変数に代入する
item_a_price = calculate_tax_included_price(1000)
item_b_price = calculate_tax_included_price(350)


print(f"A商品の税込価格は {int(item_a_price)} 円です。")
print(f"B商品の税込価格は {int(item_b_price)} 円です。")


total_price = item_a_price + item_b_price
print(f"合計金額は {int(total_price)} 円です。")


リスト2-35: BMIを計算する関数


def calculate_bmi(height_cm, weight_kg):
  """
  身長(cm)と体重(kg)からBMIを計算して返す関数。
  """
  # 身長をcmからmに変換
  height_m = height_cm / 100


  # BMIを計算
  # 身長が0だとエラーになるのを防ぐ
  if height_m == 0:
    return 0


  bmi = weight_kg / (height_m ** 2)


  # 計算結果を小数点以下第2位で四捨五入
  return round(bmi, 2)


リスト2-36: BMI関数を利用する


# 身長と体重を入力する
height = 170 # @param {type:"integer"}
weight = 60 # @param {type:"integer"}
# calculate_bmi関数を呼び出す
bmi = calculate_bmi(height, weight)


# 結果を表示する
print(f"身長: {height}cm、体重: {weight}kgの人のBMI値は、{bmi} です。") 


リスト3-1: リストを作成する


# 曜日のリスト
weekdays = ["月", "火", "水", "木", "金"]


# テストの点数のリスト
scores = [85, 92, 78, 65, 88]


# 空のリスト
empty_list = []


print(weekdays)
print(scores)
print(empty_list)


リスト3-2: インデックスを使って要素を取り出す


scores = [85, 92, 78, 65, 88]
#         [0] [1] [2] [3] [4] ← インデックス番号


# 最初の要素（インデックス0）を取り出す
first_score = scores[0]
print(f"最初の点数は{first_score}点です。")


# 3番目の要素（インデックス2）を取り出す
third_score = scores[2]
print(f"3番目の点数は{third_score}点です。")


リスト3-3: リストの要素を書き換える


scores = [85, 92, 78, 65, 88]
print("変更前:", scores)


# 4番目の要素（インデックス3）を70に書き換える
scores[3] = 70
print("変更後:", scores)


リスト3-4: appendの利用例


shopping_list = ["にんじん", "たまねぎ"]
print("追加前:", shopping_list)


# "じゃがいも"をリストの末尾に追加
shopping_list.append("じゃがいも")
print("追加後:", shopping_list)


リスト3-5: delの利用例


weekdays = ["月", "火", "水", "木", "金"]
print("削除前:", weekdays)


# 3番目の要素（インデックス2）の"水"を削除
del weekdays[2]
print("削除後:", weekdays)


リスト3-6: len()でリストの長さを調べる


scores = [85, 92, 78, 65, 88, 95]
count = len(scores)
print(f"データは全部で{count}件あります。")


リスト3-7: リストの数値の合計と平均を計算する


scores = [85, 92, 78, 65, 88]
total = 0


for score in scores:
  total = total + score # totalにscoreを加算していく


# ループが終わった後に平均点を計算
average = total / len(scores)


print(f"合計点: {total}")
print(f"平均点: {average}")


リスト3-8: ToDoリストの管理プログラム


# ToDoを保存するための空のリスト
todo_list = []


# 無限ループでユーザーの入力を待ち続ける
while True:
  print("\n--- ToDoリスト ---")
  print("1: 追加")
  print("2: 一覧表示")
  print("3: 削除")
  print("0: 終了")


  choice = input("操作を選んでください: ")


  if choice == "1":
    # 追加処理
    new_todo = input("新しいToDoを入力してください: ")
    todo_list.append(new_todo)
    print("ToDoを追加しました。")


  elif choice == "2":
    # 一覧表示処理
    print("\n--- 現在のToDo ---")
    if not todo_list: # リストが空の場合
      print("ToDoはありません。")
    else:
      # enumerateでインデックスと要素を同時に取得
      for i, todo in enumerate(todo_list, 1):
        print(f"{i}. {todo}")


  elif choice == "3":
    # 削除処理
    if not todo_list:
      print("削除するToDoがありません。")
    else:
      del_num = int(input("削除するToDoの番号を入力してください: "))
      if 1 <= del_num <= len(todo_list):
        # delはインデックスを使うので、ユーザー入力から1を引く
        del todo_list[del_num - 1]
        print("ToDoを削除しました。")
      else:
        print("無効な番号です。")


  elif choice == "0":
    # 終了処理
    print("プログラムを終了します。")
    break


  else:
    print("無効な入力です。もう一度選んでください。")


リスト3-9: 辞書でデータを管理する例


# プロフィール情報を辞書で用意
profile = {
  "name": "山田太郎",
  "age": 35,
  "city": "東京"
}


# 商品情報を辞書で管理
product = {
  "product_name": "すごいリンゴ",
  "price": 150,
  "stock": 30
}


print(profile)
print(product)


リスト3-10: キーを使って値を取り出す


profile = {
  "name": "山田太郎",
  "age": 35,
  "city": "東京"
}


# "name"キーに対応する値を取り出す
print(f"名前: {profile['name']}")


# "age"キーに対応する値を取り出す
user_age = profile['age']
print(f"年齢: {user_age}歳")


リスト3-11: ペアの追加と変更


profile = {
  "name": "山田太郎",
  "age": 35
}
print("変更前:", profile)


# 新しいペア（"city": "神奈川"）を追加
profile["city"] = "神奈川"
print("追加後:", profile)


# 既存のキー（"age"）の値を変更
profile["age"] = 36
print("変更後:", profile)


リスト3-12: キーをループで取り出す


product = {
  "product_name": "すごいリンゴ",
  "price": 150,
  "stock": 30
}


for key in product:
  print(f"キー: {key}")


リスト3-13: .items()でキーと値を同時に取り出す


product = {
  "product_name": "すごいリンゴ",
  "price": 150,
  "stock": 30
}


for key, value in product.items():
  print(f"{key} は {value} です。")


リスト3-14: 辞書を要素に持つリスト


# 3人分のプロフィール辞書をリストにまとめる
users = [
  {"name": "山田", "age": 35},
  {"name": "鈴木", "age": 28},
  {"name": "佐藤", "age": 42}
]


# 2人目のユーザー（インデックス1）の名前を取り出す
print(users[1]["name"]) # -> 鈴木


# 全員の名前と年齢をループで表示する
for user in users:
  print(f"名前: {user['name']}, 年齢: {user['age']}歳")


リスト3-15: 英単語をチェックする


import random


# 英単語と意味を格納する辞書
word_dict = {
  "apple": "りんご",
  "banana": "バナナ",
  "cherry": "さくらんぼ",
  "orange": "みかん",
  "grape": "ぶどう"
}


# 辞書のキーをリストに変換
word_list = list(word_dict.keys())


# リストからランダムに単語を1つ選ぶ
question_word = random.choice(word_list)


# ユーザーに質問する
user_answer = input(f"'{question_word}' の意味は何ですか？: ")


# 答えを判定する
correct_answer = word_dict[question_word]
if user_answer == correct_answer:
  print("正解です！素晴らしい！")
else:
  print(f"残念、不正解です。正解は '{correct_answer}' でした。")


リスト3-16: タプルの利用例


# タプルを作る
point = (3, 5)


print(point[0])  # 3
print(point[1])  # 5


リスト3-17: クラスを定義し、インスタンスを作成する


# Characterという名前のクラス（設計図）を定義
class Character:
  pass


# Characterクラスからインスタンスを2つ作成
player = Character()
enemy = Character()


print(player)
print(enemy)


リスト3-18: __init__で属性を初期化する


class Character:
  # オブジェクトが作られるときに呼ばれる
  def __init__(self, name, hp, attack_power):
    print(f"{name}が生まれた！")
    self.name = name
    self.hp = hp
    self.attack = attack_power


# 設計図からオブジェクトを作るときに、初期値を引数として渡す
player = Character("勇者", 100, 15)
enemy = Character("スライム", 30, 5)


# オブジェクトの属性にアクセスするには「ドット（.）」を使う
print(f"{player.name}のHPは{player.hp}です。")
print(f"{enemy.name}の攻撃力は{enemy.attack}です。")


リスト3-19: メソッドを定義して呼び出す


class Character:
  def __init__(self, name, hp, attack_power):
    self.name = name
    self.hp = hp
    self.attack = attack_power


  # 攻撃するという「振る舞い」をメソッドとして定義
  def attack_target(self, target):
    print(f"{self.name}の攻撃！")
    print(f"{target.name}に{self.attack}のダメージを与えた！")
    target.hp -= self.attack # targetのhpを減らす


# オブジェクトを作成
player = Character("勇者", 100, 15)
enemy = Character("スライム", 30, 5)


print(f"戦闘開始！ {enemy.name}のHP: {enemy.hp}")


# プレイヤーが敵を攻撃する（メソッドを呼び出す）
player.attack_target(enemy)


print(f"戦闘後... {enemy.name}のHP: {enemy.hp}")


リスト3-20: ドリンクと自販機のクラスを作る


# ドリンクの情報を保持するクラス
class Drink:
  def __init__(self, name, price, stock):
    self.name = name
    self.price = price
    self.stock = stock


# 自動販売機の機能を持つクラス
class VendingMachine:
  # クラスの初期化処理
  def __init__(self):
    # ドリンクの在庫をリストで管理
    self.drinks = []
    # 初期在庫をセットアップ
    self._setup_drinks()


  # 初期ドリンクを準備する
  def _setup_drinks(self):
    self.drinks.append(Drink("お茶", 120, 5)) #お茶: 5本
    self.drinks.append(Drink("コーラ", 150, 3)) # コーラ: 3本
    self.drinks.append(Drink("水", 100, 0)) # 水: 在庫切れ


  # 購入可能なドリンク一覧を表示する
  def display_drinks(self):
    print("--- ドリンク一覧 ---")
    for i, drink in enumerate(self.drinks):
      stock_status = "売り切れ" if drink.stock == 0 else f"在庫:{drink.stock}本"
      print(f"{i+1}: {drink.name} ({drink.price}円) - {stock_status}")


  # 指定された番号のドリンクを購入する
  def buy(self, drink_number, money):
    # ユーザー入力の番号（1始まり）をリストのインデックス（0始まり）に変換
    drink_index = drink_number - 1


    # 番号が有効かチェック
    if not 0 <= drink_index < len(self.drinks):
      print("無効な商品番号です。")
      return


    selected_drink = self.drinks[drink_index]


    # 在庫、金額をチェック
    if selected_drink.stock <= 0:
      print(f"申し訳ありません、「{selected_drink.name}」は売り切れです。")
    elif money < selected_drink.price:
      print("お金が足りません。")
    else:
      # 購入成功
      selected_drink.stock -= 1
      change = money - selected_drink.price
      print(f"「{selected_drink.name}」を購入しました。お釣りは{change}円です。")


# --- プログラムの実行部分 ---
vm = VendingMachine() # 自動販売機インスタンスを作成


# 購入シミュレーション
vm.display_drinks()
print("\n--- 1回目の購入 ---")
vm.buy(2, 200) # 2番のコーラを200円で買う


print("\n--- 2回目の購入 ---")
vm.buy(3, 100) # 3番の水を100円で買う（売り切れ）


print("\n--- 3回目の購入 ---")
vm.buy(1, 100) # 1番のお茶を100円で買う（お金が足りない）


print("\n--- 最新の在庫状況 ---")
vm.display_drinks()


リスト3-21: モジュールファイルを作成する


%%writefile my_tools.py


# 定数
PI = 3.14159


# 円の面積を計算する関数
def calculate_circle_area(radius):
  return radius * radius * PI


# 簡単な計算ラス
class SimpleCalculator:
  def add(self, a, b):
    return a + b
  def subtract(self, a, b):
    return a - b


リスト3-22: モジュールをインポートして利用する


# my_tools.pyというモジュールをインポート
import my_tools


# モジュール内の関数や定数を使うには「モジュール名.要素名」と書く
radius = 5
area = my_tools.calculate_circle_area(radius)
print(f"半径{radius}の円の面積は {area} です。")
print(f"円周率は {my_tools.PI} です。")


# モジュール内のクラスも同様に使える
calc = my_tools.SimpleCalculator()
result = calc.add(10, 20)
print(f"10 + 20 = {result}")


リスト3-23: randomモジュールの使用例


import random


# 0以上1未満のランダムな浮動小数点数
print(random.random())


# 1から6までのランダムな整数（サイコロ）
print(random.randint(1, 6))


# リストからランダムに要素を1つ選ぶ
my_list = ["りんご", "みかん", "バナナ"]
print(random.choice(my_list))


# リストの要素をシャッフルする（リスト自体が書き換えられる）
random.shuffle(my_list)
print(my_list)


リスト3-24: datetimeモジュールの使用例


import datetime


# 現在の日付と時刻を取得
now = datetime.datetime.now()
print(now)


# 分かりやすい書式に変換して表示
print(now.strftime("%Y年%m月%d日 %H時%M分%S秒"))


# 今日の日付だけを取得
today = datetime.date.today()
print(f"今日は{today.year}年{today.month}月{today.day}日です。")


リスト3-25: mathモジュールの使用例


import math


# 平方根を計算
print("平方根:", math.sqrt(16)) 


# 実数を切り捨てる
print("整数化:", math.trunc(3.1415))


# 円周率π
print("円周率:", math.pi)


リスト3-26: 必要なライブラリをインストール


# pipはPythonのパッケージ管理ツールです
!pip install qrcode[pil]


リスト3-27: プログラムの完成形


# ライブラリをインポート
import qrcode
from IPython.display import Image, display


# QRコードを生成したいURLの用意
url = "https://www.google.com" # @param {type:"string"}


# QRコードオブジェクトを作成
qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=10,
    border=4,
)
qr.add_data(url)
qr.make(fit=True)


# 画像を作成して保存
img = qr.make_image(fill_color="black", back_color="white")
img.save("qrcode.png")


# Colaboratoryのノートブック上に画像を表示
print("QRコードが生成されました：")
display(img)


リスト4-1: Turtleを使うための準備


!pip install ColabTurtle


リスト4-2: カメを登場させて動かす


from ColabTurtle.Turtle import *


# 描画環境を初期化する
# タートルが描画するキャンバスをセットアップする
initializeTurtle()


# タートルを100前進させる
# タートルは移動しながら線を描く
forward(100)


# タートルを右（時計回り）に90度回転させる
right(90)


# タートルを新しい方向にさらに100前進させる
forward(100)


リスト4-3: 正方形を描く


from ColabTurtle.Turtle import *
initializeTurtle()


# タートルを100進める
forward(100)
# タートルを右に90度回転させる
right(90)
# タートルを100進める
forward(100)
# タートルを右に90度回転させる
right(90)
# タートルを100進める
forward(100)
# タートルを右に90度回転させる
right(90)
# タートルを100進める
forward(100)


リスト4-4: Zの字を描く


# ColabTurtle.Turtle からインポート
initializeTurtle()


# 文字「Z」を描画するアルゴリズム
# 1. 進む方向を90度左に回転します。
left(90)
# 2. 上部の水平線を描画します。
forward(100)
# 3. 右下を向くように斜めに回転します。
# 直角は90度なので、これに45度を加えます。
right(135)
# 4. 斜め線を描画します。長さは sqrt(2) * 100 で、約141です。
forward(141)
# 5. 再び右を向きます。
left(135)
# 6. 下部の水平線を描画します。
forward(100)


リスト4-5: 赤くて太い線で三角形を描く


from ColabTurtle.Turtle import *
initializeTurtle()


# ペンのプロパティを設定する
# ペンの色を赤に設定する。
color('red')
# 線の幅を5ピクセルに設定する
width(5)


# 正三角形を描く
# 正三角形の場合、外角はすべて120度。
forward(100)
left(120)
forward(100)
left(120)
forward(100)
left(120)


# ペンの色を赤に設定する
color('blue')
# 線の幅を10ピクセルに設定し、180度回転させる
width(10)
right(180)


# 正三角形を描く
# 正三角形の場合、外角はすべて120度
forward(100)
left(120)
forward(100)
left(120)
forward(100)
left(120)


リスト4-6: 関数にまとめる


from ColabTurtle.Turtle import *
initializeTurtle()


# ペンのプロパティを設定する関数
def set_color_and_width(pen_color='white', pen_width=1):
  color(pen_color)
  width(pen_width)


# 三角形を描画する
def draw_triangle(length=100):
  forward(length)
  left(120)
  forward(length)
  left(120)
  forward(length)
  left(120)


# プロパティを設定する。
set_color_and_width('red', 5)


# 三角形を描画する
draw_triangle(100)


# 向きを変える
right(180)


# プロパティを設定する
set_color_and_width('blue', 10)


# 三角形を描画する
draw_triangle(150)


リスト4-7: 2つの四角形を離して描く


from ColabTurtle.Turtle import *
initializeTurtle()


# ペンのプロパティを設定する関数
def set_color_and_width(pen_color='white', pen_width=1):
  color(pen_color)
  width(pen_width)


# 正方形を描く関数
def draw_square(length=50):
  forward(length)
  left(90)
  forward(length)
  left(90)
  forward(length)
  left(90)
  forward(length)
  left(90)


# ペンの設定をする。
set_color_and_width('blue', 3)


# 正方形を描く
draw_square(50)


# ペンを上げる
penup()
# 100移動する
forward(100)


# ペンを下げる
pendown()


# ペンの設定をする
set_color_and_width('red', 6)


# 正方形を描く
draw_square(100)


リスト4-8: forループで正方形を描く


def draw_square(length=50):
  for i in range(4):
    forward(length)
    left(90)


リスト4-9: 正多角形を描く関数


from ColabTurtle.Turtle import *
initializeTurtle()


# 指定された辺数と長さの正多角形を描画する
# sides: 辺の数（例：三角形は3、正方形は4）
# length: 各辺の長さ
def draw_polygon(sides, length):
  # 正n角形の外角は360/n
  angle = 360/sides
  # 辺の数だけ「移動と回転」を繰り返す
  for i in range(sides):
    forward(length)
    right(angle)


# 描画位置を移動する関数
def move(x):
  penup()
  forward(x)
  pendown()


# 開始位置を左に移動
left(90)
move(250)
right(180)


# この関数を使って、さまざまな多角形を描画する
draw_polygon(3, 100) # 正三角形を描画する
move(200)
draw_polygon(5, 80) # 正五角形を描画する
move(200)
draw_polygon(8, 50) # 正八角形を描く


リスト4-10: ループを重ねて模様を描く


from ColabTurtle.Turtle import *
initializeTurtle()


# 描画速度を高速に設定する
speed(10)


def draw_multi_square(size, num_squares):
  # 外側のループは36回繰り返され、パターン全体が回転する
  for i in range(num_squares):
    # 内側のループは正方形を1つ描画する
    for j in range(4):
      forward(size)
      right(90)


    # 正方形を1つ描画したら、タートルを少し右に回転させる
    # 次の正方形は、この新しい角度から始まる
    right(360 / num_squares)


draw_multi_square(100, 36)


リスト4-11: 偶数回と奇数回で色を変える


from ColabTurtle.Turtle import *
initializeTurtle()
speed(10) # カメの速度を調整


# 描く正方形の数
num_squares = 12


# 正方形を回転しながら描きます。
for i in range(num_squares):
  # ループカウンタ 'i' が偶数かどうかを確認します。
  if i % 2 == 0:
    # 'i' が偶数の場合、ペンの色を赤に設定します。
    color("red")
  else:
    # 'i' が奇数の場合、ペンの色を青に設定します。
    color("blue")
  
  # 正方形を描く
  for j in range(4):
    forward(100)
    right(90)
  
  # 向きを変える
  left(360 / num_squares)


リスト4-12: 画面の端まで進む


from ColabTurtle.Turtle import *
initializeTurtle()


speed(5) # 速度を設定する


left(90)


# 無限ループを開始する
# ループ自体には終了条件はない
while True:
  # 少しだけ前進する
  forward(10)
  
  # タートルの現在の位置 (x、y 座標) を取得する
  x, y = position()
  
  # タートルが画面の左端を越えたかどうかを確認する
  if x <= 0:
    print("Crashed into a wall!")
    # 条件が満たされた場合は、break を実行して無限ループを終了する
    break


print(f"Stopped at position: {position()}")


リスト4-13: ランダムウォーク（バージョン1）


from ColabTurtle.Turtle import *
import random


initializeTurtle()
speed(10)
width(3)
left(90)


# タートルが実行できる可能性のある動きを定義する
moves = ["forward", "right", "left"]


# プロセスを200回繰り返す
for _ in range(200):
  # リストから 1 つの動きをランダムに選択する
  move = random.choice(moves)
  
  # 選択した移動に基づいてアクションを実行する
  if move == "forward":
    color("green")
    forward(10)
  elif move == "right":
    color("blue")
    right(90)
  else: # move == "left"
    color("red")
    left(90)


リスト4-14: ランダムウォーク（バージョン2：壁で跳ね返る）


from ColabTurtle.Turtle import *
import random


initializeTurtle()
speed(10)


# 四角い枠線を描く関数
def draw_square(size):
  # 描画の準備
  penup()  # ペンを上げる
  goto(50, 50)  # (50, 50)の座標に移動する
  setheading(0)  # ペンの向きを東（右）に設定する
  pendown()  # ペンを下ろす


  # 正方形の描画
  for _ in range(4):  # 4回繰り返す
    forward(size)  # `size`で指定された長さだけ前に進む
    right(90)  # 右に90度回転する


  # 描画後の処理
  penup()  # ペンを上げる
  goto(50 + size / 2, 50 + size / 2)  # 正方形の中心に戻す
  setheading(0)  # ペンの向きを東（右）に設定する
  pendown()  # ペンを下ろす


draw_square(400)


bgcolor("black") # 背景色を黒にする
color("cyan")    # ペンカラーをシアンにする


# ColabTurtle キャンバスの境界を定義する
MIN_X, MAX_X = 50, 450
MIN_Y, MAX_Y = 50, 450


# 最大1000回繰り返す
for _ in range(1000):
  # 少し前に進む
  forward(10)
  
  # 縦横の位置を取得する
  x, y = position()
  
  # タートルが境界外にいるかどうかを確認する
  # 条件が False の場合、'not (condition)' は True
  # 条件はタートルが安全領域内にいるかどうかを確認する
  if not (MIN_X < x < MAX_X and MIN_Y < y < MAX_Y):
    # エリア外の場合は、反対向きに移動
    left(180)
    forward(10)
    left(random.randint(-45, 45)) # ランダムに向きを変える
  else:
    # 境界の内側であれば、20%の確率でランダムに方向を変える
    if random.random() < 0.2: # 20% chance
      left(random.randint(-45, 45))


リスト4-15: 再帰で木を描く


from ColabTurtle.Turtle import *
import random


# 木のようなフラクタル構造を再帰的に描画する関数
# branch_len: 現在の枝の長さ
# pen_width: 現在の枝のペンの幅
def draw_tree(branch_len, pen_width):
  # 基本ケース: 分岐が短すぎる場合は、再帰を停止する
  # これは、無限ループを防ぐための再帰関数の最も重要な部分
  if branch_len > 10:
    # 再帰ステップ
    
    # 1. 現在の枝を描く
    if pen_width > 0:
      width(pen_width)
    forward(branch_len)
    
    # 2. 右のサブツリーを描画する準備をする
    angle = random.randint(15, 35) # ランダムな角度
    right(angle) # 指定角度だけ右に振る
    
    # 3. サブツリーを描画するために、自分自身を呼び出す
    # 新しい枝はより短く、より細くなる
    draw_tree(branch_len - random.randint(5, 15), pen_width - 1)
    
    # 4. 左のサブツリーを描画する準備をする
    left(angle * 2)
    
    # 5. サブツリーを描画するために、自分自身を呼び出す
    draw_tree(branch_len - random.randint(5, 15), pen_width - 1)
    
    # 6. 元の位置と向きに戻る
    right(angle)
    backward(branch_len)


# メインプログラムの実行
initializeTurtle()
speed(13)
color("brown")


# 初期設定: カメを上に向け、画面の下部に移動する
penup()
setheading(-90)
backward(200)
pendown()


# 再帰プロセスを開始する
draw_tree(80, 7)
hideturtle() # カメを消す


リスト4-16: コッホ曲線を描く


from ColabTurtle.Turtle import *


# 指定されたレベルと長さのコッホ曲線を描画する
# レベル: 再帰レベル
# 長さ: このレベルでの曲線の全長
def koch_curve(level, length):
  # 基本ケース: レベル 0 では、単なる直線
  if level == 0:
    forward(length)
    return


  # 再帰ステップ: レベル 'n' の曲線は、4 つのレベル 'n-1' の曲線で構成される
  # それぞれの小さな曲線の長さは、全体の長さの 3 分の 1 
  small_len = length / 3
  
  # 1. 最初のセグメントを描画する
  koch_curve(level - 1, small_len)
  
  # 2. 左に向きを変え、三角形が始まる
  left(60)
  
  # 3. 2番目のセグメント (バンプの左側) を描画する
  koch_curve(level - 1, small_len)
  
  # 4. 右に回転して、バンプの右側を描く
  right(120)
  
  # 5. 3番目のセグメント (隆起の右側) を描画する
  koch_curve(level - 1, small_len)
  
  # 6. 左に回転する
  left(60)
  
  # 7. 最後のセグメントを描画する
  koch_curve(level - 1, small_len)


# メインプログラム
initializeTurtle()
speed(13)
# 描画位置を300, 400に調整
penup()
goto(300, 400)
pendown()


# 曲線を 3 回繰り返してコッホ曲線による雪の結晶を描く
for i in range(3):
  koch_curve(3, 300) # 描画レベル3, 大きさ300
  right(120) # 次の曲線を120度回転して描く
hideturtle() # カメを消す


リスト4-17: ドラゴン曲線を描く


from ColabTurtle.Turtle import *


# ドラゴン曲線を描く関数
# level: 再帰レベル
# length: セグメントの長さ
# turn: 回転方向を制御する (左の場合は 1、右の場合は -1)
def dragon_curve(level, length, turn):
  # 基本ケース: レベル 0 では、単なる直線
  if level == 0:
    forward(length)
    return


  # 再帰ステップ: レベル 'n' の曲線は、2 つのレベル 'n-1' の曲線で構成される
  # 重要なのは、回転の方向が交互に変わること
  
  # 曲線の前半
  dragon_curve(level - 1, length, 1)
  
  # 90度回転。方向はturnパラメータによって決まる
  right(90 * turn)
  
  # 曲線の後半部分
  dragon_curve(level - 1, length, -1)


# メインプログラム
initializeTurtle()
speed(13)
color("magenta")
width(2)
penup()
goto(400, 150)
pendown()


# ドラゴン曲線を開始する
dragon_curve(10, 5, 1) # レベル10, 線長さ5, 左方向
hideturtle() # カメを消す


リスト4-17: 次第に大きくなる円の図形


from ColabTurtle.Turtle import *
import colorsys


# 画面とタートルをセットアップ
initializeTurtle()
hideturtle()
bgcolor("black")
width(6)
speed(13)
x = 300
y = 200


# 虹色のカラーリスト
colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]


#ColabTurtleで円の近似図形を描画する関数
# radius: 円の半径
# center_x: 円の中心のX座標
# center_y: 円の中心のY座標
# color: 描画する色
def draw_circle_approximation(radius, center_x, center_y, color):
  pencolor(color)
  circumference = 2 * 3.14159 * radius
  
  # 円の描画準備
  penup()
  goto(center_x + radius, center_y)
  setheading(90)
  pendown()
  
  # 小さな直線を多数描いて円を近似
  steps = int(circumference / 5) if circumference > 0 else 1
  angle_per_step = 360 / steps


  for _ in range(steps):
    forward(5)
    right(angle_per_step)


# --- 同心円の描画処理 ---
for i in range(20):
  # 半径を徐々に大きくする
  radius = i * 5
  
  # 色を虹色に変化させる
  color_index = i % len(colors)
  current_color = colors[color_index]
  
  # 関数を呼び出して円を描画
  draw_circle_approximation(radius, x, y, current_color)


リスト4-18: 星型の図形を描く


from ColabTurtle.Turtle import *


def draw_star(size):
  color("red")
  for i in range(5):
    forward(size)
    right(144)


# 初期化処理
initializeTurtle()
speed(13)
hideturtle()


# 関数を呼び出して星を描く
draw_filled_star(100)


リスト4-19: 図形の位置と大きさを設定する


# 図形を描く関数
def draw_filled_star(x, y, size):
  # x, yの位置に移動する。
  penup()
  goto(x, y)
  pendown()


  # 星を描く
  color("red")
  for i in range(5):
    forward(size)
    right(144)


# メインプログラム
initializeTurtle()
speed(13)
hideturtle()
# 図形を描く
draw_filled_star(300, 300, 200)


リスト4-20: Geminiが最終的に生成したコード


from ColabTurtle.Turtle import *
import random


# 指定位置に指定のサイズと色で星を描画する関数
def draw_filled_star(x, y, size, star_color):
  penup()
  goto(x, y)
  pendown()


  color(star_color)
  for i in range(5):
    forward(size)
    right(144)


# メインプログラム
initializeTurtle()
speed(13)
bgcolor("navy") # 背景色を夜空っぽい色にする


# 使用する色のリストを用意する
colors = ["white", "yellow", "cyan", "magenta"]


# 描くエリア（縦横の最小値・最大値）を用意する
min_x, max_x = 50, 700
min_y, max_y = 50, 500


# 100回、描画を繰り返す
for _ in range(100):
  # 各星の設定値をランダムに用意する
  rand_x = random.randint(min_x, max_x) # 横位置
  rand_y = random.randint(min_y, max_y) # 縦位置
  rand_size = random.randint(10, 50) # サイズ
  rand_color = random.choice(colors) # 色


  # 指定した設定で関数を呼び出す
  draw_filled_star(rand_x, rand_y, rand_size, rand_color)


hideturtle()


リスト5-1: 文字列をファイルに書き出す


# ファイルを開き、ファイルオブジェクトを取得します。
with open("memo.txt", "w", encoding="utf-8") as file:
  file.write("こんにちは、Pythonの世界へ！\n")
  file.write("ファイル操作は簡単です。")


print("ファイルへの書き込みが完了しました。")


リスト5-2: ファイルから文字列を読み込む


try:
  with open("memo.txt", "r", encoding="utf-8") as file:
    content = file.read() # ファイルの中身をすべて一括で読み込む
    print("--- ファイル全体 ---")
    print(content)
except FileNotFoundError:
  print("エラー: memo.txt が見つかりません。")


リスト5-3: 1行ずつ読み込んで処理する


try:
  with open("memo.txt", "r", encoding="utf-8") as file:
    print("\n--- 一行ずつ処理 ---")
    # forでファイルオブジェクトを回すと一行ずつ文字列を取り出せる
    for i, line in enumerate(file):
      # .strip() で文字列の先頭と末尾にある空白文字を削除する
      print(f"{i + 1}行目: {line.strip()}")
except FileNotFoundError:
  print("エラー: memo.txt が見つかりません。")


リスト5-4: ダミーのログファイルを作成する


%%writefile error.log
INFO: Application started successfully.
DEBUG: Connecting to database.
ERROR: Failed to connect to database: timeout expired.
INFO: Retrying connection...
WARNING: High memory usage detected.
ERROR: User authentication failed for user 'admin'.


リスト5-5: ログファイルからERRORの行を抜き出す


input_filename="error.log"
output_filename="error_lines.txt"


try:
  # 'with'で２つのファイルを使うこともできる
  with open(input_filename, "r", encoding="utf-8") as infile, \
    open(output_filename, "w", encoding="utf-8") as outfile:
  
      # 入力ファイルを1行ずつ読み込む
      for line in infile:
        # 行に 'ERROR' という文字列が含まれているかチェック
        if 'ERROR' in line:
          # 含まれていれば、出力ファイルに書き出す
          outfile.write(line)
      
      print(f"エラー行を '{output_filename}' に書き出しました。")


except FileNotFoundError:
  print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
  print(f"予期せぬエラーが発生しました: {e}")


リスト5-6: GithubからREADME.mdを取得する


import requests


# データを取得したいURL
url = "https://raw.githubusercontent.com/python/pythondotorg/refs/heads/main/README.md"


try:
  # GETリクエストを送信して、サーバーからの応答を受け取る
  response = requests.get(url)


  #  ステータスが200番台以外のときエラーを発生させる
  response.raise_for_status() # ステータスが200番台以外のときエラーを発生させる
  
  # response.text にWebページの内容がテキストとして格納されている
  print(response.text)


except requests.exceptions.HTTPError as e:
  print(f"HTTPエラーが発生しました: {e}")
except requests.exceptions.RequestException as e:
  print(f"通信エラーが発生しました: {e}")


リスト5-7: JSONデータのサンプル


%%writefile books.json
{
  "title": "Python入門",
  "author": "山田太郎",
  "chapters": [
    {"id": 1, "title": "環境構築"},
    {"id": 2, "title": "基本文法"},
    {"id": 3, "title": "データ構造"}
  ],
  "is_published": true
}


リスト5-8: JSONデータを表示する


import json


try:
  with open('books.json', 'r', encoding='utf-8') as file:
    # JSONファイルを読み込み、Pythonのデータ構造に変換する
    data = json.load(file)
  
  # これで 'data' はPythonの辞書として扱える
  print(f"本のタイトル: {data['title']}")
  print(f"著者: {data['author']}")
  
  # ネストしたデータにもアクセスできる
  print("--- 章リスト ---")
  for chapter in data['chapters']:
    print(f"  ID: {chapter['id']}, タイトル: {chapter['title']}")


except FileNotFoundError:
  print("エラー: book.json が見つかりません。")
except json.JSONDecodeError:
  print("エラー: JSONの形式が正しくありません。")


リスト5-9: JSONPlaceholderからダミーデータを取得する


import requests
import json


# JSONを返すAPIのURL (例: ダミーのユーザーデータ)
url = "https://jsonplaceholder.typicode.com/users/1"


try:
    response = requests.get(url)
    if response.status_code == 200:
        # response.text はJSON形式の "文字列"
        json_text = response.text
        # JSON文字列をPythonの辞書に変換（パース）
        user_data = json.loads(json_text)
        
        print(f"名前: {user_data['name']}")
        print(f"Email: {user_data['email']}")
        print(f"住所: {user_data['address']['city']}, {user_data['address']['street']}")
        
    else:
        print(f"エラー: {response.status_code}")


except requests.exceptions.RequestException as e:
    print(f"通信エラー: {e}")


リスト5-10: JSONデータを書き換え、ファイルに保存する


# 読み込んだデータに新しい情報を追加
data['publisher'] = 'ラトルズ'


# 2章のタイトルを変更
data['chapters'][1]['title'] = '基本文法マスター'


# データをPythonの辞書として画面に表示
print("\n--- 変更後のデータ ---")
print(data)


# 変更後のデータを新しいJSONファイルに書き出す
with open('book_updated.json', 'w', encoding='utf-8') as file:
  # indent=2 は人間が読みやすいようにインデントを付けるオプション
  # ensure_ascii=False は日本語がそのまま書き出されるようにするおまじない
  json.dump(data, file, indent=2, ensure_ascii=False)


print("\n'book_updated.json' に保存しました。")


リスト5-11: CSVファイルを作成する


%%writefile products.csv
商品名,価格,在庫数
リンゴ,120,50
バナナ,80,100
オレンジ,150,30
イチゴ,320,15
キウイ,200,20


リスト5-12: CSVファイルを読み込む


import csv


try:
  with open('products.csv', 'r', encoding='utf-8') as file:
    reader = csv.reader(file)


    # ヘッダー行をスキップする
    header = next(reader)
    print(f"ヘッダー: {header}")


    # データを1行ずつ読み込んで処理する
    for row in reader:
      # 各行は文字列のリストとして取得できる
      print(f"商品名: {row[0]}, 価格: {row[1]}円, 在庫: {row[2]}個")
except FileNotFoundError:
  print("エラー: products.csv が見つかりません。")


リスト5-13: CSVデータをファイルに追加する


import csv


# 書き込むデータ (リストのリスト)
new_data = [
  ['ブドウ', 250, 40],
  ['モモ', 300, 25]
]


with open('products.csv', 'a', newline='', encoding='utf-8') as file:
  writer = csv.writer(file)
  writer.writerows(new_data) # 複数の行を一度に書き込む


print("products.csv にデータを追加しました。")


リスト5-14: XMLデータをファイルに保存する


%%writefile book.xml
<book>
  <title>Pythonデータ処理入門</title>
  <author>山田 太郎</author>
  <chapters>
    <chapter id="1">
      <title>はじめに</title>
    </chapter>
    <chapter id="2">
      <title>環境構築</title>
    </chapter>
  </chapters>
</book>


リスト5-15: XMLファイルを読み込み内容を表示する


import xml.etree.ElementTree as ET


try:
  # XMLファイルを解析してElementTreeオブジェクトを作成する
  tree = ET.parse('book.xml')
  # ツリーのルート要素を取得
  root = tree.getroot()


  # タグ名と属性にアクセス
  print(f"本のタイトル: {root.find('title').text}")
  print(f"著者: {root.find('author').text}")


  # 子要素をループで処理
  print("--- 章リスト ---")
  for chapter in root.find('chapters'):
    chapter_id = chapter.get('id')
    chapter_title = chapter.find('title').text
    print(f"  ID: {chapter_id}, タイトル: {chapter_title}")


except FileNotFoundError:
  print("エラー: book.xml が見つかりません。")
except ET.ParseError as e:
  print(f"XML解析エラー: {e}")


リスト5-16: XMLデータを書き出す


import xml.etree.ElementTree as ET


# 既存のXMLツリーを読み込む（前の例の続き）
try:
  tree = ET.parse('book.xml')
  root = tree.getroot()
except (FileNotFoundError, ET.ParseError):
  print("既存のXMLファイルが見つからないか、形式が不正です。")
  exit()


# データを変更
# 著者の名前を変更
root.find('author').text = 'データ太郎'


# 3つ目の章を追加
new_chapter = ET.Element('chapter', {'id': '3'})
new_chapter_title = ET.SubElement(new_chapter, 'title')
new_chapter_title.text = 'データ処理の実際'
root.find('chapters').append(new_chapter)


# 変更後のツリーを新しいXMLファイルに書き出す
tree.write('book_updated.xml', encoding='utf-8', xml_declaration=True)


print("変更を 'book_updated.xml' に保存しました。")


リスト6-1: Gradioのインストール


!pip install gradio


リスト6-2: Gradio版 Hello Worldを作る
`
import gradio as gr


# 入力された名前に挨拶を付けて返す関数
def greet(name):
  return "こんにちは、" + name + "さん！"


# GradioのUIを定義し、起動する
demo = gr.Interface(
  fn=greet, 
  inputs="text", 
  outputs="text"
)


# アプリケーションを起動
demo.launch(share=True, debug=True)


リスト6-3: 複数の入出力を持つ電卓アプリ


import gradio as gr


# 処理関数
# 2つの引数(num1, num2)を受け取る
def calculator(num1, num2):
  # 和と差を計算
  sum_result = num1 + num2
  diff_result = num1 - num2
  # 2つの値をタプルとして返す
  return sum_result, diff_result


# UIの定義
demo = gr.Interface(
  fn=calculator,
  # 入力コンポーネントをリストで指定
  inputs=[
    gr.Number(label="数値1"), 
    gr.Number(label="数値2")
  ],
  # 出力コンポーネントをリストで指定
  outputs=[
    gr.Textbox(label="和"), 
    gr.Textbox(label="差")
  ]
)


demo.launch((share=True, debug=True)


リスト6-4: UIにタイトルや説明を追加する


import gradio as gr


def greet(name):
  return "こんにちは、" + name + "さん！"


demo = gr.Interface(
  fn=greet,
  inputs=gr.Textbox(label="あなたの名前"),
  outputs=gr.Textbox(label="挨拶"),
  
  # --- UIに情報を追加する引数 ---
  title="挨拶アプリ",
  description="あなたの名前を入力すると、AIが心を込めて挨拶を返します。",
  article="""
  ## 使い方
  1. 上のテキストボックスに名前を入力してください。
  2. 「Submit」ボタンをクリックします。
  3. AIからの挨拶が表示されます。
  """,
  examples=[["田中"], ["Suzuki"]]
)


demo.launch((share=True, debug=True)


リスト6-5: テキスト系コンポーネントの活用例


import gradio as gr


def process_text(name, age, comment):
  output = f"""
  --- 登録情報 ---
  名前: {name}
  年齢: {age}歳
  コメント: 
  {comment}
  """
  return output


demo = gr.Interface(
  fn=process_text,
  inputs=[
    gr.Textbox(label="名前", placeholder="山田 太郎"),
    gr.Number(label="年齢", value=20), # valueで初期値を設定
    gr.Textbox(label="自己紹介", lines=5, placeholder="5行で自己紹介をどうぞ")
  ],
  outputs=gr.Textbox(label="確認"),
  title="プロフィール登録フォーム"
)


demo.launch((share=True, debug=True)


リスト6-6: 選択系コンポーネントの活用例


import gradio as gr


def submit_survey(pizza_topping, side_menu, agrees_to_terms, satisfaction):
    # CheckboxGroupの結果はリストになるので、文字列に変換
    side_menu_str = ", ".join(side_menu) if side_menu else "なし"
    
    output = f"""
    --- アンケート結果 ---
    好きなピザの具: {pizza_topping}
    サイドメニュー: {side_menu_str}
    利用規約への同意: {"はい" if agrees_to_terms else "いいえ"}
    満足度: {satisfaction} / 10
    """
    return output


demo = gr.Interface(
  fn=submit_survey,
  inputs=[
    gr.Radio(
      ["マルゲリータ", "ペパロニ", "シーフード", "その他"], 
      label="一番好きなピザの具は？"
    ),
    gr.CheckboxGroup(
      ["フライドポテト", "チキンナゲット", "サラダ"], 
      label="一緒に頼むサイドメニューは？（複数選択可）"
    ),
    gr.Checkbox(
      label="利用規約に同意しますか？"
    ),
    gr.Slider(
      minimum=0, maximum=10, step=1, value=5, 
      label="今回の満足度を10段階で評価してください"
    )
  ],
  outputs=gr.Textbox(label="送信内容の確認"),
  title="ピザに関するアンケート"
)


demo.launch((share=True, debug=True)


リスト6-7: Pillowライブラリをインストール


!pip install Pillow


リスト6-8: 画像の色を反転させるアプリ


import gradio as gr
from PIL import Image, ImageOps


# 入力された画像の色を反転させる関数
def invert_colors(input_image):
  # 入力がなければ何もしない
  if input_image is None:
    return None
  
  # PIL.ImageOps.invert()を使って色を反転
  inverted_image = ImageOps.invert(input_image)
  return inverted_image


demo = gr.Interface(
  fn=invert_colors,
  # type="pil"で、画像をPillowのImageオブジェクトとして受け取る
  inputs=gr.Image(type="pil", label="元の画像"),
  outputs=gr.Image(label="反転した画像"),
  title="画像の色反転ツール",
  description="画像をアップロードすると、ネガポジ反転した画像が生成されます。"
)


demo.launch((share=True, debug=True)


リスト6-9: gr.Buttonの基本的な使い方


import gradio as gr
import random


def get_lucky_number():
  return random.randint(1, 100)


with gr.Blocks() as demo:
  gr.Markdown("ボタンをクリックすると、1から100までのラッキーナンバーが表示されます。")
  
  # UIコンポーネントを定義
  lucky_number_output = gr.Textbox(label="ラッキーナンバー")
  generate_btn = gr.Button("運勢を占う", variant="primary") # variant="primary"で強調表示
  
  # イベントリスナーを設定
  generate_btn.click(
    fn=get_lucky_number, 
    inputs=None, 
    outputs=lucky_number_output
  )


demo.launch((share=True, debug=True)


リスト6-10: アップロードされたファイルの情報を表示するアプリ


import gradio as gr
import os


def file_info(file_obj):
  if file_obj is None:
    return "ファイルがアップロードされていません。"
  
  # file_objは一時ファイルオブジェクト
  file_name = os.path.basename(file_obj)
  file_size = os.path.getsize(file_obj)
  
  return f"ファイル名: {file_name}\nファイルサイズ: {file_size} バイト"


demo = gr.Interface(
  fn=file_info,
  inputs=gr.File(label="任意のファイルをアップロード"),
  outputs=gr.Textbox(label="ファイル情報"),
  title="ファイル情報チェッカー"
)
demo.launch((share=True, debug=True)


リスト6-11: 日時を選択してフォーマットするアプリ


import gradio as gr
from datetime import datetime


def format_datetime(dt):
    if dt is None:
      return "日時が選択されていません。"


    # Unixタイムスタンプとして変換
   dt_obj = datetime.fromtimestamp(dt)


    return f"あなたが選択した日時: {dt_obj.strftime('%Y年%m月%d日 %H時%M分')}"


demo = gr.Interface(
  fn=format_datetime,
  inputs=gr.DateTime(label="予約日時を選択してください"),
  outputs=gr.Textbox(label="確認"),
  title="日時フォーマッター"
)


demo.launch((share=True, debug=True)


リスト6-12: gr.Blocksでサイドバーを持つ画像エディタ


import gradio as gr
from PIL import Image, ImageOps, ImageFilter
import numpy as np


# 画像処理を行う関数
def image_processing(image, effect, blur_radius):
  if image is None:
    return None, "画像をアップロードしてください。"


  # GradioのImageコンポーネント(type="numpy")からの入力はNumpy配列
  img_pil = Image.fromarray(image)


  if effect == "色反転":
    processed_image = ImageOps.invert(img_pil)
  elif effect == "グレースケール":
    processed_image = img_pil.convert("L")
  elif effect == "ぼかし":
    processed_image = img_pil.filter(ImageFilter.GaussianBlur(radius=blur_radius))
  else:
    processed_image = img_pil


  return processed_image, f"「{effect}」処理を適用しました。"


# BlocksでUIを構築
with gr.Blocks(theme=gr.themes.Soft()) as demo:
  gr.Markdown("# 🎨 Blocksレイアウトで作る多機能画像エディタ")
  
  # gr.Rowで画面全体を左右に分割
  with gr.Row():
    # 左側のColumnをサイドバーとして使用
    with gr.Column(scale=1): # scaleで幅の比率を指定
      gr.Markdown("### ⚙️ コントロールパネル")
      input_image = gr.Image(type="numpy", label="入力画像")
      effect_dropdown = gr.Dropdown(
        ["色反転", "グレースケール", "ぼかし"],
        label="エフェクトを選択",
        value="色反転"
      )
      blur_slider = gr.Slider(
        minimum=0.1, maximum=20.0, value=5.0,
        label="ぼかしの強さ",
        info="「ぼかし」エフェクト選択時に有効です"
      )
      submit_btn = gr.Button("画像処理を実行", variant="primary")


    # 右側のColumnをメインの表示エリアとして使用
    with gr.Column(scale=3):
      gr.Markdown("### 🖼️ 処理結果")
      output_image = gr.Image(label="出力画像")
      status_text = gr.Textbox(label="ステータス")


  # イベントリスナーの設定
  submit_btn.click(
    fn=image_processing,
    inputs=[input_image, effect_dropdown, blur_slider],
    outputs=[output_image, status_text]
  )


demo.launch((share=True, debug=True)


リスト6-13: Gemini APIの初期設定


from google import genai


from google.colab import userdata


# ColabのシークレットからAPIキーを読み込む
api_key = userdata.get('GEMINI_API_KEY')


# クライアントを作成する
client = genai.Client(api_key=api_key)




# テストで会話してみる
response = client.models.generate_content(
  model="gemini-2.0-flash",
  contents="Pythonについて30文字で教えて。",
)
print(response.text)


リスト6-14: gr.ChatInterfaceの基本形


import gradio as gr


# メッセージと履歴を受け取り、応答を返すチャット関数
# message: ユーザーが新たに入力したメッセージ
# history: これまでの会話のリスト
def chat_function(message, history):
  # ここにAIのロジックが入る
  # 今回は、単純にオウム返しするだけ
  response = f"あなたが言ったのは「{message}」ですね？"
  return response


# ChatInterfaceを作成
demo = gr.ChatInterface(
  fn=chat_function,
  title="チャットボット",
  type="tuples" 
)


# アプリを起動
demo.launch(share=True, debug=True)


リスト6-15: GradioとGeminiで作るAIチャットボット


import gradio as gr
from google import genai


from google.colab import userdata


# --- API設定 ---
api_key = userdata.get('GEMINI_API_KEY')
# クライアントを作成する
client = genai.Client(api_key=api_key)


# --- チャット処理関数 ---
def gemini_chat(message, history):
  response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=message,
  )


  return response.text


# --- Gradio UI ---
demo = gr.ChatInterface(
  fn=gemini_chat,
  title="Geminiチャットボット",
  type="messages" 
)


demo.launch(share=True, debug=True)


リスト6-17: 連続した会話ができるAIチャットボット


import gradio as gr
from google import genai


from google.colab import userdata


# --- API設定 ---
api_key = userdata.get("GEMINI_API_KEY")


# クライアントを作成する
client = genai.Client(api_key=api_key)


# チャットを作成する
chat = client.chats.create(model="gemini-2.0-flash")


# --- チャット処理関数 ---
# message: 最新のユーザー発話（文字列）
# history: Gradio内部で管理している会話履歴（リスト）
def gemini_chat(message, history):
  # チャットにメッセージを送る
  response = chat.send_message(message)
  return response.text


# --- Gradio UI ---
demo = gr.ChatInterface(
  fn=gemini_chat,
  title="Geminiチャットボット",
  description="Gemini 2.0 Flashと会話できるチャットボットです。",
  examples=[
      ["Pythonのクラスについて教えて"],
      ["面白い物語を創作して"],
      ["今日の晩ごはんの献立を考えて"],
  ],
  type="messages"
)


demo.launch(share=True, debug=True)


リスト6-16: gr.Blocksとgr.ChatbotでチャットUIを自作する


import gradio as gr
from google import genai


import time
from google.colab import userdata


# API設定
api_key = userdata.get("GEMINI_API_KEY")


# クライアントを作成する
client = genai.Client(api_key=api_key)


# チャットを作成する
chat = client.chats.create(model="gemini-2.0-flash")


# メインのチャット処理関数
# ユーザーのメッセージと会話履歴を受け取り、Geminiからの応答を返す
def gemini_chat_custom(message, history):
  # ユーザーの新しいメッセージを会話履歴に追加
  # この時点では、ボットの応答部分は空
  history.append([message, ""])
    
  # Gemini APIに応答をストリーミングでリクエスト
  response = chat.send_message(message)
    
  # 応答を少しずつ結合し、UIを更新
  # 最後の会話ペアのボット応答部分を更新
  history[-1][1] += response.text
  # UIに更新後の会話履歴全体を送る
  return ("",history)
          
# Gradio UIの定義と起動 (Blocksを使用)
with gr.Blocks(
  theme=gr.themes.Soft(), 
  css="footer {display: none !important}"
) as demo:
  gr.Markdown("# 🤖 `gr.Chatbot`で作るカスタムチャットボット")
  
  # 1. 会話の履歴を表示するChatbotコンポーネント
  chatbot = gr.Chatbot(
    label="会話履歴", 
    bubble_full_width=False, # Trueだと吹き出しが横に広がる
    avatar_images=(None, "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Google_Favicon_2025.svg/250px-Google_Favicon_2025.svg.png") # (user, bot) のアバター画像
  )
  
  # 2. ユーザーの入力を受け付けるためのコンポーネント
  with gr.Row():
    msg_textbox = gr.Textbox(
      scale=4, # 横幅の比率
      show_label=False,
      placeholder="ここにメッセージを入力してEnterキーを押してください...",
      container=False # Textboxの枠線を消してスッキリさせる
    )
    send_button = gr.Button("送信", scale=1, variant="primary")


  # 3. イベントリスナーの設定
  
  # TextboxでEnterキーが押された時の動作
  msg_textbox.submit(
    fn=gemini_chat_custom,
    inputs=[msg_textbox, chatbot],
    outputs=[msg_textbox, chatbot] # 実行後に入力欄を空にし、チャット履歴を更新
  )
  
  # 送信ボタンがクリックされた時の動作
  send_button.click(
    fn=gemini_chat_custom,
    inputs=[msg_textbox, chatbot],
    outputs=[msg_textbox, chatbot]
  )
  
  # クリアボタン
  clear_button = gr.Button("🗑️ 会話をリセット")
  clear_button.click(lambda: (None, None), None, [msg_textbox, chatbot])


demo.launch((share=True, debug=True)


リスト7-1: トークンの設定


from pyngrok import ngrok
from google.colab import userdata


NGROK_API_KEY = userdata.get('NGROK_API_KEY')
ngrok.set_auth_token(NGROK_API_KEY) # 取得したトークン


リスト7-2: はじめてのFastAPIアプリ


from fastapi import FastAPI


# FastAPIのインスタンスを作成
app = FastAPI()


# ルートURL ("/") へのGETリクエストを処理する関数を定義
@app.get("/")
async def read_root():
  return {"message": "Hello, FastAPI from Colaboratory!!"}


リスト7-3: FastAPIサーバーの起動


import uvicorn
import threading


# uvicornを別スレッドで起動
def run():
  uvicorn.run(app, host="0.0.0.0", port=8000)


thread = threading.Thread(target=run, daemon=True)
thread.start()


# ngrokでトンネルを開く
public_url = ngrok.connect(8000)
print("Public URL:", public_url)


リスト7-4: # スレッドとappの初期化


import threading
from pyngrok import ngrok


# ngrok トンネルを閉じる
ngrok.kill()
print("既存サーバーの停止済み")


# app 変数を削除
del globals()['app']
print("app 変数を削除しました")


リスト7-5: main.pyのコード


from fastapi import FastAPI


# FastAPIのインスタンスを作成
app = FastAPI()


# ルートURL ("/") へのGETリクエストを処理する関数を定義
@app.get("/")
async def read_root():
  return {"message": "Hello, FastAPI!"}


リスト7-6: staticフォルダーを作る


%%bash
rm -rf static
mkdir -p static


リスト7-7: index.htmlファイルを作る


%%writefile static/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>FastAPI: Hello</title>
  <style>
  h1 { font-size:20px; }
  pre { border: 2px solid #aaaaff; 
      font-size:16px; padding: 10px;}
  </style>
</head>
<body>
  <h1>FastAPI サンプル</h1>
  <pre id="out">no message...</pre>
  <button id="btn">APIを呼び出す</button>
  <script>
  document.getElementById('btn').onclick = async()=>{
    const res = await fetch('/api');
    const result = await res.json();
    const out = document.getElementById('out');
    out.textContent = result.message;
  };
  </script>
</body>
</html>


リスト7-8: main.pyを修正する


from fastapi import FastAPI
from fastapi.responses import FileResponse


app = FastAPI()


# APIのエンドポイント
@app.get("/api")
async def read_root():
  return {"message": "Hello, FastAPI!"}


# index.htmlを表示する
@app.get("/")
async def ui_root():
  return FileResponse("static/index.html")


リスト7-9: パスパラメータを持つエンドポイント


%%writefile main.py
from fastapi import FastAPI
from fastapi.responses import FileResponse


app = FastAPI()


@app.get("/")
async def read_root():
  return {"message": "Hello, World"}


# {item_id} パスパラメータを受け取るエンドポイント
@app.get("/items/{item_id}")
async def read_item(item_id: int): # 整数パラメータitem_id
  return {"item_id": item_id, "description": f"これは商品ID {item_id} の情報です。"}


@app.get("/items")
async def ui_items():
  return FileResponse("static/items.html")


リスト7-10: パスパラメータを使ってAPIにアクセスする


%%writefile static/items.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Items (path)</title>
  <style>
  h1 { font-size:20px; }
  pre { border: 2px solid #aaaaff; 
      font-size:16px; padding: 10px;}
  </style></head>
<body>
<h1>商品情報を取得 (/items/{id})</h1>
  <input id="id" type="number" placeholder="商品ID">
  <pre id="out"></pre>
  <button id="btn">取得</button>
  <script>
    document.getElementById('btn').onclick = async () => {
    const id = document.getElementById('id').value || '1';
    const res = await fetch(`/items/${id}`);
    const j = await res.json();
    document.getElementById('out').textContent = JSON.stringify(j, null, 2);
  };
  </script>
</body>
</html>


リスト7-11: クエリパラメータを受け取るエンドポイント
`
%%writefile main.py
from fastapi import FastAPI
from fastapi.responses import FileResponse
from typing import Optional


app = FastAPI()


# 複数のクエリパラメータを受け取るエンドポイント
@app.get("/api/search")
async def search_items(keyword: str, page: int = 1, limit: Optional[int] = 10):
  # pageとlimitにはデフォルト値が設定されている
  # keywordは必須のクエリパラメータ
  return {"keyword": keyword, "page": page, "limit": limit}


@app.get("/search")
async def ui_search():
  return FileResponse("static/search.html")


リスト7-12: /api/search


%%writefile static/search.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Search (query)</title>
  <style>
  h1 { font-size:20px; }
  pre { border: 2px solid #aaaaff; 
      font-size:16px; padding: 10px;}
  </style>
</head>
<body>
  <h1>検索</h1>
  <pre id="out"></pre>
  <input id="keyword" placeholder="キーワード">
  <input id="page" type="number" value="1">
  <input id="limit" type="number" value="10">
  <button id="btn">検索</button>
  <script>
  document.getElementById('btn').onclick = async () => {
    const k = document.getElementById('keyword').value;
    const p = document.getElementById('page').value;
    const l = document.getElementById('limit').value;
    const res = await fetch(`/api/search?keyword=${k}&page=${p}&limit=${l}`);
    const j = await res.json();
    const out = document.getElementById('out')
    out.textContent = JSON.stringify(j, null, 2);
  };
  </script>
</body>
</html>


リスト7-13: gradioでAPIにアクセスする


import gradio as gr
import requests # リクエスト送信のライブラリ
import json


# ここにFastAPIサーバーの公開URLを設定-
# 例: "http://xxxxxxxx.ngrok-free.app"
FASTAPI_SERVER_URL = "http://xxxxxxxx.ngrok-free.app/"


# /api/searchにアクセスする関数
def search_api(keyword, page, limit):
    url = f"{FASTAPI_SERVER_URL}/api/search"
    params = {
        "keyword": keyword,
        "page": page,
        "limit": limit
    }
    
    try:
        response = requests.get(url, params=params)
        
        # JSONを解析する前にステータスコードを確認
        if response.status_code == 200:
            # 応答が成功した場合はJSONを解析する
            data = response.json()
            return json.dumps(data, indent=2, ensure_ascii=False)
        else:
            # ステータスコードが200以外なら生テキストを返す
            return f"エラーが発生しました。ステータスコード: ⏎
               {response.status_code}\n{response.text}"
            
    except requests.exceptions.RequestException as e:
        return f"リクエスト中にエラーが発生しました: {e}"
    except json.JSONDecodeError as e:
        #JSONデコードエラーをキャッチする
        return f"JSONのデコード中にエラーが発生しました: {e}\n⏎
            レスポンス内容: {response.text}"


# Gradioインターフェースの定義
with gr.Blocks() as demo:
  gr.Markdown("## FastAPI検索APIクライアント")
  with gr.Row():
    keyword_input = gr.Textbox(label="検索キーワード (必須)")
    page_input = gr.Number(label="ページ番号", value=1)
    limit_input = gr.Number(label="取得件数", value=10)
    
  submit_btn = gr.Button("APIを呼び出す")
  result_output = gr.JSON(label="APIからのレスポンス")
    
  submit_btn.click(
    fn=search_api,
    inputs=[keyword_input, page_input, limit_input],
    outputs=result_output
  )


demo.launch()


リスト7-14: Pydanticモデルを定義する


%%writefile main.py
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel


# Pydanticモデルを定義
class Item(BaseModel):
  name: str
  description: str | None = None
  price: float
  tax: float | None = None


app = FastAPI()


# POSTリクエストでItemモデルを受け取るエンドポイント
@app.post("/items/")
async def create_item(item: Item): # 引数の型ヒント指定
  item_dict = item.model_dump() # Pydanticオブジェクトを辞書に変換
  if item.tax:
    price_with_tax = item.price + item.tax
    item_dict.update({"price_with_tax": price_with_tax})
  return item_dict


@app.get("/create")
async def ui_create_item():
  return FileResponse("static/create_item.html")


リスト7-15: 商品情報のフォームがあるWebページを作る


%%writefile static/create_item.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Create Item (POST)</title>
  <style>
  h1 { font-size:20px; }
  pre { border: 2px solid #aaaaff; 
      font-size:16px; padding: 10px;}
  </style>
</head>
<body>
  <h1>商品を作成 (POST /items/)</h1>
  <input id="name" placeholder="name"><br>
  <input id="desc" placeholder="description"><br>
  <input id="price" type="number" placeholder="price"><br>
  <input id="tax" type="number" placeholder="tax"><br>
  <button id="btn">送信</button>
  <pre id="out"></pre>
  <script>
  document.getElementById('btn').onclick = async () => {
    const payload = {
      name: document.getElementById('name').value,
      description: document.getElementById('desc').value,
      price: parseFloat(document.getElementById('price').value || 0),
      tax: parseFloat(document.getElementById('tax').value || 0)
    };
    const res = await fetch('/items/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    const j = await res.json();
    document.getElementById('out').textContent = JSON.stringify(j, null, 2);
  };
  </script>
</body>
</html>


リスト7-16: レスポンスモデルを指定する


%%writefile main.py
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.responses import FileResponse


# --- Pydanticモデル定義 ---
# ユーザーが入力するデータモデル（パスワードあり）
class UserIn(BaseModel):
  username: str
  password: str
  email: str
  full_name: str | None = None


# APIが返すデータモデル（パスワードなし）
class UserOut(BaseModel):
  username: str
  email: str
  full_name: str | None = None


app = FastAPI()


# 送信されたUserInオブジェクトをUserOutにして出力する関数
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
  # ここで、受け取ったuser (UserIn) のデータを処理する
  # 処理した後のデータを返す
  return user


@app.get("/user")
async def ui_register_user():
  return FileResponse("static/register_user.html")


リスト7-17: ユーザー登録のフォームページを作る


%%writefile static/register_user.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Register User (POST)</title>
  <style>
  h1 { font-size:20px; }
  pre { border: 2px solid #aaaaff; 
      font-size:16px; padding: 10px;}
  </style>
</head>
<body>
<h1>ユーザー登録 (POST /users/)</h1>
  <input id="username" placeholder="username"><br>
  <input id="email" placeholder="email"><br>
  <input id="fullname" placeholder="full_name"><br>
  <input id="password" type="password" placeholder="password"><br>
  <button id="btn">登録</button>
  <pre id="out"></pre>
  <script>
    document.getElementById('btn').onclick = async () => {
      const payload = {
        username: document.getElementById('username').value,
        email: document.getElementById('email').value,
        full_name: document.getElementById('fullname').value,
        password: document.getElementById('password').value
      };
      const res = await fetch('/users/', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      });
      const j = await res.json();
      document.getElementById('out').textContent = JSON.stringify(j, null, 2);
    };
  </script>
</body>
</html>


リスト7-18: Geminiが生成したAPIコード


%%writefile main.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Optional


# --- Pydanticモデル定義 ---
class RecipeRequest(BaseModel):
    ingredients: List[str]
    cuisine_type: Optional[str] = None


class RecipeResponse(BaseModel):
    recipe_name: str
    steps: List[str]


# --- FastAPIアプリ ---
app = FastAPI()


# レシピ生成APIエンドポイント 
@app.post("/generate-recipe/", response_model=RecipeResponse)
async def generate_recipe(request: RecipeRequest):
    # Geminiへの処理は後で追加。まずはダミーデータを返す。
    dummy_recipe = {
        "recipe_name": "ダミーのレシピ",
        "steps": [
            "ステップ1: 材料を切る",
            "ステップ2: 材料を炒める",
            "ステップ3: 盛り付ける"
        ]
    }
    return dummy_recipe


リスト7-19: レシピ提案APIの完成版


%%writefile main.py
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List, Optional
from fastapi.responses import FileResponse
import google.generativeai as genai
from google.colab import userdata # ローカルでは使わない
import json


# --- APIキーとモデルの設定 ---
try:
  api_key = userdata.get('GEMINI_API_KEY') # ローカルではカット
  genai.configure(api_key=api_key) # ローカルではAPIキーを直接指定
  model = genai.GenerativeModel('gemini-2.0-flash-latest')
  API_READY = True
except Exception:
  API_READY = False


# --- Pydanticモデル定義 ---
# リクエスト用クラス
class RecipeRequest(BaseModel):
  ingredients: List[str] = Field(..., examples=[["鶏肉", "玉ねぎ", "卵", "ごはん"]])
  cuisine_type: Optional[str] = Field(None, examples=["和食"])


# レスポンス用クラス
class RecipeResponse(BaseModel):
  recipe_name: str
  steps: List[str]


# --- FastAPIアプリ ---
app = FastAPI(
  title="AIレシピ提案API",
  description="材料を送ると、Geminiがレシピを考えてくれるAPIです。"
)


# レシピ生成APIエンドポイント
@app.post("/generate-recipe/", response_model=RecipeResponse)
async def generate_recipe(request: RecipeRequest):
  if not API_READY:
    return {"recipe_name": "エラー", "steps": ["APIキーが設定されていません。"]}


  # Geminiへのプロンプトを組み立てる
  prompt = f"""
    以下の材料を使って、家庭で簡単に作れる美味しいレシピを1つ提案してください。
    
    # 材料
    {', '.join(request.ingredients)}
    
    # ジャンル (指定があれば)
    {request.cuisine_type if request.cuisine_type else "指定なし"}
    
    # 出力形式
    回答は必ず以下のJSON形式で、レシピ名と手順のみを返してください。
    {{
      "recipe_name": "ここにレシピ名",
      "steps": ["手順1", "手順2", "手順3", "..."]
    }}
    """
    
  # Gemini APIを呼び出し、JSONモードで応答を生成
  response = model.generate_content(
    prompt,
    generation_config={"response_mime_type": "application/json"}
  )
  # JSONデータを辞書に変換
  response_dict = json.loads(response.text)
  return response_dict # 辞書を返す


# static/recipe.htmlを表示する
@app.get("/recipe")
async def ui_recipe():
  return FileResponse("static/recipe.html")