Skip to main content

75 posts tagged with "Python"

View All Tags

Python: ファイル読み込み時の例外の扱い例 - try、except、else、finallyブロック

· 4 min read
Yu Sasaki
Enterprise Security Manager / Advisor

ファイルのパスや名前のミス、パーミッションの権限が無い等が原因でファイルを読み込めない場合がある。そのような場合、すなわち例外が発生した際にそこで処理を中断して、発生した例外に合わせた処理ブロックにジャンプする構文が、try:~except Error:~構文。 今回は、コマンドライン引数で英文テキストファイル名を指定し、スクリプト内でファイル内容を読み込み、単語数をカウントし出力するスクリプトを以って、オプションのelse、finallyブロックを含めた例外ブロックの扱いを確認する。 ただし、ここでの「単語」とは、簡単に考える為、1つの空白文字で区切られた文字列とする。

ソースコード

# -*- coding: UTF-8 -*-
import sys
script_name = sys.argv[0]
try:
arg = sys.argv[1]
f = open(arg, 'r')
except IndexError:
print 'Usage: %s TEXTFILE' % script_name
except IOError:
print '"%s" cannot be opened.' % arg
else:
print arg, 'contains', len(f.read().split(' ')), 'words.'
f.close()
finally:
print 'n"%s" process end.' % script_name
quit()
print 'Not reach this line.'

コードの説明

tryブロック

まず、例外を発生させる恐れのある行は、tryブロック内に書く。 ここで発生する可能性がある例外はコマンドライン引数が格納されているリストへのアクセス部分であるsys.argv[1]。[]演算子で存在しない要素を参照する例外。また、ファイルを開くopen(arg, 'r')も冒頭の理由で例外発生の可能性もある。

exceptブロック

exceptブロックは、tryブロックで例外が発生した場合にのみ実行されるブロックです。その為、例外が発生しない場合は実行されない。 発生する可能性のある例外のタイプ毎にexceptブロックを書けば、そのタイプ毎の例外への対処処理を書くことが出来る。

except TYPE_AError:
TYPE_AErrorが発生した際のとある処理...
except TYPE_BError:
TYPE_BErrorが発生した際のとある処理...

elseブロック (オプション)

elseブロックは全てのexceptブロックの後に書く(任意なので書かなくても構わない)。 elseブロックはtryブロックで例外が発生しなかった場合にのみ実行されるブロック。今回は、ファイルの読み込みとクローズに使用している。

finallyブロック (オプション)

finallyブロックはtryブロックで例外が発生するしないに関わらず実行されるブロック(任意なので書かなくても構わない)。

実行結果

読み込むテキストファイルを以下の_aLine.txt_とする。 aLine.txt

Unless I set the standard where I am in any level, I'll be puzzled about what I should do from now on.

コマンドラインで適切な引数を与えた場合

$ python excp01.py aLine.txt
aLine.txt contains 22 words.
"excp01.py" process end.

存在しないファイル名を引数を与えた場合

$ python excp01.py texfile.tex
"texfile.tex" cannot be opened.
"excp01.py" process end.

コマンドライン引数を与えなかった場合

$ python excp01.py
Usage: excp01.py TEXTFILE
"excp01.py" process end.

例外オブジェクト名をpython処理系に聞いてみる

どんなメソッドや関数、演算子が、どのような例外を投げるのか予測が付かない場合もある。そんな場合、一旦はスクリプトを実行してエラーを確認する。 SyntaxError: invalid syntax以外のエラーがある場合、例えば、

$ python excp01.py
Traceback (most recent call last):
File "excp01.py", line 5, in
arg = sys.argv[1]
IndexError: list index out of range

のような場合は、最終行の行頭のIndexErrorが例外名となる。

何でも例外任せにしていいの?

計算量が増える為、良くない。今プログラムのコマンドライン引数の確認を、 if len(sys.argv) != 2: ホニャララ とすると整数の比較ですむが、例外のキャッチに任せると、例外インスタンスを生成し送出という計算量の大きい処理が掛かる。ネットワーク接続やファイル読み書きなどのIOでは例外はイベント駆動で発生する為、例外制御が必須ですが、それ以外では使用しない。

ドキュメント

Python: テキストファイルの行頭に行番号を追加

· One min read
Yu Sasaki
Enterprise Security Manager / Advisor

コマンドラインで指定されたテキストファイルの行頭に行番号を追加し、そのデータを新たなファイルに書き出すスクリプトです。

ソースコード

#!/usr/bin/python
# coding: UTF-8
import sys
argvs = sys.argv
argc = len(argvs)
if (argc != 3):
print 'Usage: $ python %s target_file making_file' % argvs[0]
quit()
f = open(argvs[1])
lines2 = f.readlines()
f.close()
nf = open(argvs[2], 'w')
i = 1
for line in lines2:
nf.write('%3d: %s' % (i, line))
i = i + 1
nf.close()

仮にこのソースコードをfile05a.pyというファイルで保存した場合、使用する際はプロンプトに以下のように打ち込みます。

実行結果

プロンプト

> python file05a.py file05a.py file05a-l.txt

この結果、生成されたファイルが下記になります。

file05a-l.txt

1: #!/usr/bin/python
2: # coding: UTF-8
3:
4: import sys
5:
6: argvs = sys.argv
7: argc = len(argvs)
8: if (argc != 3):
9: print 'Usage: $ python %s target_file making_file' % argvs[0]
10: quit()
11: f = open(argvs[1])
12: lines2 = f.readlines()
13: f.close()
14: nf = open(argvs[2], 'w')
15: i = 1
16: for line in lines2:
17: nf.write('%3d: %s' % (i, line))
18: i = i + 1
19: nf.close()

Python: 二値の交換

· One min read
Yu Sasaki
Enterprise Security Manager / Advisor

ソート系のアルゴリズムを実装する際などに、_配列の任意の2つの要素を交換する_処理が必要な場合がありますので、以下にその手順の一例を示します。

ソースコード

# coding: UTF-8
a = [10, 20]
print '%d %d' % (a[0], a[1])
a[0], a[1] = a[1], a[0]
print '%d %d' % (a[0], a[1])

実行結果

10 20
20 10

何だかperlの多重代入を思い出します。

Python: 指定したパスのディレクトリ中のファイル一覧を出力

· 2 min read
Yu Sasaki
Enterprise Security Manager / Advisor

あるディレクトリから特定のファイルを検索したい場合、探索対象ディレクトリ内のファイルを全て取得する必要があります。今回は、引数にディレクトリを指すパスを指定することによって、そのディレクトリの内容を取得する関数を2つ示します。

ソースコード

# coding: Shift_JIS
import os # osモジュールのインポート
# os.listdir('パス')
# 指定したパス内の全てのファイルとディレクトリを要素とするリストを返す
files = os.listdir('C:\Python25\')
for file in files:
print file

実行結果の一例

DLLs
Doc
include
Lib
libs
LICENSE.txt
lxml-wininst.log
NEWS.txt
PIL-wininst.log
pysqlite-wininst.log
pysqlite2-doc
python.exe
pythonw.exe
README.txt
Removelxml.exe
RemovePIL.exe
Removepysqlite.exe
Scripts
tcl
Tools
w9xpopen.exe

ワイルドカードでリスティング対象を指定

globモジュールのglob()関数の引数内にワイルドカード「*」を含めることが出来ます。これによって、より手軽に目的のファイルを探索することが出来ます。

ソースコード

# coding: Shift_JIS
import glob
# パス内の全ての"指定パス+ファイル名"と"指定パス+ディレクトリ名"を要素とするリストを返す
files = glob.glob('C:\Python25\*.*') # ワイルドカードが使用可能
for file in files:
print file

os.listdir()と異なり、glob.glob()は取得したファイル文字列には先頭に探査ディレクトリ(引数)のパスが付いています。

実行結果の一例

C:Python25LICENSE.txt C:Python25lxml-wininst.log C:Python25NEWS.txt C:Python25PIL-wininst.log C:Python25pysqlite-wininst.log C:Python25python.exe C:Python25pythonw.exe C:Python25README.txt C:Python25Removelxml.exe C:Python25RemovePIL.exe C:Python25Removepysqlite.exe C:Python25w9xpopen.exe

リファレンス

Python: 正規表現の基本 - 最長、最短マッチング

· 3 min read
Yu Sasaki
Enterprise Security Manager / Advisor

直前の文字、メタ文字を繰り返しマッチングさせる量指定記号である「*」「+」「?」などは、テキスト中にその繰り返しパターンがマッチする箇所が複数ある場合は、通常最後にマッチした箇所をオブジェクトに記録します。このような最長マッチングに対して、マッチング箇所が複数の場合に最初にマッチした箇所を記録する最短マッチング方法があります。 最短マッチングを行う量指定記号は最長マッチングの記号の末尾に「?」を付けるだけです。すなわち、最長の量指定が「*」「+」「?」であるのに対して、**最短マッチングは「*?」「+?」「??」**となります。それでは、以下のコードで確認してみましょう。

ソースコード

# coding: Shift_JIS import re # 正規表現を扱うモジュールのインポート # 正規表現のチェックプリント用の関数 def PrintRegMatch(pat, txt): # 書式: re.search(パターン, テキスト) m = re.search(pat, txt) # パターンにマッチしなかった場合はNoneを返す if m != None: print 'パターン: "%s"nテキスト: "%s"nマッチ : する' % (pat, txt) print 'マッチング開始位置:', m.start() print 'マッチング終了位置:', m.end() else: print 'パターン: "%s"nテキスト: "%s"nマッチ: しない' % (pat, txt) print return m # サンプルテキスト txt = '

Hello! World!!

Goodbye World

' # 最長: .* # 最短: .*? PrintRegMatch('

.*

', txt) # 最長: 文字列末尾の

にマッチ PrintRegMatch('

.*?

', txt) # 最短: World!!



にマッチ # 最長: .+ # 最短: .+? PrintRegMatch('

.+

', txt) # +?は直前の文字の1文字以上の繰り返し PrintRegMatch('

.+?

', txt) # ↑の控えめマッチング # 最長: .? # 最短: .?? PrintRegMatch('B.?C', 'BCCC') # ?は直前の文字の0 or 1回の繰り返し PrintRegMatch('B.??C', 'BCCC') # ??はその控えめ(ry

re.searchはre.matchと異なり、パターンをテキスト文字列の先頭以外にもマッチさせます。

実行結果

パターン: "

.*

" テキスト: "

Hello! World!!

Goodbye World

" マッチ : する マッチング開始位置: 0 マッチング終了位置: 41 パターン: "

.*?

" テキスト: "

Hello! World!!

Goodbye World

" マッチ : する マッチング開始位置: 0 マッチング終了位置: 21 パターン: "

.+

" テキスト: "

Hello! World!!

Goodbye World

" マッチ : する マッチング開始位置: 0 マッチング終了位置: 41 パターン: "

.+?

" テキスト: "

Hello! World!!

Goodbye World

" マッチ : する マッチング開始位置: 0 マッチング終了位置: 21 パターン: "B.?C" テキスト: "BCCC" マッチ : する マッチング開始位置: 0 マッチング終了位置: 3 パターン: "B.??C" テキスト: "BCCC" マッチ : する マッチング開始位置: 0 マッチング終了位置: 2

リファレンス

Python: set型の集合演算で2つのリスト要素を比較

· 3 min read
Yu Sasaki
Enterprise Security Manager / Advisor

2つのリストの要素を比較する際、リスト型をset型に変えると「-」「&」などの演算子1つで集合演算できます(AND、OR、NOTとか)。

ソースコード

#!/usr/bin/python
# coding: UTF-8
# リストの比較(by 集合演算)
old_list = ['A', 'B', 'C', 'D', 'E', 'F'] # 古いリスト
new_list = ['A', 'C', 'F', 'G', 'H', 'I'] # 更新された新しいリスト、とする
# 組み込み関数set()を用いて(リストを含む)シーケンス型からset型データを作成
old_set = set(old_list)
new_set = set(new_list)
print 'old_set ==', old_set
print 'new_set ==', new_set
# 差集合: old_setの要素からnew_setに含まれる要素を削除した要素集合
# すなわち、old_setをnew_setに更新した際に、削除された要素集合、とみる
print 'old_set - new_set ==', old_set - new_set
print 'len(old_set - new_set) ==', len(old_set - new_set) # 削除された個数
# old_setをnew_setに更新した際に、追加された要素集合、とみる
print 'new_set - old_set ==', new_set - old_set
print 'len(new_set - old_set) ==', len(new_set - old_set) # 追加された個数
# old_setをnew_setの共通要素集合(更新した際に変更されなかった要素、とみる)
print 'new_set & old_set ==', new_set & old_set
# old_setかnew_setに含まれる要素集合
print 'new_set ^ old_set ==', new_set ^ old_set
# 和集合: old_setとnew_setに含まれる重複の無い要素集合
print 'new_set | old_set ==', new_set | old_set

実行結果

old_set == set(['A', 'C', 'B', 'E', 'D', 'F'])
new_set == set(['A', 'C', 'G', 'F', 'I', 'H'])
old_set - new_set == set(['B', 'E', 'D'])
len(old_set - new_set) == 3
new_set - old_set == set(['I', 'H', 'G'])
len(new_set - old_set) == 3
new_set & old_set == set(['A', 'C', 'F'])
new_set ^ old_set == set(['B', 'E', 'D', 'G', 'I', 'H'])
new_set | old_set == set(['A', 'C', 'B', 'E', 'D', 'G', 'F', 'I', 'H'])

リスト(and シーケンス型)をset型に変更すると要素の順序が失われます。再度リスト型に戻したいときは、list()関数を用います。

>>> set_data = set(['a', 'b', 'c', 'd'])
>>> set_data
set(['a', 'c', 'b', 'd'])
>>> list_data = list(set_data)
>>> list_data
['a', 'c', 'b', 'd']
>>> type(list_data)
<type 'list'>
>>>

話変わりますが、Rubyだと配列のまま加減算ができます(配列オブジェクトが「+」「-」演算子などをサポートしているみたい)。

リファレンス

Python: 文字列の上位型であるシーケンス型の構文 - Sequence[X:Y:Z]

· 3 min read
Yu Sasaki
Enterprise Security Manager / Advisor

データの順序が存在するデータ型としてシーケンス型があり、文字列型の上位型となっています。このシーケンス型には文字列中の文字の抽出や操作を簡略化する分かりやすい構文がありますので、これを確認してみましょう。

String[X:] はString[X]から末尾までの文字を持つ文字列

#!/usr/bin/python
# coding: UTF-8
s = 'abcdefg'
print 's ', s
print 's[0:] ', s[0:]
print 's[1:] ', s[1:]
print 's[-1:] ', s[-1:]
print 's[-5:] ', s[-5:]

実行結果

s abcdefg
s[0:] abcdefg
s[1:] bcdefg
s[-1:] g
s[-5:] cdefg

String[:Y] は文字列の先頭からString[Y-1]までの文字を持つ文字列

s = 'abcdefg'
print 's ', s
print 's[:3] ', s[:3] # s[0] s[1] s[2]まで
print 's[:-1] ', s[:-1] # Yは-1よりs[(-1)-1]→s[-2]までの文字列
print 's[:-5] ', s[:-5]

実行結果

s abcdefg
s[:3] abc
s[:-1] abcdef
s[:-5] ab

String[::Z] は文字列の先頭から末尾まで Z 間隔で文字を抽出した文字列

s = 'abcdefg'
print 's ', s
print 's[::1] ', s[::1] # 1間隔なのでsと同じ
print 's[::2] ', s[::2]
print 's[::-1]', s[::-1] # 「-」を付けると逆順に辿っていきます

実行結果

s abcdefg
s[::1] abcdefg
s[::2] aceg
s[::-1] gfedcba

文字列の文字順序を逆順にする処理を構文で賄えるのは良いですね。汎用性があって。

上述の複合 - String[X:Y:Z]

s = 'abcdefg'
print 's ', s
print 's[1:3] ', s[1:3]
print 's[:-1:2] ', s[:-1:2]
print 's[-1::-2]', s[-1::-2]
print 's[1:4:2] ', s[1:4:2]

実行結果

s abcdefg
s[1:3] bc
s[:-1:2] ace
s[-1::-2] geca
s[1:4:2] bd

チュートリアル

リファレンス

Python: リストの上位型であるシーケンス型の構文 - Sequence[X:Y:Z]

· 3 min read
Yu Sasaki
Enterprise Security Manager / Advisor

データの順序が存在するデータ型としてシーケンス型があり、リストの上位型となっています。このシーケンス型には要素の抽出や操作を簡略化する分かりやすい構文がありますので、これを確認してみましょう。

List[X:] はList[X]から末尾までの要素を持つリスト

#!/usr/bin/python
# coding: UTF-8
a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[0:] ', a[0:]
print 'a[1:] ', a[1:]
print 'a[-1:] ', a[-1:]
print 'a[-5:] ', a[-5:]

実行結果

a [10, 20, 30, 40, 50]
a[0:] [10, 20, 30, 40, 50]
a[1:] [20, 30, 40, 50]
a[-1:] [50]
a[-5:] [10, 20, 30, 40, 50]

List[:Y] はListの先頭からList[Y-1]までの要素を持つリスト

a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[:3] ', a[:3] # a[0] a[1] a[2]まで
print 'a[:-1] ', a[:-1] # Yは-1よりa[(-1)-1]→a[-2]までのリスト
print 'a[:-5] ', a[:-5]

実行結果

a [10, 20, 30, 40, 50]
a[:3] [10, 20, 30]
a[:-1] [10, 20, 30, 40]
a[:-5] []

List[::Z] はListの先頭から末尾まで Z 間隔で要素を抽出したリスト

a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[::1] ', a[::1] # 1間隔なのでaと同じ
print 'a[::2] ', a[::2]
print 'a[::-1]', a[::-1] # 「-」を付けると逆順に辿っていきます

実行結果

a [10, 20, 30, 40, 50]
a[::1] [10, 20, 30, 40, 50]
a[::2] [10, 30, 50]
a[::-1] [50, 40, 30, 20, 10]

リストの要素順序を逆順にする処理を構文で賄えるのは良いですね。

上述の複合 - List[X:Y:Z]

a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[1:3] ', a[1:3]
print 'a[:-1:2] ', a[:-1:2]
print 'a[-1::-2]', a[-1::-2]
print 'a[1:4:2] ', a[1:4:2]

実行結果

a [10, 20, 30, 40, 50]
a[1:3] [20, 30]
a[:-1:2] [10, 30]
a[-1::-2] [50, 30, 10]
a[1:4:2] [20, 40]

チュートリアル

リファレンス

Python: 10進数整数を2進数文字列に変換する関数

· 2 min read
Yu Sasaki
Enterprise Security Manager / Advisor

2進数文字列を10進数整数に変換する関数int()はありますが、

>>> int('1011', 2)
11

その逆の、10進数整数を2進数文字列に変換する関数が(Python2.5では)見当たらなかったので、書いてみました。

ソースコード

#!/usr/bin/python
# coding: UTF-8
import math, string
# 10進数整数を2進数文字列に変換する関数
# decimal : 10進数整数
# press : 上位桁の0を切り詰めるフラグ
def toBinary(decimal, press=True):
if decimal == 0: return '0'
bin_str = ""
i = 31
while i >= 0:
bi = int((decimal & int(math.pow(2, i))) >> i)
bin_str += str(bi)
i -= 1
if press:
try:
bin_str = bin_str[bin_str.index('1'):]
except ValueError:
print 'error'
bin_str = '0'
return bin_str
bin_arr = [toBinary(i) for i in range(21)]
print '10進数t2進数'
for i in range(len(bin_arr)):
print '%2dt%5s' % (i, bin_arr[i])

Pythonでは明示的に型変換する必要がありますので、所々int()、str()を用いて型直ししています。

実行結果

10進数 2進数
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
16 10000
17 10001
18 10010
19 10011
20 10100

追記: 32bit以上の整数を扱う場合

参考: 10進数を2進数と16進数に変換する - gan2 の Ruby 勉強日記 ↑の記事で、データサイズにかかわらず処理できるRubyプログラムがありましたので、参考させていただきました。

def toBinary2(decimal):
if decimal == 0: return '0'
bin_str = ""
while decimal > 0:
bin_str += str(decimal % 2)
decimal >>= 1
return bin_str[::-1]

実行結果は上と同じです。

リファレンス