coLinux日記

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

SimplePrograms で Python を学ぶ その09

2024-04-26 21:33:24 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 9番目のプログラムは、ファイルのオープン です。

# indent your Python code to put into an email
import glob
# glob supports Unix style pathname extensions
python_files = glob.glob('*.py')
for file_name in sorted(python_files):
    print (' ------' + file_name)

    with open(file_name) as f:
        for line in f:
            print ('    ' + line.rstrip())

    print()


9行じゃないですが、最初のコメント行は関係無いような気がするので無視してみます。

インポートする glob モジュールは、

「ディレクトリのワイルドカード検索からファイルのリストを生成するための関数を提供」

をするものです。bash でもお馴染みの 「*.txt」 とか、が使えるのですね。
glob の glob()メソッドは、この形式でファイル名を得ることができるはずで早速試してみます。

あるディレクトリ下で、prog-001.py と prog-002.py ファイルがあるとします。

$ ls
prog-001.py prog-002.py
$

そのディレクトリ下でpython3 を起動してみました。

>>> import glob
>>> glob.glob('*.py')
['prog-001.py', 'prog-002.py']
>>>
>>> glob.glob('*002*')
['prog-002.py']
>>>

各要素がパターンに合致するファイル名であるリストが得られますね。
リストが得られるということは、for文の sorted()は、引数のリストの各要素が文字列のものをソートしてくれそうです。

>>> sorted(['xyz','abc','dcb','dcc'])
['abc', 'dcb', 'dcc', 'xyz']
>>>

これで、python3 を実行中のディレクトリ下にあるパターンが合致するファイル名を python_files 変数にリストとして代入し、
for文で、それをソートして、一つづつ file_name に代入して、ループの中身を実行することが分かりました。

インデントされているループの中身を見てみましょう。

>>> print('aaaa' + 'bbbb')
aaaabbbb
>>>

なので、 文字列 + 文字列 は、文字列の連結ですね。

for ループの中の with は、with文ですね。
https://docs.python.org/ja/3/reference/compound_stmts.html#with

この解説は難しいので、チュートリアルを見ると、

「ファイルオブジェクトを扱うときに with キーワードを使うのは良い習慣です。 その利点は、処理中に例外が発生しても必ず最後にファイルをちゃんと閉じることです。」

要するに、with文を使ってファイルをオープンして、中身を読み込むのが目的で、as の後ろにあるファイルオブジェクト f が C言語で言うところのファイルポインタですね。
なぜこのようなものが必要かと言えば、複数ファイル同時オープン時に識別子として使うためのものでしょう。
しかも、with で open()すれば、close() ? を陽にしなくて良い?のが便利です。

Pythonでは read 文みたいなもの ( f.read() ) を使わなくても、for文でそのまま1行づつ読み込めて、

with open(ファイル名) as f:
    for line in f:
        読み込んだファイル内の1行 line に対する処理

と出来ると覚えておけば、今後の Python プログラミングで使えそうです。

https://docs.python.org/ja/3/library/stdtypes.html?highlight=rstrip#str.rstrip
から、line.rstrip()は、

「文字列の末尾部分を除去したコピーを返します。引数 chars は除去される文字集合を指定する文字列です。」

の chars が省略された場合で、行 line の後ろの空白文字が除去されるわけですね。
そこでわざと prog-002.py の1行目に長い空白文字を加えて見ます。

$ cat prog-002.py
#!/usr/bin/python3
name = input('What is your name?\n') #(分かりにくいですがここに空白文字)
print ('Hi, %s.' % name)
$
ちょっとうまく表示されそうにないので、 od コマンドで明確にしておきます。odの出力も相当乱れて表示されているので、実際に確認してみてください。

$ od -cx prog-002.py
0000000 #   !   /   u   s   r   /   b   i   n   /   p   y   t   h   o
     2123 752f 7273 622f 6e69 702f 7479 6f68
0000020 n   3   \n   n   a   m   e   =   i   n   p   u   t   (
     336e 6e0a 6d61 2065 203d 6e69 7570 2874
0000040 '   W   h   a   t      i   s   y   o   u   r   n   a
     5727 6168 2074 7369 7920 756f 2072 616e
0000060 m   e   ?   \   n   '   )
     656d 5c3f 276e 2029 2020 2020 2020 2020
0000100
     2020 2020 2020 2020 2020 2020 2020 2020
*
0000220 \n
     2020 2020 2020 2020 2020 2020 2020 0a20
0000240 p   r   i   n   t   (   '   H   i   ,   %   s   . '
     7270 6e69 2074 2728 6948 202c 7325 272e
0000260 %   n   a   m   e   )   \n
     2520 6e20 6d61 2965 000a
0000271
$

ここで、その上のディレクトリに、先頭に #!/usr/bin/python3 行を付加したこのプログラムの prog-009.py を作成し実行します。

$ ../prog-009.py
------prog-001.py
#!/usr/bin/python3
print ('Hello, world!')

------prog-002.py
#!/usr/bin/python3
name = input('What is your name?\n')
print ('Hi, %s.' % name)

$

プログラムの最後の print()で空行が表示されます。
念のため出力の該当部分を od 出力の抜粋で明らかにしておきます。

0000220 y   o   u   r   n   a   m   e   ?   \   n   '   )   \n
     7920 756f 2072 616e 656d 5c3f 276e 0a29
0000240      p   r   i   n   t   (   '   H   i   ,

for文の中にwith文、その中にfor文があるので、この辺のインデントの仕方は覚えておく必要がありますね。

ここまできて、だいぶ Python にもなれたので、そのまま続けたいと思います。
コメント (1)
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その08

2024-04-19 16:10:01 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 8番目のプログラムは、表題から コマンドライン引数 と 例外処理 です。

# This program adds up integers that have been passed as arguments in the command line
import sys
try:
     total = sum(int(arg) for arg in sys.argv[1:])
     print ('sum =', total)
except ValueError:
     print ('Please supply integer arguments')


# から command line まで1行でしたので、7行?しかないです。

Python でのコメントの仕方を1行目が示しています。

「Python におけるコメント文は、ハッシュ文字 # で始まり、物理行の終わりまで続きます。」

ですね。改行するとプロンプトが >>> になるので、継続行にも「 # 」を付けるようです。

>>> # comment-1
>>> # comment-2
>>> #   comment-3

さて、import で sys モジュールをインポートすると、

「スクリプト名と引数を指定してインタプリタを起動した場合、スクリプト名やスクリプト名以後に指定した引数は、文字列のリストに変換されて sys モジュールの argv 変数に格納されます。 import sys とすることでこのリストにアクセスできます。」

となり、ここでは sys.argv を使っています。argv と言えば C言語由来でコマンドラインの引数を表しますが、Python も同じですね。
この場合、コマンドとして指定された引数を受け取るので、今までの対話型ではこうなってしまいます。

>>> import sys
>>> sum(int(arg) for arg in sys.argv[1:])
0
>>>

これでは、確認しようがないのでプログラムをファイルに保存してそちらを実行します。
8番目のプログラムを prog-008.py というファイルに保存して、python3 で実行すれば良さそうです。

$ python3 prog-008.py
sum = 0
$
$ python3 prog-008.py 1 2 3 4
sum = 10
$

予想通り、プログラムをコマンドとして実行すると引数が指定できて sys.argv を通してプログラムに渡されました。

$ python3  プログラムファイル  このプログラムの引数1  このプログラムの引数2  .......

ですね。コマンドっぽくするには、bash なら同じディレクトリにシェルスクリプト prog-008.sh を作って、実行可能にできます。

$ cat prog-008.sh
#!/bin/bash
python3 prog-008.py $*
$
$ chmod u+x prog-008.sh
$
$ ./prog-008.sh 1 2 3 4
sum = 10
$

しかし、prog-008.py の先頭に bash のために python3 を指定することもできるので、こちらを使用するのが一般的でしょう。

まず which コマンドで Python3 のフルパス名を求めましょう。

$ which python3
/usr/bin/python3
$

なので、prog-008.py の1行目に次のものを挿入します。

#!/usr/bin/python3

もしかして、8番目のこのプログラムが7行なのは、この1行を挿入するためかもしれませんね。
このファイルを実行可能にすると、

$ chmod u+x prog-008.py
$
$ ./prog-008.py 1 2 3
sum = 6
$

となりました。pythonで作成されたプログラムは、Linux ではこんな感じで作成するのですね。
その時は、sys と argv を使用すると覚えます。

さて、 try: と except ValueError: は何でしょうか。これは、
https://docs.python.org/ja/3/tutorial/errors.html
の、「8.3 例外を処理する」に説明されています。

「まず、 try 節 (try clause) (キーワード try と except の間の文) が実行されます。
何も例外が発生しなければ、 except 節 をスキップして try 文の実行を終えます。」


だそうです。そのまま覚えましょう。

try: の下のインデントされた行を見てみます。
sys.argv[1:] は、引数配列(コマンド行そのもので、空白で区切ったもの)から部分的に要素を取り出してリストにするやり方のようです。
そこで、prog-008.py の try 節を、以下のようにした prog-008-01.py と、
さらに sys.argv を sys.argv[1:] にした prog-008-02.py を作成します。

try:
     total = sys.argv
     print (total)

$ ./prog-008-01.py 1 2 3
['./prog-008-01.py', '1', '2', '3']
$

$ ./prog-008-02.py a b c
['1', '2', '3']
$

ということで、sys.argv は、引数のリストでした。ここで sys.argv[0] はプログラム名ですから、
sys.argv[1] が1番目の引数、sys.argv[2] が2番目の引数 .... となるので、
[1:] の意味は、2つめ(0が先頭)以降の要素を表すので、sys.argv[1:] は引数全部を要素(文字列)にしたリストということですね。

また、int()ですが、

>> 1 + 2
3
>>> 1 + '2'   # '2' は、数字からなる文字列です。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> 1 + int('2')
3
>>>

つまり、数値しか使えない算術演算を実行するために、引数は文字列なのでint()を使って整数に変換するという意味ですね。

今回のプログラムは、引数をジェネレータ式を使って数値の複数要素からなるジェネレータオブジェクトにしてsum()で合計するわけです。

さて、引数が数値を表す文字列でなかったらどうなるでしょうか。

$ ./prog-008.py 1 2 3 4
sum = 10
$ 
$ ./prog-008.py 1 2 3 'abc'
Please supply integer arguments
$

引数が文字列なので int() で例外が発生したようです。すると、try節の残り
     print ('sum =', total)
はスキップして、except ValueError: へ移動して、その後の、
     print ('Please supply integer arguments')
を実行するわけですね。例外処理の方法が分かるプログラムでした。

8番目にしてこのような例外処理がでてきたので、この後のプログラムも凄そうです。
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その07

2024-04-12 23:14:12 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の、 7番目のプログラムは、表題から「データ構造の辞書型」 と 「ジェネレータ式」です。

prices = {'apple': 0.40, 'banana': 0.50}
my_purchase = {
   'apple': 1,
   'banana': 6}
grocery_bill = sum(prices[fruit] * my_purchase[fruit]
      for fruit in my_purchase)
print ('I owe the grocer $%.2f' % grocery_bill)


7番目にして Python らしい?機能が紹介されています。5行で表せそうですが7行になっています。つまり、長くなってしまった行を複数行にする方法が分かりそうです。

ここからは、

Pythonチュートリアル
https://docs.python.org/ja/3/tutorial/index.html

も、仕様を調べるのに参照したいと思います。Pythonリファレンスマニュアルは、表記法が、
https://docs.python.org/ja/3/reference/introduction.html#notation

で、規定されている「modified Backus–Naur form (BNF バッカス・ナウア記法) grammar notation」を使っているので、
今更ですが入門には向かない気がしたからです。早い話が、Python自身を作成するのに向いている表記方法ですから。

試しに、リスト型について見てみると、

「Pythonは多くの 複合 (compound) データ型を備えており、複数の値をまとめるのに使われます。最も汎用性が高いのは リスト (list) で、コンマ区切りの値 (要素) の並びを角括弧で囲んだものとして書き表されます。リストは異なる型の要素を含むこともありますが、通常は同じ型の要素のみを持ちます。」

と、より分かりやすそうです。Pythonでは、配列とは言わずに 「リスト」 と言った方が良さそうですね。

さて、Python は、データ構造の型の一つとして「辞書型」が組み込まれているそうです。bashとかで言う連想配列ですね。

「辞書は キー(key): 値(value) のペアの集合であり、キーが (辞書の中で)一意でなければならない、と考えるとよいでしょう。」

だそうです。最初の1行がそれを表しており、以下の様になるわけです。

  {キー1:値, キー2:値, ..... }

1行目は、キー「apple」に対して値「0.40」、キー「banana」に対して値「0.50」となる辞書を prices に代入し、
2から4行目は、キー「apple」に対して値「1」、キー「banana」に対して値「6」となる辞書を my_purchase へ代入することになります。1行で表せるのに3行にしているのは、辞書型の定義は長くなる可能性があるのでとても1行では収まらないので 「複数行」 で定義したくなるからでしょう。そこでプログラムのようにインデントで継続行として、続けることができるようになっているわけですね。試してみます。

>>> a = {
...      'apple': 1,
...      'banana': 2,
...      'orange': 3 }
>>>

for文やwhile文などと同じプロンプトがでてきましたが、最後の行に '}'があると終了とみなすようです。

同様に、5、6行もつながっていて継続行はインデントで指示するようです。そこで、わざと5行目を分割して、全体を8行にして実行結果をみてみましょう。

>> prices = {'apple': 0.40, 'banana': 0.50}
>>> my_purchase = {
...      'apple': 1,
...      'banana': 6}
>>> grocery_bill = sum(prices[fruit] *
...      my_purchase[fruit]
...      for fruit in my_purchase)
>>> print ('I owe the grocer $%.2f' % grocery_bill)
I owe the grocer $3.40
>>>

ちゃんと継続していますね。表示された結果から、りんごとバナナの価格と個数から合計金額を求めていることが分かります。
詳しく見ていきましょう。

まず、辞書型の各キーに対応する値はどのようにして参照できるのでしょうか。連想配列と似てるので、
>>> prices['apple']
0.4
>>> prices['banana']
0.5
>>> prices['orange']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'orange'
>>>

キーに合致すると対応する値を返し、合致しないと KeyError になるのですね。

次に sum()を調べます。

>>> b = sum(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>>

なので、表計算で使うようなsum関数とは違うようです。最初の引数はリストと予想して試します。また、関数の出力はそのまま表示されるのが分かったのでそうします。
もちろんこの出力は変数に代入できます。

>>> sum([1,2,3,4])
10
>>> sum([1,2,3,4],5)
15
>>> sum([1,2,3,4],5,10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sum expected at most 2 arguments, got 3
>>> sum([1,2,3,4],[1,2,3,4])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> sum([1,2,3,4],sum([1,2,3,4]))
20
>>>

2つめの引数は、数値のみ許すようです。複雑なのはその内出てくるでしょう。

今回のプログラムは二つの辞書から同じキーの値をかけ算して、結果をすべてたすのが目的ですが、リストの場合、

>>> [1,2]*2
[1, 2, 1, 2]
>>> [1,2]*[1,2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'list'
>>>

となってしまうので、5,6行目のジェネレータ式を使うようです。用語集、
https://docs.python.org/ja/3/glossary.html
から、ジェネレータ式は、以下の様なものです。

「イテレータを返す式です。 普通の式に、ループ変数を定義する for 節、範囲、そして省略可能な if 節がつづいているように見えます。 こうして構成された式は、外側の関数に向けて値を生成します:」

sum()の中身(ジェネレータ式)をみると以下の様になっています。

prices[fruit] * my_purchase[fruit] for fruit in my_purchase

ここの for は for節 ですね。本来ジェネレータ式はカッコでくくるのが正しいのですが、

「関数の唯一の引数として渡す場合には、丸括弧を省略できます。」

だそうです。そこで、このジェネレータ式を丸括弧で囲むと、変数に代入できてこのように表示されます。

>>> c = (prices[fruit] * my_purchase[fruit] for fruit in my_purchase)
>>> print(c)
<generator object <genexpr> at 0x7691be40>
>>>

ジェネレータ式は、ジェネレータオブジェクト?が生成されるようです。前に使ったenumerate()に似ています。
このジェネレータオブジェクトをsum() の引数に与えると要素が加算されるようです。

>>> sum(c)
3.4
>>>

つまり今回の式では、
( forの後ろの変数を使った算術式 for 変数 in 辞書 )
と考えると、
算術式が prices[fruit] * my_purchase[fruit]、
変数が、 fruit、
辞書が、 my_purcharse
ですね。
このジェネレータ式の意味は、辞書の中からキーを取りだして変数に代入し算術式で使って計算する、
を辞書のキーの数だけ行って、ジェネレータオブジェクトを生成する、ということらしいです。

ここで、再び算術式がでてきました。やはり他のプログラミング言語と同様な四則演算の形式ですね。
今回は * を使った「乗算」ですが、「足し算」に代えて試してみます。辞書の代わりに配列にします。

>>> sum( i + 1 for i in [1,2,3,4])
14
>>>

これで使い方が分かりました。辞書なら、各キーが変数に代入されて算術式が実行されるわけです。

ところで、

>>> c = (prices[fruit] * my_purchase[fruit] for fruit in my_purchase)
>>> d = c
>>> sum(d)
3.4
>>> sum(d)
0
>>> sum(c)
0
>>>

このジェネレータオブジェクトですが、どうやら1回しか使えないようです。
しかも、別の変数にコピーしてもジェネレータオブジェクトはコピーが生成されず
2つの変数とも1回しか使えなくなるようです。

更に、ジェネレータ式で生成された変数(ここでは fruit )は、for文とかと異なり参照できません。
>>> print(fruit)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'fruit' is not defined
>>>

興味深いですね。
7番目にして、かなり複雑な話になりました。これでPython入門しようとしても無理があるかもしれませんが、このまま続けます。
コメント (3)
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その06

2024-04-05 16:08:07 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 6番目のプログラムは、import と正規表現です。
(以下で、¥と表示されている場合は、バックスラッシュです。)

import re
for test_string in ['555-1212', 'ILL-EGAL']:
   if re.match(r'^\d{3}-\d{4}$', test_string):
      print (test_string, 'is a valid US local phone number')
   else:
      print (test_string, 'rejected')


1行目の import ですが、

「ある 1 つの module にある Python コードから他のモジュールを インポート することで、そこにあるコードへアクセスできるようになります。 import 文はインポート機構を動かす最も一般的な方法ですが、それが唯一の方法ではありません。」

import文で、re というモジュールをインポート(つまり、使えるように)することですね。
これで、3行目の re.match() が使えるようになるという、他のオブジェクト指向の言語で良くある形式ですね。

次に、
https://docs.python.org/ja/3/library/re.html
から Python の正規表現の意味を調べてみます。

まずは、このプログラムの実行結果です。(例によって、インデントがある前提でご覧下さい。)

>>> import re
>>> for test_string in ['555-1212', 'ILL-EGAL']:
... if re.match(r'^\d{3}-\d{4}$', test_string):
... print (test_string, 'is a valid US local phone number')
... else:
... print (test_string, 'rejected')
...
555-1212 is a valid US local phone number
ILL-EGAL rejected
>>>

注目するのは、print()で、カンマで区切った複数の引数を与えると、連続して表示されると言うことです。
確かめて見ましょう。引数が一つの場合は今まで通りの表示です。

>>> print(test_string)
ILL-EGAL

これが2つの引数になると、1文字の空白を挟んで表示されることが確認できます。
>>> print('aaa', 'bbb')
aaa bbb
>>>

さて、for で、2要素のリストを定義して先頭の要素から順番に test_string に値を代入して 2回ループするのが分かります。
ループする中身ですが、if 文が出てきました。形式は以下の様になると思います。

if じょうけん:
条件を満たした場合の処理(インデントする)
else:
条件を満たさない場合の処理(インデントする)

恐らく他のプログラム言語のように、条件のブール値が真(True)なら最初の処理を、偽(False)ならelse:の後を処理する、となるのでしょう。条件の形式(条件式?)は多くのプログラミング言語と一緒と推定して試してみましょう。
>>> a = 'abc'
>>> if a == 'abc':
...     print('Yes')
... else:
...     print('No')
...
Yes

>>> if a != 'abc':
...     print('Yes')
... else:
...     print('No')
...
No
>>>
間違いなさそうですね。

Pythonではインデントが重要なので、「 if 条件: 」 と 「 else: 」のインデントが揃っている必要がありそうです。
ここで、else: のインデントをずらしてみましょう。
>>> if a != 'abc':
...     print('Yes')
...   else:
File "<stdin>", line 3
else:
^
IndentationError: unindent does not match any outer indentation level
>>>
やはり、エラーになりました。インデントが揃っているかどうかで、if と else の対を決定しているようです。

プログラムに戻って、3行目のif文の条件を見てみましょう。

re.match(r'^\d{3}-\d{4}$', test_string)

re のメソッドの match を使っています。つまり、reモジュールが含む match()関数みたいなものですね。
1番目の引数の正規表現と2番目の引数の値が一致したら条件を満たす(真)となるものも一般的です。

正規表現と言えば、grep 、 awk、 sed コマンドなどで使われますが、

「{n}」 は、直前の1文字がちょうどn個の並び
「^」 は、直後の正規表現が文字列の先頭であることを示す
「$」 は、直前の正規表現が文字列の末尾であることを示す

は、一般的な解釈で問題ないですね。

Pythonの正規表現では、

\d は、特殊シーケンスであり、

「Unicode (str) パターンでは:
任意の Unicode 10 進数字 (Unicode 文字カテゴリ [Nd]) にマッチします。これは [0-9]
とその他多数の数字を含みます。 ASCII フラグが使われているなら [0-9] のみにマッチします。
8 ビット (bytes) パターンでは:
任意の 10 進数字にマッチします。これは [0-9] と等価です。」


とのことですから、ここは 8 ビットパターンなので、\d{3} は0~9の数字3文字を示し、\d{4} は0~9の数値4文字を示すようです。

と言うことで、'^\d{3}-\d{4}$' は、「3文字の数字-4文字の数字」となる8文字の文字列と一致するものを表すようです。

残りのその前の'r'文字は何でしょう。

これは、正規表現の

「バックスラッシュの使い方は、 Python の文字列リテラルにおける同じ文字の使い方と衝突します。」 

なので必要で、

「これを解決するには、正規表現パターンに Python の raw 文字列記法を使います。 'r' を前置した文字列リテラル内ではバックスラッシュが特別扱いされません。」

だそうです。つまり、バックスラッシュ「\」を分かりやすく使うために 「r」 が必要だということですね。

正規表現でバックスラッシュを使うなら r を頭に付けると覚えておけば良さそうです。

念のため、re.match() の出力を調べて見ましょう。4文字の数字かどうか、の例です。

>>> a='1234'
>>> b='567'
>>> import re
>>> c=re.match(r'^\d{4}$',a)
>>> d=re.match(r'^\d{4}$',b)
>>>
>>> print(c)
<_sre.SRE_Match object; span=(0, 4), match='1234'>
>>> print(d)
None
>>>

if 文は、「None」で無ければ条件を満たすとなるようです。
正規表現にマッチした場合に返されるものを 「マッチオブジェクト」 と言うそうです。

「マッチオブジェクトのブール値は常に True です。」

なので、if文の条件に使えるようです。つまり、

>>> if c:
... print('TRUE')
...
TRUE
>>> if d:
... print('TRUE')
...
>>>

となるわけです。マッチオブジェクトの使い方は、今後さらに分かるかもしれません。
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その05

2024-03-31 09:00:55 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 5番目のプログラムは、関数定義です。

def greet(name):
print ('Hello', name)

greet('Jack')
greet('Jill')
greet('Bob')


最初の2行が関数定義で、残りの3行が関数の実行例ですね。実行結果は次の通りです。(例によって、インデントがある前提でご覧下さい。)
>>> def greet(name):
... print('Hello', name)
...
>>> greet('Jack')
Hello Jack
>>> greet('Jill')
Hello Jill
>>> greet('Bob')
Hello Bob
>>>

関数定義は、関数名と引数を決めて、その引数を使って実際の関数の動きを指定する、のが一般的だと思います。と、言うことで

def 関数名(引数1,引数2....):

が Python での関数定義ですね。最後が 「:」 で終わるのは for や while と一緒ですね。ループの例と同じくインデントが続く限り関数の中身(動きを指定する部分)でしょう。
外から関数に値が引数に渡され、関数の中身に引数の名前を使うとこの値が使えるわけです。

試して見ましょう。二つの引数を指定した例を試します。

>>> def abc(f, g):
...      print(f)
...      print(g)
...
>>> abc(1,2)
1
2
>>>

考えずに複数引数定義を試みましたが、やはり複数の引数の分離はカンマでした。
関数のもう一つの重要機能である出力方法はこの段階ではまだ出てきていません。

ちなみに、Python では、入力した行の履歴を覚えているので、矢印キーで簡単に再現できます。この履歴は Python を終了してもう一度 Python を動かしても有効なので、複数行の関数定義を試すときに活用できます。これは便利ですね
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その04

2024-03-23 09:31:48 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 4番目のプログラムは、

parents, babies = (1, 1)
while babies < 100:
print ('This generation has {0} babies'.format(babies))
parents, babies = (babies, parents + babies)


1行目の右側がタプルですので、これはタプルの代入に関する例ですね。試して見ましょう。
Pythonでは、"#"文字の後ろがコメント扱いだそうで、これを利用してみました。

>>> a = (1,2)   # タプルをそのまま変数に代入できる。
>>> print(a)
(1, 2) 
>>> b,c = a     # タプルの各要素の値ををそれぞれ変数に代入できる
>>> print(b)
1
>>> print(c)
2
>>>

つまりタプルを変数に代入するときに、= の前にタプルの要素分の変数をカンマで区切って指定することができ、各変数にタプルのそれぞれの要素が代入されるようです。

2行目以降は forに代わる while ループを表すようで、while文と呼ぶようです。
https://docs.python.org/ja/3/reference/compound_stmts.html#while

while ループと言えば、その後は条件で、その条件を満たす間はループを繰り返すのが一般的ですが、pythonの場合も同様ですね。
条件の終わりは for と同様に 「:」 で示すようです。条件は一般的な表記と同じで babies が 100 より小さい場合はループを継続する、です。ループの中身がインデントする点は、forループと同じですね。

3行目の print()は、formatメソッドを利用していますが、置換フィールドとして数値を指定する例ですね。

おそらく複数の数値が使えて、formatメソッドの中のカンマで区切ったものの値に対応すると思われます。今回は babies の値だけなので {0} ですね。先頭が0番なのが python の標準のようです。

4行目もタプルを変数に代入する例で、今回のタプルはその場で作成しています。

babies と parents + babies の計算結果でタプルを新たに作成し、それを parents と babies に代入するということです。当然前の値はここで廃棄されるわけです。それで babies の値がどんどん増えるわけです。

実行結果はこんなかんじです。(例によって、while文の中のインデントはうまく表示されない可能性があるので、インデントがある前提でご覧下さい。)

>>> parents, babies = (1, 1)
>>> while babies < 100:
... print ('This generation has {0} babies'.format(babies))
... parents, babies = (babies, parents + babies)
...
This generation has 1 babies
This generation has 2 babies
This generation has 3 babies
This generation has 5 babies
This generation has 8 babies
This generation has 13 babies
This generation has 21 babies
This generation has 34 babies
This generation has 55 babies
This generation has 89 babies
>>>

つまりフィボナッチ数列の第2項目から11項目までです。
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その03

2024-03-15 08:46:37 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 3番目のプログラムは、

friends = ['john', 'pat', 'gary', 'michael']
for i, name in enumerate(friends):
print ("iteration {iteration} is {name}".format(iteration=i, name=name))


です。最後の行は表示で折り曲げられていたとしても、長い1行です。

3行なのでプログラムらしくなって来ました。1行目は形から配列ですね。多くの言語のように配列名の後に"[" と "]" があり、各要素を "," で分けるのですね。

説明文からfor によるループ表現と、enumerate() というビルトイン関数と、"%"とは別のフォーマットの方法がわかるようです。早速実行してみましょう。長い行は折れ曲がっているように見えますが1行です。

>>> friends = ['john', 'pat', 'gary', 'michael']
>>> for i, name in enumerate(friends):
... print ("iteration {iteration} is {name}".format(iteration=i, name=name))
...
iteration 0 is john
iteration 1 is pat
iteration 2 is gary
iteration 3 is michael
>>>

ここで各行のインデントが for ループの中を表すことが分かります。ループの中のプロンプトが "..." に変わるので、インデントしている行が続く限りループの中にあることになるようです。(インデントされていないように見える場合はインデントがあるという前提でご覧下さい。)ちなみに、... の後にインデントのない行を入れると

IndentationError: expected an indented block

のエラーになります。

問題は、for の後の、

i, name in enumerate(friends):

です。 まず Python では for の終わりに":"を付けるようです。よくある言語のように、 in の後ろのものから一つデータを生成して、in の前のものに代入するようです。in の前に ',' で分けた2つの変数に代入しているようですが、流石に類推は困難です。ここは、

The Python Language Reference
https://docs.python.org/3/reference/

を参照することにします。ちなみに、日本語ページはこちらです。(今はブラウザから適時翻訳できるので、そちらも活用できますね。)

https://docs.python.org/ja/3/reference/

リファレンスマニュアルで重要なのは索引ですが、右上の検索窓でも検索できます。

「enumerate(iterable,start=0)
enumerate オブジェクトを返します。 iterable は、シーケンスか iterator か、あるいはイテレーションをサポートするその他のオブジェクトでなければなりません。 enumerate() によって返されたイテレータの __next__() メソッドは、 (デフォルトでは 0 となる start からの) カウントと、 iterable 上のイテレーションによって得られた値を含むタプルを返します。」


と言うわけで、enumerate()関数は0から始まるカウンターとそれに対応するfriendsの要素の値を「タプル」(tuple)というペアにして返すようです。最初のカウンターは、friendsの何番目の要素かを示す値ですね。つまりループするたびに、enumerate()は、
0 , john
1 , pat
.....
のようにタプルを返す?わけです。返されたタプルの2つの値をそれぞれ i , name に代入して、friendsの全要素終了するまでループするのがこの記述ですね。

このenumerate()ですが、
>>> a,b = enumerate(friends,start=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
のような代入はできず。以下のように1つの変数には代入できます。
>>> a = enumerate(friends,start=0)

しかし、aの値は、
>>> print(a)
<enumerate object at 0x768749e0>
>>>
となるので、そのため a,b = のような代入ができなかったようです。当面は for の中で使うだけにしておいたほうが良さそうです。

ループの中の print()ですが、% の代わりに文字列の後ろに.format()が付加されています。「.」で繋ぐこういう形式はオブジェクトに対するメソッドとなることがよくあるものですが、python でも formatメソッドと言うそうです。

オブジェクトにあたる文字列は「置換フィールド」を含む文字列だそうで、{iteration} と {name} が置換フィールドに「キーワード」を指定した場合です。このキーワードの値は、formatメソッドのなかで、
キーワード=値
の形式で指定できるようです。ですから、
.format(iteration=i, name=name)
で、キーワードiterationの値を変数iの値に、キーワードnameの値を変数nameの値に指定するということのようです。
キーワードの値は文字列の指定場所に埋め込まれて、print()がその文字列を表示するわけですね。

formatメソッドは、他にも色々できそうなので今後に期待します。

また、
https://docs.python.org/ja/3/reference/compound_stmts.html#for
によると、「for文」と呼ぶのですね。

今後はプログラムからの推定が難しくなりそうなので、リファレンスを参考にすることになりそうです。
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その02

2024-03-09 21:43:30 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 2番目のプログラムは、

name = input('What is your name?\n')
print ('Hi, %s.' % name)

です。

 ここは、1行づつ実行してみます。

>>> name = input('What is your name?\n')
What is your name?
Espiya       (Espiyaと入力しました。)
>>> print ('Hi, %s.' % name)
Hi, Espiya.
>>>

 多くのプログラミング言語では、「 = 」は変数への代入を表します。Python でも同じですね。つまり name は変数を表します。代入する値は、input()の出力です。この行を実行すると、関数 input() の引数の文字列が標準出力から表示され、入力待ちになります。ここで「Espiya」と入力すると、この入力した文字列がinput()の出力となり、nameに代入されるわけです。

 次の行のprint()関数の引数が、

%s を含んだ文字列 % 変数

となっています。

 その形状から「 % 」は演算子と思われますが、左の文字列に %s が含まれているので、右側の変数の値を %s の位置に埋め込む演算子であることが、print()の表示から確認できます。早速 Hello, world! のプログラムが使われおり、1番目のプログラムの重要性が分かります。%s があれば、整数を埋め込む %d や小数を表す %f がありそうですので試して見ます。

 まず変数への代入を使って変数a に数字を代入します。
>>> a = 1.234
>>> print(a)
1.234
>>>
print()の引数に変数を入れるとそのまま表示されますね。(空白文字の連続は正しく表示されない可能性があります。実際の Python で確かめて下さい。)

>>> print('a is %d .' % a)
a is 1 .
>>> print('a is %f .' % a)
a is 1.234000 .
>>> print('a is %.3f .' % a)
a is 1.234 .
>>> print('a is %10.3f .' % a)
a is 1.234 .
>>>

 予想通り、C言語で言うところのフォーマット指定子が使えそうです。%f の.3 は小数点以下3桁表示で、%10.3f は10文字の中に右詰めで小数点以下3桁表示の数字を入れる事ですね。

 ところで、print()の「出力」としないで「表示」としたのは、print()を実行しても通常の関数とは異なり出力があってその値を変数に代入できないからです。いい加減ですが、一応切り分けて書いています。

 例えば、

>>> print( 'a is %f .' % a )
a is 1.234000 .
>>> b = print( 'a is %f .' % a )
a is 1.234000 .
>>> print( b )
None
>>>

のように print() の出力を bに代入しても、bの値は 「None」です。
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その01

2024-03-02 21:55:47 | Python
今回から、Raspberry Pi を使って Python を学ぼうと思います。と、言っても入門等は多数のサイトがあるので、別の方法を探ってみます。

そこで目を付けたのは、Python の公式サイト、

https://www.python.org/

です。ここの、「Documentation」のサブメニュー「Beginner's Guide」をクリックして、そこの「Learning Python」にあるプログラム経験者用の「BeginnersGuide/Programmers」をクリックして、「BeginnersGuide/Overview」を見ると、「SimplePrograms」という短いサンプルプログラムをまとめたものがありました。

SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

見ると、1行から、2行、3行、・・・と順番にプログラムの行数が増えて、複雑になるようにまとめられています。段々複雑になるそうで初心者向けサンプルプログラム集としてとても凝っています。

最後の方は難しそうなのでどうなるか分かりませんが、今回はこれを使って実際に Python を学んで見たいと思います。

一つ一つのプログラムは、実際に Raspberry Pi の python3 で1行づつ実行して、確かめて理解を深めます。いちから文法を見てというのはやらないで、その他のプログラミング言語からの類推で個々のサンプルプログラムを試して、確認を取るやり方で行います。

まずは、python3 を起動してみます。(Raspbian 12 の標準のものです。)

$ python3
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

バージョンは 3.11.2 でやや古いですがそのまま利用します。

「>>>」 がプログラムの各行の入力待ちのプロンプトですね。

標準入力待ちですので、Ctrl-D で終了できます。その他に、

>>> quit()
$

のように終了できます。python はインタープリタですから1行づつ入力しても動作するはずです。
早速 Python を学んでみましょう。

「SimplePrograms」の 1番目のプログラムは、

print ('Hello, world!')

です。早速実行してみます。

>>> print ('Hello, world!')
Hello, world!
>>>

いわゆる Hello, world! を表示するプログラムですが、このプログラムは極めて重要です。
プログラミング言語を習得する場合、その結果がどのようになるかを表示する必要があり、それはすなわち Hello, world! の変形で実現するからです。早速見てみますと、Python のいくつかの特徴がわかります。

1) print という関数を使って表示(標準出力への出力)を行う。
  関数は高校数学で y=f(x) のように表記するので、関数名とカッコで表し、カッコ内に関数に渡す引数を指定するのが一般的です。というわけで、この表記から python3 では print の命令文みたいなものは無く、関数で指示を行うと考えられます。つまりカッコの中は、引数であると考えてよさそうです。
 そこで、print という関数を表すために、print() と書くことにします。

2) print() に引数を指定するとそのまま表示される。
  引数が複数の場合はどうなるかは今後分かるでしょう。

3) 文字列は 'Hello, world!' のように「 ' 」文字で囲む。
  現在のプログラミング言語で、数値、文字列という概念は必須だと思うのでここで文字列の表記方法が分かりました。

4) 1行の終わりに何も無い。
  となると、長い行はどうなるのか興味深いです。

 ここで試しに、print()の前に空白文字をいれるとエラーになります。

>>> print( 'Hello, world!' )
File "<stdin>", line 1
print( 'Hello, world!' )
IndentationError: unexpected indent
>>>

 つまり、行の先頭の空白(インデント)に意味があるようです。

 おまけで、print と 「(」 文字の間の空白は無視されるようです。
>>> print ('Hello, world!')
Hello, world!
>>>
 分かりにくいので通常はそうしませんが。

 まだ、1番目なので簡単ですが、早速色々な事が分かったつもりになりました。今後、途切れないようにしたいと思います。
コメント
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする