.. only:: latex .. raw:: latex \appendix .. only:: not latex .. rubric:: 付録 .. _misc: ============== その他諸々 ============== zip, enumerate, range and other iterator/generaotor +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pythonのfor文は 反復可能なオブジェクト(iterable oject)に対して続くスイートの中身を実行します。 iterableオブジェクトとしてはリスト(list)やタプル(tuple)がよく知られているが、その他にもイテラブルな オブジェクトが存在します。 python3では、\ :py:class:`zip, range, enumerate, map`\ はクラスとなりその呼び出しはイテラブルな オブジェクトを返すようになりました。python2では\ :py:func:`zip, enumerate, map`\ はリストを返していましたので、 結果のリストが必要な場所では、これらの呼び出しを\ :py:func:`list`\ でラップしておく必要があります。 python3の\ :py:mod:`itertools`\ には、for文でよく使われるパターンを簡略するためにさまざまなイテラブルなオブジェクト を生成するためのメソッドが提供されています。 :py:func:`iterator.count` の使い方 ----------------------------------- 例えば、ある条件を満たすまで、繰り返しを行いつつ、実行回数の把握が必要な場合には、 .. code-block:: python i=0 while True: do_something() if check_condition(i): break i +=1 のようなパターンがよく使われていました。このパターンをitertoolsの\ :py:func:`count`\ を使って書き換えると .. code-block:: python import itertools for i in itertools.count(): do_something() if check_condition(i): break と見易い形に書き換えることができます。 :py:func:`map`\ と\ :py:func:`zip`\ ----------------------------------------------------------------- python2では\ :py:mod:`map(None, list1,lisn2,..)`\ を\ :py:mod:`zip(list1,list2,..)`\ の代わりに使うことができました。 python3では, \ :py:func:`map`\ のこの形での利用は不可能になっています. Python2でも\ :py:func:`zip`\ を使うように書き換えておくべきでしょう。 :py:mod:`functools` module and :py:meth:`cmp_to_key` method ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ リストのソートメソッドの使い方はpython2からpython3で大きく変わったことのひとつです。 python2では listの\ :py:meth:`.sort`\ のプロトタイプは、 | sort(...) | L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*; | cmp(x, y) -> -1, 0, 1 となっています。通常は文字、数字などの自然な並びに従って整列させる場合には、とくに引数は必要ありませんが、 特別なデータ型や、標準的でない順番に整列させrたい場合には、cmp引数に、ふたつの要素を比較するための関数を指定します。 この関数はふたつの引数の大小に従って、-1,0,1のいずれかを返すことを期待されています。 (python2.4以降ではpython3と同じく、key関数を使うこともできますが、それはまた別のお話。) 例えば、 .. jupyter-kernel:: python2 :id: python2_kernel .. jupyter-execute:: :linenos: :emphasize-lines: 2 :hide-output: 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) をpython2で実行すると .. jupyter-execute:: :hide-code: 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) が出力されます。 一方、python3では\ :py:meth:`sort`\ のインタフェースは | sort(\*, key=None, reverse=False) method of builtins.list instance | Sort the list in ascending order and return None. | | The sort is in-place (i.e. the list itself is modified) and stable (i.e. the | order of two equal elements is maintained). | | If a key function is given, apply it once to each list item and sort them, | ascending or descending, according to their function values. | | The reverse flag can be set to sort in descending order. となっています。ソート順を制御するためのcmpに代えて、keyと呼ばれる関数を用意することになっています。 この\ :py:func:`key`\ 関数は、ソート対象の各要素毎に評価され、その結果に基づいてソートが実行されます。 \ :jupyter-download:script:`click to download python script `\ \ :jupyter-download:notebook:`click to download jupyter notebook `\ このcmpとkeyの違いを吸収するための関数\ :py:func:`cmp_to_key`\ が モジュール\ :py:mod:`functools`\ の中に用意されています。これを使って、先ほどのpython2でのソートを 実行するには、次の様に書き換えます。 .. jupyter-kernel:: python3 :id: python3_kernel .. jupyter-execute:: :linenos: :emphasize-lines: 3 import functools s=list("python") s.sort(key=functools.cmp_to_key(lambda x,y: 0 if ord(x) == ord(y) else 1 if ord(x) > ord(y) else -1)) print(s) この様に、python2で使っていた\ :py:func:`cmp`\ 関数を、pytyon3で動作するようにすることができます。 \ :jupyter-download:script:`click to download python script `\ \ :jupyter-download:notebook:`click to download jupyter notebook `\ ちなみに、python3での :py:func:`sort` を key パラメータを使って、同様のソートを行うには .. jupyter-execute:: :linenos: :emphasize-lines: 3 s=list("python") s.sort(key=lambda x:ord(x)) print(s) とすれば十分です。 この様に、 :py:func:`cmp` から :py:func:`key` への切り替えは、 それほど困難なことではありません。 :py:func:`cmp_to_key` はあくまで、python2で使っていた :py:func:`cmp` 関数を どうしてもそのまま使いたい場合(例えば大量のソースコードをpython2からpython3に機械的に変換したい時など)に限定しておくのが良さそうです。 reload 問題 ++++++++++++++++ Python2とPython3では、reloadの取り扱いが変わっています。 Python2ではreload関数は組み込み関数で、globalなネームスペースに存在していますが、 Python3においては、reload関数は、importlibモジュールの中で定義されています。 アプリケーションの中で、モジュールのリロードを行うことはほとんど考えられないので、 問題になることは無いかと思いますが、プログラム開発中にはしばしば 修正済みのモジュールを \ ``reload``\ することになるでしょう。 python3では、 reload関数の利用に先立ち、 .. code-block:: python3 :caption: Python3でのreload関数の利用 >>> from importlib import reload reload としてreload関数をimportします。この後、 .. code-block:: python3 :caption: mymoduleをリロードする。 reload(mymodule) としてやることで、python2と同じく、モジュールをreloadしてやることができます。 print 問題 +++++++++++++++ print はpython2では **文**, python3では **関数** となっています。 2to3はこの違いを適切に取り扱って、プログラムを更新してくれます。 ただし、この場合変更後のコードはpython2では動作しなくなります。 このため、同じコードベースをpython2/python3のどちらでも動作させるには、 ちょっと工夫が必要です。 print()関数 --------------- まずは、python2では__future__モジュールを使ってpython2でも python3のprint関数が使えるようにすることができます。 .. code-block:: python from __future__ import print_function __future__ モジュールのインポートはファイル中の最初の実行文である 必要があるので、注意してください。 sys.stdout/stderr ------------------------------ もともと、print文は、標準出力に指定された文字列を出力します。 標準出力および標準エラー出力は\ :py:mod:`sys`\ モジュールの \ :py:data:`sys.stdout`\ および \ :py:data:`stderr`\ としてアクセス 可能ですから、print文を使う代わりに、\ :py:data:`sys.stdout`\ あるいは \ :py:data:`sys.sterr`\ に文字列を送り出すことで、print文を使用しないようにも できます。 .. code-block:: python import sys sys.stdout.write("Hello World") logging モジュール ------------------------------ そもそも、デバグメッセージを表示するためにprint文を使っているなら、 \ :py:mod:`logging`\ などのモジュールを使いメッセージを表示させる のが良いでしょう。\ :py:mod:`logging`\ を使えば、開発のフェーズ毎に "Debug", "Info","Warnig"などのメッセージレベルを使って出力される メッセージを制御することができます。 使い方は、次の:py:mod:`logging` モジュールの使用例を ご覧ください。 .. code-block:: python import logging logging.getLogger().setLevel(logging.WARNING) ... logging.warning("これは警告です。") 出力例: WARNING:root:これは警告です。 :py:mod:`logging` モジュールには、 {'DEBUG':10, 'INFO':20, 'WARNING':30, 'ERROR':40, 'CRITICAL':50, 'FATAL':50} のレベルが定義されています。また、このレベルに対応した出力関数 :py:func:`debuf, info, warning, error, critical, fatal` が提供されています。 実行時の設定レベル以上のメッセージが出力されます。 メッセージの出力先は、端末だけでなく、ファイル、syslogなどに変更/追加することも可能です。 exceptions モジュール ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ python3では ``exceptions`` モジュールは廃止されています。 python2で ``exceptions`` モジュール経由で呼び出すException のクラスは全て ``__builtins__`` の中に入っていますので、 moduleを ``import`` する必要は有りません。 ``exceptions.xxxException`` の様な使い方をしていた場合には、直接 ``xxxException`` を使う様にすれば良いかと思われます。 maxint ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ python3では取り扱える整数の最大値が **存在しません** 。 従って、 ``sys.maxint`` も廃止されています。 取り扱える上限を決めているものが何かによって、対応が変わるかと思いますが、 ``sys.maxint`` ではない別のものにしておくべきでしょう [#]_ 。 .. [#] (``maxint``, exceptionsの問題はplexをpython3に対応させるために必要でした。) time.mktimeの2000年問題(2023/12/15追記) ++++++++++++++++++++++++++++++++++++++++++ .. code-block:: python2 >>> 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では、 .. code-block:: python3 >>> 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 では、 .. code-block:: python3 >>> 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 "", line 1, in OverflowError: mktime argument out of range となっています。ともあれ、 :py:func:`time.mktime` で二桁の年号をお使いの場合は、気をつけましょう。 :py:`python2 の :py:func:`mktime`\では, :py:data:`year`\ が 69 より小さい場合は :py:data:`year+2000` が、69以上で100より小さい場合には、\ :py:year:`year+1900`\ がUnix time換算時に年号として使われます。 python3.9では, :py:func:`mktime` のyearの引数が1900より小さい場合には、 :py:exc:`OverFlowError` を返すようです。 .. code-block:: python3 >>> time.mktime ((1900, 1, 1,0,0,0,0,0,0 )) -2209021200.0 なので、単に「Unix時間が負になる」ことは問題では無いようにも見えます。