試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

バージョン2.6/3.0系以上のPythonにおけるprint()関数に関するメモ

(2015/1/4)以前は前半と後半に分けて公開していたが、このページに統合した。

バージョン2系までのPythonでは端末の標準出力や標準エラー出力に改行付きでデータを表示する(書き出す)のにprint文を用いて

(標準出力への書き出し)
print [文字列]

(標準エラー出力への書き出し)
import sys
print >> sys.stderr, [文字列]

のようにしていたが、バージョン3系ではこのprint文という文法は廃止されてprint()という関数を用いるようになっている。

  1. 書式
    1. 簡単な出力
    2. 複数の文字列や値を連結
    3. 最後の改行を変更する
  2. バージョン2系の2.6以上でprint()関数を用いる
  3. 例1
  4. 例2

書式

簡単な出力
旧バージョンのprint文を置き換える使い方としては下のようになる。

(標準出力への書き出し)
print([文字列])

(標準エラー出力への書き出し)
import sys
print([文字列], file=sys.stderr)

キーワード引数fileは標準エラー出力のときにsys.stderrを指定する(別途sysモジュールのimport処理が必要)が、ここには文字列の引数を1つ取るwrite()というメンバ関数を持つオブジェクトであれば他のものを入れることもできる。

複数の文字列や値を連結
print()関数は複数の引数(型は混合可)を受け取ってそれを特定の文字で区切って連結して出力することができる。その際の区切り文字列はキーワード引数sepで指定できる。

>>> print (1, '2', 3.0, sep=' and ')                                            
1 and 2 and 3.0

最後の改行を変更する
出力の最後に改行を付けたくなければキーワード引数end(既定値は「\n」)を空白に書き換える。他の文字列にしたければそのようにendの値を指定する。

>>> print ('ABCD', end=''); print ('EFG')
ABCDEFG

この例では「ABCD」の後ろに改行が付かずに次の「EFG」が続いている。

バージョン2系の2.6以上でprint()関数を用いる

バージョン2系のPythonでも、バージョン2.6系からは以下の1行を実行することでprint()関数を用いることができるようになっており、バージョン3系への緩やかな移行のために役立つ。

from __future__ import print_function

以下の例も、この記述を入れることでバージョン2.6系でも動作する。

例1

下のスクリプトを端末で実行すると標準入力を1行ずつ標準出力と標準エラー出力に同時に書き出す。
[任意]ファイル名: tee.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

# print()関数のキーワード引数fileを用いたテスト
# 標準出力と標準エラー出力に標準入力と同一の文字列を出力する

# バージョン2系では2.6系以上が必要
from __future__ import print_function

import sys


class Tee:
  """
  2つの出力先に同一の文字列を出力するためのクラス
  """
  def __init__ (self, out1=sys.stdout, out2=sys.stderr):
    # 2つの出力先(ファイルオブジェクト)を受け取って指定できるようにしている
    # 既定値はsys.stdout(標準出力)とsys.stderr(標準エラー出力)
    # ファイルに書き出したい場合はopen()で書き込みモードで開いたオブジェクトを指定する
    self._out1 = out1
    self._out2 = out2
  def write (self, text):
    """
    print()関数のキーワード引数fileの動作に合わせたもの
    (文字列の引数を1つ取るメンバ関数write())
    """
    # 1つ目の出力先
    print (text, end='', file=self._out1)
    # 2つ目の出力先
    print (text, end='', file=self._out2)


# メイン処理
# Teeクラスのオブジェクト(インスタンス)を作成
# 今回の例では出力先は(Teeクラスの__init__()における)既定値の標準出力と標準エラー出力なので
# 引数は省略
t = Tee ()
# 1行ずつ標準入力を読み込んで最後まで到達したらループを抜ける
for l in sys.stdin:
  # 行の内容をオブジェクトtのwrite()に渡す
  # キーワード引数fileにTeeクラスのオブジェクトを指定している
  print (l, end='', file=t)

(2015/1/4)読み込みのループにfor文を使用
例えば

$ dmesg | [tee.pyの場所] >| stdout.txt

とするとdmesgの出力をstdout.txtに書き出しつつ標準エラー出力から表示し、

$ dmesg | [tee.pyの場所] >| stdout.txt 2>| stderr.txt

とすると同じ内容をstdout.txtstderr.txtに書き出す。上の実行例では「>|」指定しているので、ファイルが存在する場合は強制的に上書きする。

例2

コマンド行引数に入出力ファイルが指定できるようにしたバージョン。teeコマンドと似たような動作をするが、2つの出力先の内の1つが標準出力とは決まっていない点が異なる。
[任意]ファイル名: tee2.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

# print()関数のキーワード引数fileを用いたテスト2
# ファイルの内容を2つのファイルに出力する

# バージョン2系では2.6系以上が必要
from __future__ import print_function

import locale
import sys


class Tee:
  """
  2つの出力先に同一の文字列を出力するためのクラス
  """
  def __init__ (self, out1=sys.stdout, out2=sys.stderr):
    # 2つの出力先(ファイルオブジェクト)を受け取って指定できるようにしている
    # 既定値はsys.stdout(標準出力)とsys.stderr(標準エラー出力)
    # ファイルに書き出したい場合はopen()で書き込みモードで開いたオブジェクトを指定する
    self._out1 = out1
    self._out2 = out2
  def write (self, text):
    """
    print()関数のキーワード引数fileの動作に合わせたもの
    (文字列の引数を1つ取るメンバ関数write())
    """
    # 1つ目の出力先
    print (text, end='', file=self._out1)
    # 2つ目の出力先
    print (text, end='', file=self._out2)
  @staticmethod
  def do_tee (f_in, outfile1, outfile2):
    """
    入力ファイルの各行を読み込んで2つの出力先へ書き出す
    """
    with open (outfile1, 'w') as f_out1:
      with open (outfile2, 'w') as f_out2:
        # Teeクラスのオブジェクト(インスタンス)を作成
        t = Tee (out1=f_out1, out2=f_out2)
        # 1行ずつ標準入力を読み込んで最後まで到達したらループを抜ける
        for l in f_in:
          # 行の内容をオブジェクトtのwrite()に渡す
          # キーワード引数fileにTeeクラスのオブジェクトを指定している
          print (l, end='', file=t)


# メイン処理
locale.setlocale (locale.LC_ALL, '')  # 国際化されたエラーメッセージを表示
if len (sys.argv) == 3:
  # 引数が2つの場合は
  # 標準入力を入力ファイル,2つの引数を出力ファイルとみなす
  try:
    Tee.do_tee (sys.stdin, sys.argv[1], sys.argv[2])
  except IOError as e:
    sys.exit ('Error: write failed [{0}]: {1}'.format (e.errno, e.strerror))
elif len (sys.argv) == 4:
  # 引数が3つの場合は
  # 1番目の引数を入力ファイル,2番目と3番目の引数を出力ファイルとみなす
  try:
    f_in = open (sys.argv[1], 'r')
  except IOError as e:
    sys.exit ('Error: read failed [{0}]: {1}'.format (e.errno, e.strerror))
  try:
    Tee.do_tee (f_in, sys.argv[2], sys.argv[3])
  except IOError as e:
    sys.exit ('Error: write failed [{0}]: {1}'.format (e.errno, e.strerror))
else:
  print ('USAGE: {0} ([infile]) [outfile1] [outfile2]'.format (sys.argv[0]))

(2015/1/4)読み込みのループにfor文を使用し、エラー発生時の処理も調整
下は実行例。引数の数によって例1のスクリプトと同様に標準入力を用いることもできるようにした。

(入力ファイルを指定する)
$ [tee2.pyの場所] [入力ファイル] [出力ファイル1] [出力ファイル2]

(標準入力を用いる)
$ [tee2.pyの場所] < [入力ファイル] [出力ファイル1] [出力ファイル2]

(パイプを用いる)
$ [コマンド...] | [tee2.pyの場所] [出力ファイル1] [出力ファイル2]

(2015/1/4)標準入力にファイルを用いる形を追加

使用したバージョン: