coLinux日記

coLinuxはフリーソフトを種として、よろずのシステムとぞなれりける。

入門者が生成AIも利用して Python プログラミングを行う。 その2

2024-10-04 20:49:43 | Python
前回の続きで、具体的なプログラミングを行ってみます。

まず、Google Gemini を使って9要素のリストが1~9のすべての数字を含んでいるかどうかを調べる関数check_list()を作ります。

Gemini へのプロンプトは、以下の通りです。

「要素数9のリスト b を引数として、
リストの各要素が重複しないで1から9の値になっているかどうかを調べて、この条件を満たすときは真、満たさない場合は偽を返すような関数 check_list(b)を作成してください。」

Gemini の回答: (抜粋)
## リストの要素が重複なく1から9まで含まれているか判定する関数

def check_list(b):
   """
   リストbの要素が重複なく1から9まで含まれているかを判定する関数
   Args:
      b: 判定対象のリスト
   Returns:
      bool: 条件を満たす場合はTrue、満たさない場合はFalse
   """

   # 要素数が9でない場合、Falseを返す
   if len(b) != 9:
      return False

   # 要素が1から9の範囲に含まれていないか、重複があるかを確認
   return set(b) == set(range(1, 10))

### コード解説

1. **関数定義:**
   * `check_list(b)`: リスト `b` を引数にとる関数。
2. **要素数チェック:**
   * `if len(b) != 9:`: リストの長さが9でない場合、条件を満たさないためFalseを返す。
3. **重複と範囲チェック:**
   * `return set(b) == set(range(1, 10))`:
      * `set(b)`: リスト `b` を集合に変換することで、重複を自動的に除去する。
      * `set(range(1, 10))`: 1から9までの数値の集合を作成する。
      * `==`: 両方の集合が等しいか(要素が完全に一致するか)を比較する。
      * 集合が等しい場合、リスト `b` の要素が重複なく1から9まで含まれていることを意味するため、Trueを返す。

### 動作原理

* **集合の利用:** 集合は重複した要素を持たないため、リストを集合に変換することで、要素の重複を簡単に検出できる。
* **範囲の比較:** `range(1, 10)` で生成した集合と、リスト `b` から作成した集合を比較することで、要素が1から9までの範囲に含まれているか、かつ重複がないかを同時に確認できる。

=====ここまで


docstring 付きですね。数字が順番に並んでいないので、set()を使ってリストを集合に替えて判定するのですね。
解説も分かりやすく、そのまま利用できます。

次に、「数独」の9個のブロックが1から9の数値を重複無く含んでいるかを調べるために、
3行毎に3回に分けて調べるようにします。一つでも False になると直ぐに終了して次に行くのと、長くなりそうなのでこうしてみました。

そのために、3つの行を引数にして条件を調べる chk_block(a,b,c) という関数を作ってみます。
これは、生成AIを使わないで、単純に次のようにしました。
(Gemini は、docstring作成にのみ使いました。Raises:とExamples:は省略)

def chk_block(a,b,c):
   """
   3つのリストを9要素ずつに分割し、各ブロックに対して `check_list` 関数を適用します。
   全てのブロックで `check_list` が `True` を返す場合にのみ、`True` を返します。
   Args:
      a (list): 要素数9のリスト
      b (list): 要素数9のリスト
      c (list): 要素数9のリスト
   Returns:
      bool: 全てのブロックが `check_list` の条件を満たす場合にTrue、そうでない場合にFalse
   """


   block1 = a[0:3] + b[0:3] + c[0:3]
   block2 = a[3:6] + b[3:6] + c[3:6]
   block3 = a[6:] + b[6:] + c[6:]

   if check_list(block1) == False:
      return False
   if check_list(block2) == False:
      return False
   if check_list(block3) == False:
      return False
   return True

この2つの関数をテストします。
ファイル prog-01.py で、2つの関数を定義して、その下に以下のテストプログラムを挿入して実行します。

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 2, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 7, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 2, 9, 1, 2, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 2, 3 ]
row3 = [7, 8, 1, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

$ python3 prog01.py
OK
NG
NG
NG
$

テストで問題ないことが分かりました。
長くなりましたので、続きはまた後で投稿します。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その1

2024-10-03 21:32:06 | Python
SimplePrograms を利用して、Pythonの入門も大体終了したので、実際にプログラミングしてみます。

プログラムしようと思ったのは「数独」です。

スマートフォンを使っていると、この問題は解けるか?という宣伝広告が出てくることがあります。いくつか候補を試して進めるしかないような場合があって鬱陶しいので、それをプログラムで解いてしまおうとする試みです。本当に解が1つしかないのかなども試してみたいです。

今回は、問題を与えて解を求めるように作ってみます。最近、生成AIによるプログラミングも話題になっているので使ってみたいと思います。と言っても、全部作ってもらうと入門にならないので、手順はあらかじめ考えておいて、それを実現するような関数を生成AIに作ってもらい実現してみようと思いました。

考えた手順はこんな感じです。

初期化)
1)マスを横方向にみて、9個の要素を含むリストとみなし、上からR1、R2、...、R9 とします。
 各リストには1~9の数字が重複しないで入っている必要があります。
2)空白は0、初期の数字はそのまま(1から9の整数)R1~R9の各要素に代入して初期値にします。

数値挿入)
3)このリストrの0の要素に、残りの数値を入れたリスト(複数できる)をすべて生成するような
関数 fill_list(r)
を作ります。その返却値は、一つの要素が、要素数9個のリストである、リストです。

条件判定)
4)引数で与えたリストの要素が1)の条件を満たすリストかどうかを判定する
関数 check_list(list)
を作ります。
各行は条件をすでに条件をみたしているので、ブロックと列の判定にこれを使います。

答えを探す)
5)話を単純にするために、9重のforループを作り、条件を満たすかどうかを check_list() で判定します。こんな感じでしょうか。

for row1 in fill_list(R1):
   for row2 in fill_list(R2):
      for row3 in fill_list(R3):
         for row4 in fill_list(R4):
            for row5 in fill_list(R5):
               for row6 in fill_list(R6):
                  for row7 in fill_list(R7):
                     for row8 in fill_list(R8):
                        for row9 in fill_list(R9):

 チェックする量が膨大ですが、そのほとんどが条件を満たさないので、満たさなかったらcontinue文を使って直ぐ次に行くようにしたほうが良さそうです。答えが2つ以上ある場合もありそうですから。

列の判定)
6)列の判定は、row1 ~ row9 の各要素を先頭から取り出してマスの縦方向のカラムを表す
9個のリストに対してcheck_list()で判定して、
どれかが偽なら次のループへ、
すべてが真なら列は条件を満たしていることになります。

ブロックの判定)
7)ブロックの判定は、数独の9つのブロックをリストにして、そのリストに対してcheck_list()を実施して、
どれかが偽なら次のループへ、
すべてが真ならブロックの条件を満たしてることになります。
ループの一番内側で使うより、3つの行毎に判定するのが良さそうです。最初の3ブロックで条件を満たさなければ残りの6行を用意して判定する必要はありませんから。

6)、7)の順番は自由です。

こんな感じです。

生成AIは、各関数を作成するのに利用してみます。
上の手順の中で、check_list() と fill_list()の関数を作成してもらいたいと思います。

長くなるので、今後分割して投稿したいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Raspberry Pi に Python 3.12.6 をインストールする

2024-09-20 15:40:08 | Python
前回 Raspberry Pi の Pi OS も再インストールできたので、Python3 をインストールしてみます。

Python 3.12.6 が、日本時間 2024-09-07 にリリースされていました。

3.12.6 は最新のメンテナンス リリースで、3.12.5 以降の約 90 のバグ修正、ビルドの改善、ドキュメントの変更が含まれています。これは、次のセキュリティ問題に対処するための緊急リリースです。

早速ダウンロードしてみましょう。

$ wget https://www.python.org/ftp/python/3.12.6/Python-3.12.6.tar.xz

PGP署名ファイルもダウンロードします。

$ wget https://www.python.org/ftp/python/3.12.6/Python-3.12.6.tar.xz.asc

壊れたOSのユーザファイルは元に戻してあり、公開鍵はすでにインポートしているので、確認します。

$ gpg --verify Python-3.12.6.tar.xz.asc
gpg: assuming signed data in 'Python-3.12.6.tar.xz'
gpg: Signature made Fri 06 Sep 2024 21:33:01 BST
gpg:        using RSA key 7169605F62C751356D054A26A821E680E5FA6305
gpg: Good signature from "Thomas Wouters <thomas@python.org>" [unknown]
gpg:        aka "Thomas Wouters <thomas@xs4all.nl>" [unknown]
gpg:        aka "Thomas Wouters <twouters@google.com>" [unknown]
gpg:        aka "Thomas Wouters <thomas.wouters.prive@gmail.com>" [unknown]
gpg:        aka "Thomas Wouters <thomaswout@gmail.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:     There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7169 605F 62C7 5135 6D05 4A26 A821 E680 E5FA 6305
$


早速、以前行ったこちらのようにインストールしてみます。
https://blog.goo.ne.jp/espiya/e/fdf200d42183170e14bd374987749a7d

まずは、関連パッケージを apt でインストールします。

# apt install libncurses-dev libreadline-dev libssl-dev libssh-dev
#
    ( libssh-dev は、念のため )

# apt install libsqlite3-dev libbz2-dev libffi-dev libgdbm-dev liblzma-dev tcl tcl-dev tk tk-dev uuid-dev zlib1g-dev
#

早速 make してみましょう。

$ tar xvf Python-3.12.6.tar.xz
$ cd Python-3.12.6
$ ./configure --prefix=/usr/local/python --enable-optimizations  --with-readline --enable-shared
$ make
$


_dbm がないのは前回通りです。

make できたら、インストールします。インストール先の /usr/local/python は、無ければ生成してくれました。

# make install

Python3 を使うための環境設定は、 環境変数を export するところを python3.sh というファイルにして、Bash の場合次のように設定します。

$ cat python3.sh
export PATH=/usr/local/python/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/python/lib
$

$ source python3.sh


早速実行してみました。

$ python3
Python 3.12.6 (main, Sep 14 2024, 16:22:55) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>>
$
$ pip3 --version
pip 24.2 from /usr/local/python/lib/python3.12/site-packages/pip (python 3.12)
$


これで、最新の Python3 がインストールできました。

壊れてしまったRaspberry Pi のシステムもほぼ元通りになり、10月に 3.13 がリリースされる予定なので、その準備もできましたね。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Raspberry Pi、システム的な不具合をなおす。

2024-09-15 07:16:01 | Raspberry Pi
使っている Raspberry Pi で、 apt update を実行したら、基本的なライブラリ?が壊れたらしく、コマンドが動かなくなり、再度ログインできなくなりました。
お手上げですね。これでは Python3 も試せません。

しかたがないので、OSをインストールし直します。以下は大雑把なメモです。SSHD を起動して外からログインできるようにするのがとりあえずの目標ですね。

https://www.raspberrypi.com/software/operating-systems/
から、
Raspberry Pi OS (64-bit)/Raspberry Pi OS Lite
2024-07-04-raspias-bookworm-arm64-lite.img

を、Image Writer for Microsoft Windows のようなツールを使ってUSBメモリへコピーします。

本体にメモリを差し込んで、ディスプレイ、キーボード、マウス、ネットワークケーブルを繋いで、電源を入れてブートしてと、ホスト名を設定し、ユーザ名を入力してパスワードを入れてブートするとそのユーザ名でログインできるようになります。

ログインして、raspi-config で sshd を起動させます。ここは、専用ツールを使います。

# raspi-config

で、
3. Interface Options を選択して、
11. SSH を選択して、「Yes」を選ぶと

 The SSH server is enabled

になります。再起動が要求されるならそれに従って、他からログインできれば完了です。

一度シャットダウンして、すべてを外して、ネットワークケーブルだけ繋いで立ち上げれば一応動くようになりました。

今回は64ビット版ですね。

$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
espiya@rasp4mb:~ $

このままだと、パスワード無しで sudo が実行出来てしまうので、

まず、root のパスワードを設定します。
$ sudo passwd root

確認するために root になります。
$ su

#

今回は、
/etc/sudoers.d/010_pi_nopasswd
の内容の行の先頭に # を付けてコメントにし、

# visudo
で、
%sudo ALL=(ALL:ALL)ALL
の行の先頭に # を付けてコメントしました。これは、/etc/sudoers ファイルを更新するのですね。

詳しく調べていませんが、一応これで設定は終了です。

古いメモリカードをUSBで接続すると、ファイルシステムは /dev/sda2 となって、これをマウントすれば読み取れました。

これで、Python環境(make したディレクトリとか)を復元しました。
cp コマンドに -Rp オプションを付けてコピーすればディレクトリ毎コピーできます。

apt も試してみました。

# apt update
......
# apt upgrade
......

無事にアップデートもできました。

次回は、再び Python3 をインストールしてみます。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その21

2024-09-07 21:28:15 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 21番目のプログラムは、33行の数字当てゲームです。

import random

guesses_made = 0

name = input('Hello! What is your name?\n')

number = random.randint(1, 20)
print ('Well, {0}, I am thinking of a number between 1 and 20.'.format(name))

while guesses_made < 6:

    guess = int(input('Take a guess: '))

    guesses_made += 1

    if guess < number:
       print ('Your guess is too low.')

    if guess > number:
       print ('Your guess is too high.')

    if guess == number:
       break

if guess == number:
    print ('Good job, {0}! You guessed my number in {1} guesses!'.format(name, guesses_made))
else:
    print ('Nope. The number I was thinking of was {0}'.format(number))


例によって、prog-33.py というファイルにして実行してみます。

$ python3 prog-33.py
Hello! What is your name?
espiya
Well, espiya, I am thinking of a number between 1 and 20.
Take a guess: 5
Your guess is too low.
Take a guess: 10
Your guess is too high.
Take a guess: 8
Your guess is too high.
Take a guess: 6
Good job, espiya! You guessed my number in 4 guesses!
$


6回までに1から20までの数を当てるゲームですね。

6回で当てられないと、
Nope. The number I was thinking of was 6
$

となります。

さすがに最後のプログラムとなると新しい機能はほとんど使われていませんね。

その中で、random モジュールが使われています。
https://docs.python.org/ja/3/library/random.html#module-random

このモジュールでは様々な分布をもつ擬似乱数生成器を実装しています。

整数用に、ある範囲からの一様な選択があります。シーケンス用には、シーケンスからのランダムな要素の一様な選択、リストのランダムな置換をインプレースに生成する関数、順列を置換せずにランダムサンプリングする関数があります。

実数用としては、一様分布、正規分布 (ガウス分布)、対数正規分布、負の指数分布、ガンマおよびベータ分布を計算する関数があります。角度の分布を生成するにはフォン・ミーゼス分布が利用できます。

ほとんど全てのモジュール関数は、基礎となる関数 random() に依存します。この関数はランダムな浮動小数点数を半開区間 0.0 <= X < 1.0 内に一様に生成します。Python は中心となる乱数生成器としてメルセンヌツイスタを使います。これは 53 ビット精度の浮動小数点を生成し、周期は 2**19937-1 です。本体は C で実装されていて、高速でスレッドセーフです。メルセンヌツイスタは、現存する中で最も広範囲にテストされた乱数生成器のひとつです。しかしながら、メルセンヌツイスタは完全に決定論的であるため、全ての目的に合致しているわけではなく、暗号化の目的には全く向いていません。



以前、LINEのAIチャット君の紹介で出てきたように、random()の乱数生成方法はメルセンヌツイスタを使っているのですね。注意書きにもあるように、メルセンヌツイスタは決定論的なので、使用するシードに工夫が必要ですが、生成される乱数はOS提供の乱数に比べれば非常に良いと考えられるので、乱数が必要なプログラムでは十分使えますね。この中で、


random.randint(a, b)

a <= N <= b であるようなランダムな整数 N を返します。randrange(a, b+1) のエイリアスです。



を使って、1から20までの乱数を生成して答えとするわけです。

クイズなので、input() で入力を、print() で出力をするプログラムですね。
整数入力の時は、 int(input(.....)) と覚えておきます。

while ループ内の if 文ですが、elif や continue を使いたいと思う気がしますが、
SimplePrograms ではいずれも使っていませんでした。

さて、出力ですが、

https://docs.python.org/ja/3.8/tutorial/inputoutput.html#input-and-output
によると、


・ フォーマット済み文字列リテラル を使うには、開き引用符や三重の開き引用符の前に f あるいは F を付けて文字列を始めます。 この文字列の内側では、文字 { と文字 } の間に Python の式が書け、その式から変数やリテラル値が参照できます。

・ 文字列の str.format() メソッドは、もう少し手間がかかります。 ここでも { と } を使って変数に代入する場所の印を付けて、細かいフォーマットの指示を出せますが、フォーマットされる対象の情報を与える必要があります。

フォーマット済み文字リテラル (短くして f-string とも呼びます) では、文字列の頭に f か F を付け、式を {expression} と書くことで、 Python の式の値を文字列の中に入れ込めます。



これを、いつくか試してみます。

>>> a = 10
>>> b = 100
>>>
>>> print(f'A is {a}, B is {b}.')
A is 10, B is 100.
>>>
>>> print('A is {a00}, B is {b00}.'.format(a00=a, b00=b))
A is 10, B is 100.
>>>
>>> print('A is {0}, B is {1}.'.format(a, b))
A is 10, B is 100.
>>>
>>> print('A is {a}, B is {b}.'.format(a, b))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'a'
>>>
>>> print('A is {a}, B is {b}.'.format(a = a, b = b))
A is 10, B is 100.
>>>


エラーになる例は間違えそうなので、一番最後の例の形で覚えておくことにします。

SimplePrograms では、f-string が出てこなくて、str.format() のみ使っているので、
何かあるのかもしれませんね。

以上、SimplePrograms の21の各プログラムを見てきましたが、入門としてはちょっと変わった?機能を使うことがあり、これを Python プログラミングで使った方が良いという考え方があるような気がしました。特に入門時に docstring を使えるようにする(ちょっと大変と思うなら、生成AIを使う)ことは、プログラミングに有用だと感じました。

今後も何かあれば、Python について試して見たいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その20

2024-09-01 09:54:16 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 20番目のプログラムは、 28行の2度目の8クイーン問題で、独自の例外(exception)の定義です。

BOARD_SIZE = 8

class BailOut(Exception):
    pass

def validate(queens):
    left = right = col = queens[-1]
    for r in reversed(queens[:-1]):
       left, right = left-1, right+1
       if r in (left, col, right):
          raise BailOut

def add_queen(queens):
    for i in range(BOARD_SIZE):
       test_queens = queens + [i]
       try:
          validate(test_queens)
          if len(test_queens) == BOARD_SIZE:
             return test_queens
          else:
             return add_queen(test_queens)
       except BailOut:
          pass
    raise BailOut

queens = add_queen([])
print (queens)
print ("\n".join(". "*q + "Q " + ". "*(BOARD_SIZE-q-1) for q in queens))


再び、8クイーン問題を扱うプログラムですが、答えは1つだけ表示されます。早速これを prog-28.py というファイルにして実行してみます。

$ python3 prog-28.py
[0, 4, 7, 5, 2, 6, 1, 3]
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
$


最初に、Google Gemini による解説を抜粋します。

ここから========>
### コードの解説

#### 定数と例外
* **BOARD_SIZE:** チェスボードのサイズを定義しています。このプログラムでは、8×8のチェスボードを想定しています。
* **BailOut:** 探索中に矛盾が生じた場合に発生させるカスタム例外です。

#### 関数
* **validate(queens):**
* 引数として、既に配置されたクイーンの列番号のリストを受け取ります。
* 新たに追加しようとしているクイーンと、既に配置されているクイーンが互いに攻撃できるかどうかを検証します。
* 攻撃できる場合は、BailOut例外を発生させます。
* **add_queen(queens):**
* 再帰的な関数で、チェスボードの次の行にクイーンを配置する場所を探索します。
* すべての行にクイーンが配置できたら、その配置のリストを返します。
* 配置できない場合は、BailOut例外を発生させます。

#### メインロジック
* **queens = add_queen([]):**
* 空のリストから始めて、add_queen関数で全てのクイーンを配置します。
* **print(queens):**
* 求められたクイーンの配置(列番号のリスト)を出力します。
* **print("\n".join(". "*q + "Q " + ". "*(BOARD_SIZE-q-1) for q in queens)):**
* 求められたクイーンの配置を、チェスボードの形で視覚的に表示します。"Q"がクイーンを表し、"."が空のマスを表します。

### プログラムの仕組み
1. **add_queen**関数は、チェスボードの最初の行から始めて、各行にクイーンを配置できるかどうかを順に試します。
2. **validate**関数は、新しいクイーンを追加するたびに、そのクイーンが他のクイーンと攻撃できるかどうかを検証します。
3. 攻撃できる場合は、**BailOut**例外が発生し、**add_queen**関数は別の配置を試します。
4. 全ての行にクイーンが配置できたら、その配置が一つ見つかったことになるので、探索を終了します。
5. もし、全ての可能性を試しても有効な配置が見つからなければ、**BailOut**例外が発生し、プログラムはエラーで終了します。

### より詳しく
* **再帰:** このプログラムでは、**add_queen**関数が自分自身を呼び出す再帰的な構造になっています。再帰は、問題をより小さな部分問題に分割し、それらを解決することで全体の問題を解決する手法です。
* **バックトラッキング:** 探索中に矛盾が生じたら、一つ前の状態に戻って別の選択肢を試す手法をバックトラッキングといいます。このプログラムでも、**BailOut**例外が発生した際に、一つ前の状態に戻って探索を続けることで、バックトラッキングが行われています。

ここまで========>

Gemini もこのように、プログラムの概要は教えてくれますね。気になるところを見ていきます。

まず、クラス BailOutの定義が、「カスタム例外」(独自の例外:your own exceptions)ですね。
https://docs.python.org/ja/3/tutorial/controlflow.html#pass-statements

pass 文は何もしません。 pass は、文を書くことが構文上要求されているが、プログラム上何の動作もする必要がない時に使われます:

これは最小のクラスを作るときによく使われる方法です:


ということで、ここで言う「最小のクラス」を定義しているようです。何もしないのが重要ですね。
これは、validate() 関数(基本的には前に出てきた18lines プログラムのunder_attack()関数と同じもの)の中で使われている、raise文
https://docs.python.org/ja/3/reference/simple_stmts.html#the-raise-statement

と関連しており、

raise は最初の式を、例外オブジェクトとして評価します。これは、 BaseException のサブクラスまたはインスタンスでなければなりません。クラスなら、例外インスタンスが必要なとき、クラスを無引数でインスタンス化することで得られます。

例外の 型 は例外インスタンスのクラスで、 値 はインスタンスそのものです。

となって、if 文の条件が真ならば、例外 BailOut が発生するのですね。

その例外を受けるものがないと、普通のエラーみたいになる?ので、validdate()を中で呼び出す、
add_queen()関数の定義の中で 8lines プログラムのように、try ~ except を使って、
この例外を処理するようです。

つまり、validate()で例外BailOutが発生すると except節が実行されて、何もせずにfor文の次の i が代入されて繰り返すようです。validate()で例外が発生しなければ、test_queens の長さが、
8になるまではadd_queen()が再帰的に呼ばれて、
8になったら test_queens を返す
ので、実行例のように最初に見つかった配置を返すのでしょう。

add_queen()の最後の raise 文は念のためのような気がします。これがないと返却値がないことになるので、その判定が必要になるからだと思います。というわけで、適当に簡略化すると、

class カスタム例外:
    ....

def func_test(....):
    for
       ...
       try:
            ...
       except カスタム例外:
            ...
    raise カスタム例外


となるのが、入門者が覚えた方が良い形でしょうか。

最後は、add_queen()で生成された解 queens を出力して、それを使ってジェネレータ式でチェス盤を出力するのですね。 改行文字を頭に付けるのが面白いですね。

join()の使い方をみるために少し試してみました。分かりやすくするために改行文字を"++"にしました。
まず、ジェネレータ式ではない場合です。

>>> q = 3
>>> print("++".join(". "*q + "Q " + ". "*(8-q-1)))
.++ ++.++ ++.++ ++Q++ ++.++ ++.++ ++.++ ++.++


join()の中の文字列「1文字ずつ」に"++"を付加するのですね。つまり単純化すると、

>>> print("++".join("ABCDE"))
A++B++C++D++E
>>>


です。

これをプログラムのようにジェネレータ式にすると、

>>> queens = [2,3,4,6]
>>> print("++".join(". "*q + "Q " + ". "*(8-q-1) for q in queens))
. . Q . . . . . ++. . . Q . . . . ++. . . . Q . . . ++. . . . . . Q .
>>>


join()の使い方と共に、この事例は覚えておいて損はなさそうです。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その19

2024-08-23 22:54:36 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 19番目のプログラムは、 21行の XML,HTML パーサです。
(このブログではうまく表示できないので、XML/HTMLデータは大文字のカギ括弧を使っています。)

dinner_recipe = '''<html><body><table>
<tr><th>amt</th><th>unit</th><th>item</th></tr>
<tr><td>24</td><td>slices</td><td>baguette</td></tr>
<tr><td>2+</td><td>tbsp</td><td>olive oil</td></tr>
<tr><td>1</td><td>cup</td><td>tomatoes</td></tr>
<tr><td>1</td><td>jar</td><td>pesto</td></tr>
</table></body></html>'''

# From http://effbot.org/zone/element-index.htm
import xml.etree.ElementTree as etree
tree = etree.fromstring(dinner_recipe)

# For invalid HTML use http://effbot.org/zone/element-soup.htm
# import ElementSoup, StringIO
# tree = ElementSoup.parse(StringIO.StringIO(dinner_recipe))

pantry = set(['olive oil', 'pesto'])
for ingredient in tree.getiterator('tr'):
     amt, unit, item = ingredient
     if item.tag == "td" and item.text not in pantry:
         print ("%s: %s %s" % (item.text, amt.text, unit.text))


これを、prog-21.py というファイルにして実行してみます。

$ python3 prog-21.py
e "/home/espiya/test/python/samples/prog-21.py", line 18, in <module>
     for ingredient in tree.getiterator('tr'):
                 ^^^^^^^^^^^^^^^^
AttributeError: 'xml.etree.ElementTree.Element' object has no attribute 'getiterator'
$ (空白がずれていると思うので ^^ は tree.getiterator を表しています。)

SimpoePrograms 初めての実行できない事例ですね。ちなみに、コメントのURLももはや存在しません。

原因は、getiterator() ですね。

https://docs.python.org/ja/3.6/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.getiterator

バージョン 3.2 で非推奨: 代わりに Element.iter() メソッドを使用してください。

なので、 

iter(tag=None)

現在の要素を根とする木の イテレータを作成します。
イテレータは現在の要素とそれ以下のすべての要素を、文書内での出現順 (深さ優先順) でイテレートします。 tag が None または '*' でない場合、与えられたタグに等しいものについてのみイテレータから返されます。イテレート中に木構造が変更された場合の結果は未定義です。

ということで、 tree.iter('tr') で tree の中で、タグが tr のものを抽出できます。指示に従って、
for ingredient in tree.iter('tr'):
と変更して prog-21-01.py を作成して、2つのファイルの違いを表示した後に、実行してみます。

$ diff prog-21.py prog-21-01.py
18c18
< for ingredient in tree.getiterator('tr'):
---
> for ingredient in tree.iter('tr'):
$
$ python3 prog-21-01.py
baguette: 24 slices
tomatoes: 1 cup
$

これは、HTML 形式のデータ dinner_recipe を取り扱うプログラムですね。
つまり、開始タグ + コンテンツ + 終了タグ からなる要素(element)の集まりで、コンテンツに別の要素を含んでいること(入れ子)を許すようなデータの扱いです。

プログラムを見ていきましょう。

import 文から、使用するモジュールは xml.etree.ElementTree で、それを etree としています。

tree = etree.fromstring(dinner_recipe) は、

文字列定数で与えられた XML 断片を解析します。 XML() と同じです。 text には XML データを含む文字列を指定します。 parser はオプションで、パーザのインスタンスを指定します。指定されなかった場合、標準の XMLParser パーザを使用します。 Element インスタンスを返します。

なので、試して見ます。

>>> d = '''<html><body><table>
... <tr><td>aaa</td><td>001</td></tr>
... <tr><td>bbb</td><td>002</td></tr>
... </table></body></html>
... '''
>>>
>>> d
'<html><body><table>\n<tr><td>aaa</td><td>001</td></tr>\n<tr><td>bbb</td><td>002</td></tr>\n</table></body></html>\n'
>>> t = etree.fromstring(d)
>>> t
<Element 'html' at 0xf6d83690>
>>>

作成された Element インスタンスですが、

>>> print(tree[0].tag)
body
>>> print(tree[0][0].tag)
table
>>> for trTag in tree[0][0]:
...        print(trTag.tag, trTag.text)
...        for child in trTag:
...            print(' ',child.tag, child.text)
...
tr None
     td aaa
     td 001
tr None
     td bbb
     td 002
>>>
のように、ツリー構造になっているそうです。
ちなみに、HTMLが間違っていると、mismatched tag というエラーが出るようです。

この例の Element インスタンス t は、for 文でこのプログラムのように iter('tr')を使えば、
tr タグで囲まれた2つの td タグの部分を2つの要素として取り出せるので、

>>> for i in t.iter('tr'):
...        a, b = i
...        print(a.tag, a.text, b.tag, b.text)
...
td aaa td 001
td bbb td 002
>>>

それぞれ、タグ名は .tag 、値は .text として参照できるようになるのですね。

次に set が出てきました。集合型ですね。
https://docs.python.org/ja/3/tutorial/datastructures.html#sets

Python には、 集合 (set) を扱うためのデータ型もあります。集合とは、重複する要素をもたない、順序づけられていない要素の集まりです。 Set オブジェクトは、和 (union)、積 (intersection)、差 (difference)、対称差 (symmetric difference)といった数学的な演算もサポートしています。

中括弧、または set() 関数は set を生成するために使用することができます。注: 空集合を作成するためには set() を使用しなければなりません ({} ではなく)。後者は空の辞書を作成します。辞書は次のセクションで議論するデータ構造です。


ということで、set()以外に中括弧も使えるのですね。リストと似てますが、「順序づけられない要素の集まり」が集合の要点?だと思います。しかも「重複する要素をもたない」のですね。

チュートリアルにあるデモを修正して 3.12.5 で試してみました。

>>> number = set(['one', 'two', 'three', 'four', 'five'])
>>> print(number)
{'five', 'three', 'four', 'two', 'one'}
>>> 'four' in number
True
>>> 'seven' in number
False
>>> number = {'one', 'two', 'one', 'three', 'seven', 'three'}
>>> print(number)
{'two', 'one', 'three', 'seven'}
>>> 'four' in number
False
>>> 'seven' in number
True
>>>

ちゃんと、重複要素は除去されていますね。この例から 集合 number に対して、
a in number が、
True なら a は集合number の要素であり、
False なら a は集合number の要素ではない
となるようですね。更に、
a not in number も可能で、上の最後の例から、

>>> 'seven' not in number
False
>>>

となりました。

プログラムは、

tree = etree.fromstring()で、Elementインスタンス tree を得て、

集合 pantry を定義して、

for文で、tree.iter('tr') から1つずつ tr の中身を ingredient に代入して、

ingredientから3つの要素(タグがth または td)を、amt, unit, item に代入して、

if文で 要素item のタグ item.tag が td でかつ、 そのタグで囲まれたコンテンツ item.text が 集合 pantry に属さなければ、

要素 item: amt unit として出力する

ですね。

今回は、XML/HTML データを扱うプログラムでしたが、
これらのタイプデータを取り扱うときは Python を使おう!ということも、入門者に伝わってくる?ものでした。
特に、fromstring()で生成される、木構造のElementインスタンスが有益そうに思いました。
使うためには、様々なこのタイプのデータがどのような構造になるのかを自分で調べることになるので、
プログラミングで意外と重要な、実際にプログラミングする前に使おうとする機能を色々「試して」確認しておく、
ということが実感できるかもしれませんね。

また、Python も色々なモジュールが公開されているので、これらを活用してプログラムするというのが SimpleProgram の考え方かもしれませんね。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Raspberry Pi に Python 3.12.5 をインストールする

2024-08-16 17:04:26 | Python
2024年8月6日(日本時間8月7日)に Python 3.12.5 がリリースされました。

https://www.python.org/downloads/release/python-3125/
「Python 3.12 は Python プログラミング言語の最新メジャー リリースであり、多くの新機能と最適化が含まれています。 3.12.5 は最新のメンテナンス リリースで、3.12.4 以降の 250 を超えるバグ修正、ビルドの改善、ドキュメントの変更が含まれています。

このバージョンの Python 3.12 には、デフォルトで pip 24.2 も付属しています。ただし、古い macOS バージョンとの互換性がないため、macOS 10.9 ~ 10.12 では、インストール プロセス中 (証明書のインストール手順)、pip のバージョンが 24.1.2 にダウングレードされます。詳細については、インストーラの ReadMe とこの件に関する pip の問題を参照してください。 10.13 より古いバージョンの macOS は 2019 年以降 Apple によってサポートされておらず、それらのサポートを維持することがますます困難になっています。このリリースの 3.12 では引き続きこれらをサポートしていますが、将来の 3.12 リリースでは macOS 10.12 以前のサポートを中止せざるを得なくなる可能性があります。 (Python 3.13 ではすでにサポートが終了しています。)」
(DeepL 翻訳)

最後の、Python 3.13 ですが、10月頃に公開されるようです。楽しみですね。

早速、こちらの Raspberry Pi にインストールしてみました。

入手したファイルは、
Python-3.12.5.tar.xz
Python-3.12.5.tar.xz.asc

です。 "Public key servers" で検索して、
7169605f62c751356d054a26a821e680e5fa6305.asc
を得て、

$ gpg --import 7169605f62c751356d054a26a821e680e5fa6305.asc
$
$ gpg --verify Python-3.12.5.tar.xz.asc
gpg: 署名されたデータが'Python-3.12.5.tar.xz'にあると想定します
gpg: 2024年08月07日 05時32分27秒 JSTに施された署名
gpg: RSA鍵7169605F62C751356D054A26A821E680E5FA6305を使用
gpg: "Thomas Wouters <thomas@python.org>"からの正しい署名 [不明の]
gpg: 別名"Thomas Wouters <thomas@xs4all.nl>" [不明の]
gpg: 別名"Thomas Wouters <twouters@google.com>" [不明の]
gpg: 別名"Thomas Wouters <thomas.wouters.prive@gmail.com>" [不明の]
gpg: 別名"Thomas Wouters <thomaswout@gmail.com>" [不明の]
gpg: *警告*: この鍵は信用できる署名で証明されていません!
gpg: この署名が所有者のものかどうかの検証手段がありません。
主鍵フィンガープリント: 7169 605F 62C7 5135 6D05 4A26 A821 E680 E5FA 6305
$


キーサーバから入手した鍵を証明するものを入手していないのでこのように表示されますが、この結果とMD5 Sum が、

$ md5sum Python-3.12.5.tar.xz;echo 02c7d269e077f4034963bba6befdc715
02c7d269e077f4034963bba6befdc715 Python-3.12.5.tar.xz
02c7d269e077f4034963bba6befdc715
$


と、一致しているので問題なしとしました。

前回のように make します。例によって1時間くらいかかるので気長に待ちます。

$ tar xvf Python-3.12.5.tgz
$ cd Python-3.12.5
$ ./configure --prefix=/usr/local/python --enable-optimization --with-readline --enable-shared
$ make

# make install


以上で、インストールは完了です。実際に動作させてみます。

$ which python3
/usr/local/python/bin/python3

$ python3
Python 3.12.5 (main, Aug 16 2024, 12:49:15) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

$ pip3 --version
pip 24.2 from /usr/local/python/lib/python3.12/site-packages/pip (python 3.12)
$


無事に、Python3 3.12.5 になりました。

ついでにインストールしたばかりの 3.12.5 で、ちょっと気になった yield でリストを返す事を試してみました。

>>> a = [1]
>>> a
[1]
>>> a.append(2)
>>> a
[1, 2]
>>> a.append(3)
>>> a
[1, 2, 3]
>>>


を利用して、無限にリストの要素数を増やしていくジェネレータ関数を作ってみます。

>>> def test_list():
...       number = 1
...       a = [number]
...       while True:
...           yield a
...           number += 1
...           a.append(number)
...
>>>


この関数で生成されたジェネレータイテレータを変数 data に代入します。

>>> data = test_list()
>>> data
<generator object test_list at 0xf7573570>


作成したイテレータ data から next() でメンバーを一つずつ得てみます。

>>> next(data)
[1]
>>> next(data)
[1, 2]
>>> next(data)
[1, 2, 3]
>>> next(data)
[1, 2, 3, 4]
>>>


yield はリストも返せる事が分かったので、これは色々使い道がありそうですね。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ + yield

2024-08-12 23:53:32 | Python
今回は、SimplePrograms の 20lines プログラムの続きで、yield 関連について見てみます。

その前に、プログラミング言語学習で役立つと思っているチートシート (Cheat Sheet) を探してみました。

Python 3 Cheat Sheet
https://perso.limsi.fr/pointal/_media/python:cours:mementopython3-english.pdf

チートシートは、昔はC言語とかに対してコンピュータメーカーが紙で作って配っていたりしてたようですが、今ではインターネットから得られるので Python3 関連も用意されています。チートシートで重要なのは印刷したときに一枚の紙に収まることだと思いますが、現在のプリンタではA4 用紙の両面印刷で収まるかどうかで判断してこれをご紹介しました。

さて、yield 文に戻りますが、
https://docs.python.org/ja/3/reference/simple_stmts.html#the-yield-statement

yield 文は意味的に yield 式 と同じです。yield 文を用いると yield 式文で必要な括弧を省略することが出来ます。

yield 式及び文は generator を定義するときに、その本体内でのみ使うことが出来ます。関数定義内で yield を使用することで、その定義は通常の関数でなくジェネレータ関数になります。


なので、具体的な説明は yield式に書いてあるそうです。
https://docs.python.org/ja/3/reference/expressions.html#yield-expressions

関数の本体で yield 式 を使用するとその関数はジェネレータ関数になり、


であり、ジェネレータ関数の説明は、

ジェネレータ関数が呼び出された時、ジェネレータとしてのイテレータを返します。ジェネレータはその後ジェネレータ関数の実行を制御します。ジェネレータのメソッドが呼び出されると実行が開始されます。開始されると、最初の yield 式まで処理して一時停止し、呼び出し元へ expression_list の値を、または expression_list が省略されていれば None を返します。ここで言う一時停止とは、ローカル変数の束縛、命令ポインタや内部の評価スタック、そして例外処理のを含むすべてのローカル状態が保持されることを意味します。再度、ジェネレータのメソッドが呼び出されて実行を再開した時、ジェネレータは yield 式がただの外部呼び出しであったかのように処理を継続します。再開後の yield 式の値は実行を再開するメソッドに依存します。__next__() を使用した場合 (一般に for 文や組み込み関数 next() など) の結果は None となり、send() を使用した場合はそのメソッドに渡された値が結果になります。

なので、yield文による動きも前回通りですね。最後の方にある next()の記述が気になったので試してみると、

>>> a = iter_primes()
>>> a
<generator object iter_primes at 0xf72a7db8>
>>> b = next(a)
>>> print(b)
2
>>> b = next(a)
>>> print(b)
3
>>> b = next(a)
>>> print(b)
5
>>> b = next(a)
>>> print(b)
7
>>>

という感じで、next()は問題なく使えるようです。これは、
https://docs.python.org/ja/3/glossary.html#term-generator

(ジェネレータ) generator iterator を返す関数です。 通常の関数に似ていますが、 yield 式を持つ点で異なります。 yield 式は、 for ループで使用できたり、next() 関数で値を 1 つずつ取り出したりできる、値の並びを生成するのに使用されます。

(ジェネレータイテレータ) generator 関数で生成されるオブジェクトです。


で説明されていました。同様に glossary から、
iterable(イテラブル)が、

メンバーを一度に 1 つずつ返すことができるオブジェクト。

iterator(イテレータ)が、

データの流れを表現するオブジェクトです。イテレータの __next__() メソッドを繰り返し呼び出す (または組み込み関数 next() に渡す) と、流れの中の要素を一つずつ返します。データがなくなると、代わりに StopIteration 例外を送出します。

と、なっていました。

今回の 20lines のプログラムでは、

関数 iter_primes() 定義内で、yield 文が使われているので、その関数は「ジェネレータ関数」になり、「ジェネレータ関数が呼び出された時、ジェネレータとしてのイテレータを返」すので、そのオブジェクトを変数に代入して、next()とかで要素をひとつづつ取り出せるようになり、

それをfor 文で1000まで出力するわけですね。

さらに、組み込み関数filter() ですが、
https://docs.python.org/ja/3/library/functions.html#filter

filter(function, iterable)
iterable の要素のうち、 function が真であるものからイテレータを構築します。 iterable にはシーケンスか、イテレーションをサポートするコンテナか、イテレータを渡せます。 function が None のときは恒等関数が指定されたものとして扱われ、 iterable のうち偽であるものがすべて取り除かれます。



となるので、
filter(prime.__rmod__, numbers)
は、イテレータ numbers から、prime で割り切れる(0なので偽になる)ものを除いて、新たなイテレータを構築するわけですね。

yield も filter() も面白い動きをしますね。これが、SimplePrograms に出てきたということで、入門時に是非知っておいてほしい Python の一押しの機能ということかもしれません。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その18

2024-08-02 22:41:20 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 18番目のプログラムは、 20行の素数のふるいです。

import itertools

def iter_primes():
    # an iterator of all numbers between 2 and +infinity
    numbers = itertools.count(2)

    # generate primes forever
    while True:
        # get the first number from the iterator (always a prime)
        prime = next(numbers)
        yield prime

        # this code iteratively builds up a chain of
        # filters...slightly tricky, but ponder it a bit
        numbers = filter(prime.__rmod__, numbers)

for p in iter_primes():
    if p > 1000:
        break
    print (p)


15 lines プログラムで使った itertools モジュールのイテレータ count() を使うのですね。
https://docs.python.org/ja/3/library/itertools.html
によれば、

count()、 引数:[start,[step]]、 結果:start,start+step,start+2*step,...

となる、無限イテレータ(Infinite iterators)だそうです。
つまり、itertools.count(2) は 2以上の 1づつ増加する数列を生成するのですね。
そして、この数列は、next()で1つづつ取り出せるわけです。

>>> n = itertools.count(2)
>>> p = next(n)
>>> print(p)
2
>>> p = next(n)
>>> print(p)
3
>>> p = next(n)
>>> print(p)
4
>>>

iter_primes()関数の動きを見てみます。
メインのwhile ループの中で、 yield を無視して動作を調べると、

>>> import itertools
>>> numbers = itertools.count(2)
>>> # ここから while ループ
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
2
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
3
....................
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
31
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
37
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
41
>>>
....................

このように、毎回素数が得られるわけですね。filter() は numbers からprime で割り切れるものを除いて、新しい numbers を生成するメソッドのようです。

その後、for 文を使って、iter_primes() を1回だけ呼び出して、生成された素数のイテレータオブジェクトから1つづつ取り出して(つまり next() のように動作する?) p に代入し、その素数が1000 を越えるまで print(p) で出力するのがこのプログラムです。

しかし、普通のプログラミング言語だと、このような関数を作り出すのは難しいように思います。それを Python では、 yield 文(式?)を使ってこれを実現するようです。

関数 iter_prime() は、中で yield prime つまり、 yield 文を使っているのでジェネレータ関数になります。

最初にこのジェネレータ関数を呼び出すと、
yield 式まで実行して prime の値を返して処理を「一時停止」し、
次にこの関数の一時停止を解除すると、yield の次を実行して while ループで繰り返して yield 式の処理をして、
また一時停止します、
のようになるらしいです。

つまり、
>>> import itertools
>>>
>>> # 1回目
>>> numbers = itertools.count(2)
>>>
>>> prime = next(numbers)
>>> print(prime)
2
>>>
>>> # 2回目
>>> numbers = filter(prime.__rmod__, numbers)
>>> prime = next(numbers)
>>> print(prime)
3
>>>
>>> # 3回目
>>> numbers = filter(prime.__rmod__, numbers)
>>> prime = next(numbers)
>>> print(prime)
5
>>>
===== 以下省略 ====

となるので、ここで生成される関数iter_primes()は、素数の無限イテレータ・オブジェクトを生成し、 for 文の in で指定され最初に1回だけこの関数が実行され、生成されたジェネレータオブジェクトから次々と素数が p に代入されるみたいです。

これは for文の復習として、以下のリファレンスを参考に説明してみました。

------------------------
for 文は、シーケンス (文字列、タプルまたはリスト) や、その他の反復可能なオブジェクト (iterable object) 内の要素に渡って反復処理を行うために使われます:

for_stmt ::= "for" target_list "in" starred_list ":" suite
            ["else" ":" suite]

starred_list 式は一度だけ評価され、評価結果として イテラブル オブジェクトを返す必要があります。そのイテラブルから イテレータ が作られます。

------------------------

つまり、
>>> a = iter_primes() # 一回しか関数を呼び出さないことを強調します。
>>> a
<generator object iter_primes at 0xf7027db8>
>>> for p in a:
...       if p > 6:
...          break
...       print(p)
...
2
3
5
>>>

です。 この動きはとても興味深いです。

ちょっと理解が怪しくなっているように感じているので、次回以降に yield や filter() を調べて見たいと思います。
コメント (3)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする