付録
9. その他諸々¶
9.1. zip, enumerate, range and other iterator/generaotor¶
pythonのfor文は 反復可能なオブジェクト(iterable oject)に対して続くスイートの中身を実行します。
iterableオブジェクトとしてはリスト(list)やタプル(tuple)がよく知られているが、その他にもイテラブルな
オブジェクトが存在します。
python3では、zip, range, enumerate, map
はクラスとなりその呼び出しはイテラブルな
オブジェクトを返すようになりました。python2ではzip, enumerate, map()
はリストを返していましたので、
結果のリストが必要な場所では、これらの呼び出しをlist()
でラップしておく必要があります。
python3のitertools
には、for文でよく使われるパターンを簡略するためにさまざまなイテラブルなオブジェクト
を生成するためのメソッドが提供されています。
9.1.1. iterator.count()
の使い方¶
例えば、ある条件を満たすまで、繰り返しを行いつつ、実行回数の把握が必要な場合には、
i=0
while True:
do_something()
if check_condition(i):
break
i +=1
のようなパターンがよく使われていました。このパターンをitertoolsのcount()
を使って書き換えると
import itertools
for i in itertools.count():
do_something()
if check_condition(i):
break
と見易い形に書き換えることができます。
9.1.2. map()
とzip()
¶
python2ではmap(None, list1,lisn2,..)
をzip(list1,list2,..)
の代わりに使うことができました。
python3では, map()
のこの形での利用は不可能になっています. Python2でもzip()
を使うように書き換えておくべきでしょう。
9.2. functools
module and cmp_to_key()
method¶
リストのソートメソッドの使い方はpython2からpython3で大きく変わったことのひとつです。
python2では listのsort()
のプロトタイプは、
となっています。通常は文字、数字などの自然な並びに従って整列させる場合には、とくに引数は必要ありませんが、 特別なデータ型や、標準的でない順番に整列させrたい場合には、cmp引数に、ふたつの要素を比較するための関数を指定します。 この関数はふたつの引数の大小に従って、-1,0,1のいずれかを返すことを期待されています。 (python2.4以降ではpython3と同じく、key関数を使うこともできますが、それはまた別のお話。)
例えば、
1s=list("python")
2s.sort(lambda x,y: 0 if ord(x) == ord(y) else 1 if ord(x) > ord(y) else -1)
3print(s)
をpython2で実行すると
s=list("python")
s.sort(lambda x,y: 0 if ord(x) == ord(y) else 1 if ord(x) > ord(y) else -1)
print(s)
['h', 'n', 'o', 'p', 't', 'y']
が出力されます。
一方、python3ではsort()
のインタフェースは
となっています。ソート順を制御するためのcmpに代えて、keyと呼ばれる関数を用意することになっています。
このkey()
関数は、ソート対象の各要素毎に評価され、その結果に基づいてソートが実行されます。
click to download python script
click to download jupyter notebook
このcmpとkeyの違いを吸収するための関数cmp_to_key()
が
モジュールfunctools
の中に用意されています。これを使って、先ほどのpython2でのソートを
実行するには、次の様に書き換えます。
1import functools
2s=list("python")
3s.sort(key=functools.cmp_to_key(lambda x,y: 0 if ord(x) == ord(y) else 1 if ord(x) > ord(y) else -1))
4print(s)
['h', 'n', 'o', 'p', 't', 'y']
この様に、python2で使っていたcmp()
関数を、pytyon3で動作するようにすることができます。
click to download python script
click to download jupyter notebook
ちなみに、python3での sort()
を key パラメータを使って、同様のソートを行うには
1s=list("python")
2s.sort(key=lambda x:ord(x))
3print(s)
['h', 'n', 'o', 'p', 't', 'y']
とすれば十分です。 この様に、 cmp()
から key()
への切り替えは、
それほど困難なことではありません。 cmp_to_key()
はあくまで、python2で使っていた cmp()
関数を
どうしてもそのまま使いたい場合(例えば大量のソースコードをpython2からpython3に機械的に変換したい時など)に限定しておくのが良さそうです。
9.3. reload 問題¶
Python2とPython3では、reloadの取り扱いが変わっています。
Python2ではreload関数は組み込み関数で、globalなネームスペースに存在していますが、
Python3においては、reload関数は、importlibモジュールの中で定義されています。
アプリケーションの中で、モジュールのリロードを行うことはほとんど考えられないので、
問題になることは無いかと思いますが、プログラム開発中にはしばしば 修正済みのモジュールを
reload
することになるでしょう。
python3では、 reload関数の利用に先立ち、
>>> from importlib import reload
reload
<function reload at 0x10275e158>
としてreload関数をimportします。この後、
reload(mymodule)
としてやることで、python2と同じく、モジュールをreloadしてやることができます。
9.4. print 問題¶
print はpython2では 文, python3では 関数 となっています。 2to3はこの違いを適切に取り扱って、プログラムを更新してくれます。 ただし、この場合変更後のコードはpython2では動作しなくなります。
このため、同じコードベースをpython2/python3のどちらでも動作させるには、 ちょっと工夫が必要です。
9.4.1. print()関数¶
まずは、python2では__future__モジュールを使ってpython2でも python3のprint関数が使えるようにすることができます。
from __future__ import print_function
__future__ モジュールのインポートはファイル中の最初の実行文である 必要があるので、注意してください。
9.4.2. sys.stdout/stderr¶
もともと、print文は、標準出力に指定された文字列を出力します。
標準出力および標準エラー出力はsys
モジュールの
sys.stdout
および stderr
としてアクセス
可能ですから、print文を使う代わりに、sys.stdout
あるいは
sys.sterr
に文字列を送り出すことで、print文を使用しないようにも
できます。
import sys
sys.stdout.write("Hello World")
9.4.3. logging モジュール¶
そもそも、デバグメッセージを表示するためにprint文を使っているなら、
logging
などのモジュールを使いメッセージを表示させる
のが良いでしょう。logging
を使えば、開発のフェーズ毎に
"Debug", "Info","Warnig"などのメッセージレベルを使って出力される
メッセージを制御することができます。 使い方は、次の:py:mod:logging モジュールの使用例を
ご覧ください。
import logging
logging.getLogger().setLevel(logging.WARNING)
...
logging.warning("これは警告です。")
- 出力例:
WARNING:root:これは警告です。
logging
モジュールには、 {'DEBUG':10, 'INFO':20, 'WARNING':30, 'ERROR':40, 'CRITICAL':50, 'FATAL':50}
のレベルが定義されています。また、このレベルに対応した出力関数 debuf, info, warning, error, critical, fatal()
が提供されています。
実行時の設定レベル以上のメッセージが出力されます。 メッセージの出力先は、端末だけでなく、ファイル、syslogなどに変更/追加することも可能です。
9.5. exceptions モジュール¶
python3では exceptions
モジュールは廃止されています。
python2で exceptions
モジュール経由で呼び出すException のクラスは全て __builtins__
の中に入っていますので、
moduleを import
する必要は有りません。
exceptions.xxxException
の様な使い方をしていた場合には、直接 xxxException
を使う様にすれば良いかと思われます。
9.6. maxint¶
python3では取り扱える整数の最大値が 存在しません 。
従って、 sys.maxint
も廃止されています。
取り扱える上限を決めているものが何かによって、対応が変わるかと思いますが、
sys.maxint
ではない別のものにしておくべきでしょう [1] 。
9.7. time.mktimeの2000年問題(2023/12/15追記)¶
>>> time.mktime((2023,12,15,0,0,0,0,0,0))
1702566000.0
>>> time.mktime((23,12,15,0,0,0,0,0,0))
time.mktime((23,12,15,0,0,0,0,0,0))
1702566000.0
となりますが、python3.6では、
>>> time.mktime((2023,12,15,0,0,0,0,0,0))
1702566000.0
>>> time.mktime((23,12,15,0,0,0,0,0,0))
-61411339139.0
python3.9 では、
>>> time.mktime((2023,12,15,0,0,0,0,0,0))
1702566000.0
>>> time.mktime((23,12,15,0,0,0,0,0,0))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: mktime argument out of range
となっています。ともあれ、 time.mktime()
で二桁の年号をお使いの場合は、気をつけましょう。 :py:`python2 の :py:func:`mktime`では, year
が 69 より小さい場合は year+2000
が、69以上で100より小さい場合には、:py:year:`year+1900`がUnix time換算時に年号として使われます。
python3.9では, mktime()
のyearの引数が1900より小さい場合には、 OverFlowError
を返すようです。
>>> time.mktime ((1900, 1, 1,0,0,0,0,0,0 ))
-2209021200.0
なので、単に「Unix時間が負になる」ことは問題では無いようにも見えます。