Pythonのお勉強2回目

みんなのPython』を使っての読書会の2回目。


今回の範囲は P.68 〜 P.108

では早速まとめ。

条件分岐

  • pythonにはswitch文がない
  • else if は elifと書く

elseと文字数が同じなので読み間違えそう。
CやJavaのswitch文の代わりに使うと良い書いてあるが、
複数のcaseが成り立つ場合はどうなるんだろうか。
やってみる。

if False:
   print "if"
elif True:
   print "1個目"
elif True:
   print "2個目"
else:
   print "else"

>>>1個目

成り立った時点でifブロック自体を抜けるようだ。
つまり全てにbreakを書いたswitch文という事。複数成り立つ場合は素直にifを複数書く。
まぁswitchがある場合でも可読性の観点から複数成り立つようなものは書くべきではないが。

  • 条件式

範囲を条件にする場合の書き方が非常にすっきりしている。

変数yearが1970年から1980年なら真という書き方。

year = 1975
if 1970 <= year < 1980:
    print "70年代生まれです"

Javaの場合だと

int year = 1975;
if (1970 <= year && year < 1980) {
    System.out.println("70年代生まれです");
}

&&の分が余分。&&と||を間違えてバグを産んだ事がある人は多いはず。


ちなみに「&&、||」という論理演算子はない。代わりに「and、or」を使う。
なので上記は if 1970 <= year and year < 1980: と書く事もできる。

  • 否定の比較には「!=」もしくは「<>」を使う。

「<>」は別にいらないだろ。。。

  • in 演算子を使ってシーケンス中にその値が含まれるかを判定できる。
if "a" in "abcde":
  print "aが見つかった"

if "abcde".find("a") != -1:
  print "aが見つかった"

は同じ結果。

ループ

  • for文を使う。

forはfor each文しかない。

for i in range(10):
  print i

# 1
# 2
# …
# 9

rangeを使う事で柔軟に繰り返しができる。rangeについては次節の関数に書く。


for文に渡すシーケンスにはリスト、タプル、文字列など様々なものが渡せる。

for i in [1,2,3,4,5]:
  print i

# 1
# …
# 5

for i in (1,2,3,4,5):
  print i

# 1
# …
# 5

for c in "abcde":
  print c

# a
# b
# …
# e


シーケンスは複数の要素を受け取る事ができる。

for i in [1,2],[3,4]:
  print i

# [1,2]
# [3,4]

先の記述では実行結果を
# 1
# 2
# 3
# 4
としてましたが訂正しました。(5/10)


それぞれの要素をタプルで受け取ることもできる

for (a,b) in [1,2],"ab",(4,5):
  print a + b

# 3
# ab
# 9

複数のシーケンスを与えると、それぞれのシーケンスのデカルト積をとるかと思ったがそうはならない。
リスト内包表記で使えるかもと思ったのに、残念。

関数

  • range関数

引数を1つ与えると0から任意の数までの範囲シーケンスを返す。
引数を2つ与えると任意の数から任意の数までの範囲シーケンスを返す。
引数を3つ与えると任意の数から任意の数までを3つめの引数の数ずつステップした範囲シーケンスを返す。

range(10)
# [0,1,2,3,4,5,6,7,8,9]

range(5,10)
# [5,6,7,8,9]

range(0,10,3)
# [0,3,6,9]

与える引数の数によって1個目の引数の意味が変わってるという事はpythonにはJavaのような関数のオーバーロードがある?
それとも可変引数で内部で引数の個数をチェックしてそれぞれ場合分けをしているのか。
rangeは意味的には引数2つを標準にして1つの場合はrange0to(num)という名前で別定義しても良さそう。
schemeとかだときっとそうなるはず。

  • キーワード付き引数
def foo(a,b):
  print a
  print b

# 引数の順番を逆にして呼び出す事もできる
foo(b="bbb", a="aaa")

# aaa
# bbb

ハッシュを渡せるかもと思いやってみた

h = {"a":"aa", "b":"bb"}
foo(h)

# Traceback (most recent call last):
#   File "<stdin>", line 1, in ?
# TypeError: foo() takes exactly 2 arguments (1 given)

無理みたいだ。

  • デフォルト引数は扱える
def print_spam(num=1):
  print "Spam" * num

print_spam()
# Spam
print_spam(2)
# SpamSpam

ファイル入出力

  • ファイルを開く時はopen()またはfile()関数を呼び出す。

戻り値がファイルポインタ的なオブジェクトになるので、
そのオブジェクトに対する操作をする事でファイルの操作を行う。
どうでもいいけど何故openまたはfileなんだろう。
どちらか一方にするか、むしろ二つ繋げてopen_fileの方が分かり易そうだが。。。

  • ファイルを開く時のオプション

<r,r+>
読み込み専用でシーク位置は0byte目となる。
この場合にwriteを実行するとエラーとなる。
「+」を付けると書き込みを行う事もできるようになる。


<w,w+>
書き込み専用でシーク位置は0byte目となる。
開く時にそれまでのファイルの内容を全てクリアする。
この場合にreadを実行するとエラーとなる。
「+」を付けると読み込みを行う事もできるようになる。


<a,a+>
書き込み専用でシーク位置はファイルの末尾となる。
ファイルを開く時にそれまでのファイルの内容はクリアしない。
この場合にreadを実行するとエラーとなる。
「+」を付けると読み込みを行う事もできるようになる。


まとめると以下の通り

option r r+ w w+ a a+
読み込み × ×
書き込み ×
オープン時のファイルクリア × × × ×
オープン時のシーク位置 先頭 先頭 先頭 先頭 末尾 末尾
  • readlines関数

readlines関数を使うと一度にファイルの内容を読み込み、行毎に分割したリストとして返してくれる。

f = open("C:\tmp.txt","r+")
for line in f.readlines():
  print line

readlines実行後はシーク位置はもちろん末尾になる。

  • writelines関数

writelinesの引数にシーケンスを渡すと、要素全てを書きだしてくれる。
但し、改行コードを挟んでくれない。
なるほど。


・・・!?
エッ!?
挟んでくれ”ない”のかい!
意味わからん。


単に読み込み側とデザインを合わせる為に用意しただけなのか、
もしくは後で出てくるリスト内包表記あたりと組み合わせる事ですごく便利に書けるに違いないと予想。



やっと全体の1/3まで読み終わった。