os.walk関数を使ってファイル数を列挙する

osモジュールの wolk 関数はちょっと面白い。ディレクトリを指定して呼び出すと、中に含まれるディレクトリ名、サブディレクトリのリスト、ファイルのリストを返すジェネレータを返してくれる。典型的にはこうやって使うみたい。

import os

for base_dir, dirs, files in os.walk("."):
    何らかの処理

そこで、ディレクトリに含まれるファイル数を列挙するスクリプトを書いてみた。

import os
import sys

for base_dir, dirs, files in os.walk(sys.argv[1]):
    num = len(files)
    print "%8d %-30s" % (num, base_dir)

実行結果:

^o^ > python count_files.py .
      83  .
       8  .\.git
      10  .\.git\hooks
       1  .\.git\info
       1  .\.git\logs
       0  .\.git\logs\refs
       1  .\.git\logs\refs\heads
       0  .\.git\logs\refs\remotes
       1  .\.git\logs\refs\remotes\origin
       1  .\.git\logs\refs\remotes\u
       0  .\.git\objects
       2  .\.git\objects\00
       1  .\.git\objects\01
       3  .\.git\objects\02
       3  .\.git\objects\03
       2  .\.git\objects\04
       1  .\.git\objects\07
       1  .\.git\objects\0c
       2  .\.git\objects\0d
       1  .\.git\objects\0e
       1  .\.git\objects\0f
       3  .\.git\objects\10
       1  .\.git\objects\13
       2  .\.git\objects\14
       3  .\.git\objects\16
       2  .\.git\objects\17
       1  .\.git\objects\19
       2  .\.git\objects\1a
       3  .\.git\objects\1b
       2  .\.git\objects\1c
       1  .\.git\objects\1f
       2  .\.git\objects\21
       5  .\.git\objects\22
       5  .\.git\objects\23
       1  .\.git\objects\25
       1  .\.git\objects\29
       2  .\.git\objects\2c
       1  .\.git\objects\2e
       2  .\.git\objects\2f
       2  .\.git\objects\30
       1  .\.git\objects\33
       1  .\.git\objects\35
       1  .\.git\objects\36
       1  .\.git\objects\37
       1  .\.git\objects\39
       3  .\.git\objects\3b
       1  .\.git\objects\3c
       2  .\.git\objects\40
       1  .\.git\objects\41
       1  .\.git\objects\42
       3  .\.git\objects\43
       1  .\.git\objects\44
       1  .\.git\objects\49
       4  .\.git\objects\4a
       2  .\.git\objects\4b
       2  .\.git\objects\4d
       1  .\.git\objects\4e
       3  .\.git\objects\4f
       1  .\.git\objects\51
       2  .\.git\objects\52
       2  .\.git\objects\53
       2  .\.git\objects\54
       1  .\.git\objects\55
       1  .\.git\objects\56
       3  .\.git\objects\57
       2  .\.git\objects\58
       1  .\.git\objects\59
       2  .\.git\objects\5a
       3  .\.git\objects\5d
       2  .\.git\objects\5e
       2  .\.git\objects\5f
       1  .\.git\objects\61
       1  .\.git\objects\62
       1  .\.git\objects\66
       1  .\.git\objects\67
       3  .\.git\objects\68
       1  .\.git\objects\69
       3  .\.git\objects\6a
       2  .\.git\objects\6b
       2  .\.git\objects\6c
       1  .\.git\objects\6d
       1  .\.git\objects\6f
       1  .\.git\objects\70
       1  .\.git\objects\71
       1  .\.git\objects\72
       1  .\.git\objects\75
       1  .\.git\objects\76
       1  .\.git\objects\77
       1  .\.git\objects\78
       1  .\.git\objects\79
       1  .\.git\objects\7b
       1  .\.git\objects\7c
       2  .\.git\objects\7d
       1  .\.git\objects\7f
       2  .\.git\objects\80
       1  .\.git\objects\85
       2  .\.git\objects\86
       1  .\.git\objects\87
       1  .\.git\objects\88
       1  .\.git\objects\89
       1  .\.git\objects\8b
       2  .\.git\objects\8d
       1  .\.git\objects\8e
       1  .\.git\objects\8f
       1  .\.git\objects\91
       2  .\.git\objects\93
       1  .\.git\objects\94
       3  .\.git\objects\95
       1  .\.git\objects\96
       3  .\.git\objects\97
       1  .\.git\objects\98
       2  .\.git\objects\9a
       3  .\.git\objects\9b
       1  .\.git\objects\9c
       3  .\.git\objects\a0
       2  .\.git\objects\a3
       1  .\.git\objects\a6
       1  .\.git\objects\a7
       1  .\.git\objects\a9
       1  .\.git\objects\ac
       1  .\.git\objects\ad
       1  .\.git\objects\ae
       1  .\.git\objects\b0
       2  .\.git\objects\b1
       1  .\.git\objects\b3
       3  .\.git\objects\b4
       2  .\.git\objects\b7
       1  .\.git\objects\b9
       3  .\.git\objects\ba
       1  .\.git\objects\bb
       1  .\.git\objects\bd
       1  .\.git\objects\c1
       1  .\.git\objects\c2
       1  .\.git\objects\c4
       2  .\.git\objects\c6
       2  .\.git\objects\c8
       1  .\.git\objects\c9
       2  .\.git\objects\cb
       1  .\.git\objects\cd
       2  .\.git\objects\ce
       3  .\.git\objects\cf
       2  .\.git\objects\d0
       1  .\.git\objects\d3
       4  .\.git\objects\d4
       1  .\.git\objects\d7
       3  .\.git\objects\d8
       2  .\.git\objects\d9
       2  .\.git\objects\da
       1  .\.git\objects\db
       3  .\.git\objects\dc
       5  .\.git\objects\dd
       1  .\.git\objects\e0
       1  .\.git\objects\e2
       1  .\.git\objects\e3
       1  .\.git\objects\e6
       1  .\.git\objects\e7
       2  .\.git\objects\e9
       1  .\.git\objects\ea
       1  .\.git\objects\ec
       5  .\.git\objects\ed
       3  .\.git\objects\ee
       1  .\.git\objects\ef
       1  .\.git\objects\f0
       1  .\.git\objects\f1
       2  .\.git\objects\f2
       1  .\.git\objects\f3
       1  .\.git\objects\f4
       2  .\.git\objects\f5
       1  .\.git\objects\f8
       1  .\.git\objects\fc
       1  .\.git\objects\fd
       1  .\.git\objects\fe
       0  .\.git\objects\info
       0  .\.git\objects\pack
       0  .\.git\refs
       1  .\.git\refs\heads
       0  .\.git\refs\remotes
       1  .\.git\refs\remotes\origin
       1  .\.git\refs\remotes\u
       0  .\.git\refs\tags
      10  .\projecteuler

git のリポジトリのファイルが多いな。

urllibモジュールの超簡単なサンプル(その3)

RSSフィードを取得して記事のタイトルを表示する。

import sys
import re
import urllib

url = sys.argv[1]

src = urllib.urlopen(url).read()

for title in re.findall(r"", src):
    print title.decode("utf-8")

実行例:

^o^ > python get_feed.py https://blog.panicblanket.com/feed
blog.PanicBlanket.com
urllibモジュールの超簡単なサンプル(その2)
urllibモジュールの超簡単なサンプル
変数に関数と同じ名前をつけてはいけない
== 演算子と is 演算子
randomモジュール
fileinputモジュール
Rubyで点数を集計するとき、あなたはどうしてますか? をPythonで
Pythonで偽と見なされる値
エラトステネスの篩
ファイルの文字数、ワード数、行数を数える

タイトルの抜き出しを正規表現でやってるので、ブログのタイトルまで表示されてるけど、とりあえずはこんなものだろう。ちゃんとやるならXML解析をやるべきだろうな。

urllibモジュールの超簡単なサンプル(その2)

URL を指定してヘッダーを得る。

import urllib
import sys

url = sys.argv[1]

r = urllib.urlopen(url)
info = r.info()

for key in info.keys():
    print key, ":", info[key]

実行結果:

^o^ > python httphead.py https://blog.panicblanket.com/
date : Sun, 03 Feb 2013 00:12:51 GMT
connection : close
content-type : text/html; charset=UTF-8
x-pingback : https://blog.panicblanket.com/xmlrpc.php
server : Apache/2.2.23

urllibモジュールの超簡単なサンプル

コマンドラインから入力された url を data.html に保存する。

import urllib
import sys

url = sys.argv[1]

filename = "data.html"

urllib.urlretrieve(url, filename)

実行結果の data.html(の冒頭部分)

<!DOCTYPE html>
<!--[if IE 6]>
<html id="ie6" lang="ja">
<![endif]-->
<!--[if IE 7]>
<html id="ie7" lang="ja">
<![endif]-->
<!--[if IE 8]>
<html id="ie8" lang="ja">
<![endif]-->
<!--[if !(IE 6) | !(IE 7) | !(IE 8)  ]><!-->
<html lang="ja">
<!--<![endif]-->
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>変数に関数と同じ名前をつけてはいけない | blog.PanicBlanket.com</title>
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="stylesheet" type="text/css" media="all" href="https://blog.panicblanket.com/wp-content/themes/twentyeleven/style.css" />
<link rel="pingback" href="https://blog.panicblanket.com/xmlrpc.php" />
<!--[if lt IE 9]>
<script src="https://blog.panicblanket.com/wp-content/themes/twentyeleven/js/html5.js" type="text/javascript"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="blog.PanicBlanket.com » フィード" href="https://blog.panicblanket.com/feed" />
<link rel="alternate" type="application/rss+xml" title="blog.PanicBlanket.com » コメントフィード" href="https://blog.panicblanket.com/comments/feed" />
<link rel="alternate" type="application/rss+xml" title="blog.PanicBlanket.com » 変数に関数と同じ名前をつけてはいけない のコメントのフィード" href="https://blog.panicblanket.com/archives/605/feed" />
<link rel='stylesheet' id='crayon_style-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/css/crayon_style.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='crayon_global_style-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/css/global_style.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='crayon-theme-classic-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/themes/classic/classic.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='crayon-font-monaco-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/fonts/monaco.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='wp-syntax-css-css'  href='https://blog.panicblanket.com/wp-content/plugins/wp-syntax/wp-syntax.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='thickbox-css'  href='https://blog.panicblanket.com/wp-includes/js/thickbox/thickbox.css?ver=20121105' type='text/css' media='all' />
<link rel='stylesheet' id='amazonjs-css'  href='https://blog.panicblanket.com/wp-content/plugins/amazonjs/amazonjs.css?ver=0.1beta3c' type='text/css' media='all' />
<script type='text/javascript' src='https://blog.panicblanket.com/wp-includes/js/jquery/jquery.js?ver=1.8.3'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var CrayonSyntaxSettings = {"version":"2.1.1","is_admin":"0","ajaxurl":"https:\/\/blog.panicblanket.com\/wp-admin\/admin-ajax.php","prefix":"crayon-","setting":"crayon-setting","selected":"crayon-setting-selected","changed":"crayon-setting-changed","special":"crayon-setting-special","orig_value":"data-orig-value","debug":""};
var CrayonSyntaxStrings = {"copy":"\u8cbc\u308a\u4ed8\u3051\u306b\u30b3\u30d4\u30fc\u3001%s\u306b%s\u3092\u62bc\u3057\u3066\u3001","minimize":"Click To Expand Code"};
/* ]]> */
</script>
<script type='text/javascript' src='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/js/util.js?ver=2.1.1'></script>
<script type='text/javascript' src='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/js/crayon.js?ver=2.1.1'></script>
<script type='text/javascript' src='https://blog.panicblanket.com/wp-includes/js/comment-reply.min.js?ver=3.5.1'></script>
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://blog.panicblanket.com/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://blog.panicblanket.com/wp-includes/wlwmanifest.xml" /> 
<link rel='prev' title='== 演算子と is 演算子' href='https://blog.panicblanket.com/archives/600' />
<meta name="generator" content="WordPress 3.5.1" />
<link rel='canonical' href='https://blog.panicblanket.com/archives/605' />
<link rel='shortlink' href='https://blog.panicblanket.com/?p=605' />
	<style type="text/css">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>
<style type="text/css" id="custom-background-css">
body.custom-background { background-color: #e2e2e2; }
</style>
</head>

変数に関数と同じ名前をつけてはいけない

Project Eulerのある問題を解いてるときにはまったのでメモ。
整数と文字列からスコアを計算してその合計を求める問題なんだけど、途中でこういうコードを書いた。

def name_score(num, str):
    v = sum(map((lambda c: ord(c) - 64), str))
    return num * v

print name_score(1, "FOO")

names = ["ANDY", "BILL", "CHARLIE"]

n = 0
sum = 0
for name in names:
    n += 1
    score = name_score(n, name)
    sum += score

print sum

name_score がスコアを計算する関数で、6行目と16行目(forブロックの中)の2箇所で呼び出している(6行目はちゃんと動くかの確認用)。ところが、これを実行するとこうなる。

^o^ > python a.py
36
Traceback (most recent call last):
  File "a.py", line 16, in 
    score = name_score(n, name)
  File "a.py", line 2, in name_score
    v = sum(map((lambda c: ord(c) - 64), str))
TypeError: 'int' object is not callable

1回目の呼び出し(6行目)ではちゃんとスコアが計算されているのに、2回目(forブロックの中)ではエラーになって、’int’オブジェクトは callable じゃないと怒られる。
これで小一時間ほども悩んだけど、問題は13行目のsumという変数と2行目で使っているsum関数の名前が同じことだった。

Pythonでは、定義した関数をほかの変数に代入することができる。

>>> def hello(name):
...     print "Hello,", name
...
>>> hello("Andy")
Hello, Andy
>>> greeting = hello
>>> greeting("Bill")
Hello, Bill

それはいいんだけど、逆に関数名にほかの値、たとえば整数を代入することもできてしまう。

>>> hello = 1
>>> hello("Charlie")
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'int' object is not callable

hello には 1 を代入してしまったので、関数呼び出しのつもりがエラーになってしまっている。上で紹介したコードでも13行目で sum に 0 を代入しているので、同じことが起こったわけだ。
結局、変数名を変えたら期待通りに動いてくれた。

def name_score(num, str):
    v = sum(map((lambda c: ord(c) - 64), str))
    return num * v

print name_score(1, "FOO")

names = ["ANDY", "BILL", "CHARLIE"]

n = 0
sum_of_score = 0
for name in names:
    n += 1
    score = name_score(n, name)
    sum_of_score += score

print sum_of_score
^o^ > python b.py
36
282

Python では関数もオブジェクトだからかもしれないけど、普通の変数名と関数名の間に区別がないようだ。

今日の結論:変数に関数と同じ名前をつけてはいけない。

== 演算子と is 演算子

どっちも等しいかどうかの比較演算子だけど、簡単に言うと、== は値として等しいか(同値性)で、is はオブジェクトとして等しいか(同一性)の判定をする。だから次のようにaとbに別々に代入した場合では、値が等しくてもオブジェクトとしては等しくないので is での比較の結果は False になる。

>>> a = (1,2,3)
>>> b = (1,2,3)
>>> a == b
True
>>> a is b
False

id関数でオブジェクトのIDをみても、べつのオブジェクトだということがわかる。

>>> id(a)
5089160
>>> id(b)
4602696

一方で、bにaを代入した場合には、オブジェクトとしても等しいので、== でも is でも True になる。

>>> a = (1,2,3)
>>> b = a
>>> a == b
True
>>> a is b
True

ちなみに否定の場合は is not。

>>> a is b
True
>>> a is not b
False

randomモジュール

CodeIQの問題を解くのに random モジュールを調べたのでメモ。

cf. http://docs.python.jp/2.7/library/random.html

使った関数

random.randrange は指定された範囲の整数からランダムな整数を返す。

>>> import random
>>> random.randrange(1, 100)
51

random.sample は母集団のシーケンスから選ばれたリストをかえす。リストの長さを指定する。

>>> random.sample(range(1,101), 10)
[54, 3, 5, 58, 24, 18, 47, 57, 62, 40]

使ってない関数からいくつか

random.randint は整数a,bを指定しa≦N≦bであるような整数Nを返す。random.randrangeよりもこっちを使ったほうがよかったかも。

>>> random.randint(1,100)
20

random.choice は空でないシーケンスから要素をひとつ返す。

>>> random.choice(range(10))
8

random.shuffle はシーケンスxを直接かき混ぜる。

>>> x = range(10)
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> random.shuffle(x)
>>> x
[6, 2, 3, 8, 5, 4, 1, 7, 9, 0]

random.random は値域[0.0,1.0)からランダムな浮動小数点数を返す。

>>> random.random()
0.7121347944194496

とりあえずこんなとこかな。

fileinputモジュール

コマンドライン引数で指定したファイルや標準入力からの入力を簡単に処理できるモジュール。
fileinput.input で各ファイルから1行ずつ読み込んでくれる。

次の例は、ファイルの各行をファイル名と行番号つきで出力する。

import fileinput

for line in fileinput.input():
    print fileinput.filename(), fileinput.filelineno(), ":", line.rstrip("\n")

実行例:

^o^ > python file_input.py fib.py file_read.py
fib.py 1 : def fib():
fib.py 2 :     a = 1
fib.py 3 :     b = 1
fib.py 4 :     while True:
fib.py 5 :         yield a
fib.py 6 :         a, b = b, a+b
fib.py 7 :
fib.py 8 :
fib.py 9 : i = fib()
fib.py 10 :
fib.py 11 : for c in range(10):
fib.py 12 :     print i.next()
fib.py 13 :
file_read.py 1 : import sys
file_read.py 2 :
file_read.py 3 : filename = sys.argv[1]
file_read.py 4 : print filename
file_read.py 5 : print "--"
file_read.py 6 :
file_read.py 7 : file = open(filename, "r")
file_read.py 8 : contents = file.read()
file_read.py 9 : print contents
file_read.py 10 : file.close()
file_read.py 11 :

標準入力へリダイレクトした場合:

^o^ > python file_input.py < fib.py
<stdin> 1 : def fib():
<stdin> 2 :     a = 1
<stdin> 3 :     b = 1
<stdin> 4 :     while True:
<stdin> 5 :         yield a
<stdin> 6 :         a, b = b, a+b
<stdin> 7 :
<stdin> 8 :
<stdin> 9 : i = fib()
<stdin> 10 :
<stdin> 11 : for c in range(10):
<stdin> 12 :     print i.next()
<stdin> 13 :

パイプを使った場合:

^o^ > type fib.py | python file_input.py
<stdin> 1 : def fib():
<stdin> 2 :     a = 1
<stdin> 3 :     b = 1
<stdin> 4 :     while True:
<stdin> 5 :         yield a
<stdin> 6 :         a, b = b, a+b
<stdin> 7 :
<stdin> 8 :
<stdin> 9 : i = fib()
<stdin> 10 :
<stdin> 11 : for c in range(10):
<stdin> 12 :     print i.next()
<stdin> 13 :

Rubyで点数を集計するとき、あなたはどうしてますか? をPythonで

Python でやってみた。

cf. Rubyで点数を集計するとき、あなたはどうしてますか? - hp12c

import re

class Score:
    def __init__(self, name, a, b):
        self.name = name
        self.a = a
        self.b = b

    def total(self):
        return self.a + self.b

data ="""player gameA gameB
Bob 20 56
Ross 68 33
Bob 78 55
Kent 90 15
Alice 84 79
Ross 10 15
Jimmy 80 31
Bob 12 36
Kent 88 43
Kent 12 33
Alice 90 32
Ross 67 77
Alice 56 92
Jimmy 33 88
Jimmy 11 87"""

a = re.split("\n", data)
headers = re.split("\s+", a[0])
values = map((lambda x: re.split("\s+", x)), a[1:])

scores = {}
for s in values:
    player = s[0]
    if player in scores:
        scores[player].a += int(s[1])
        scores[player].b += int(s[2])
    else:
        scores[player] = Score(player, int(s[1]), int(s[2]))

lanking = scores.values()
lanking.sort(lambda a,b: cmp(b.total(), a.total()))

print "%s\t%s\t%s\ttotal" % tuple(headers)
for player in lanking:
    print "%s\t%d\t%d\t%d" % (player.name, player.a, player.b, player.total())

実行結果:

^o^ > python game_score.py
player  gameA   gameB   total
Alice   230     203     433
Jimmy   124     206     330
Kent    190     91      281
Ross    145     125     270
Bob     110     147     257

Pythonで偽と見なされる値

Python のライブラリリファレンス5.1. 真理値テストには、次の値が偽と判定されると書いてある。

  • None
  • False
  • 数値におけるゼロ。例えば 0, 0L, 0.0, 0j 。
  • 空のシーケンス型。例えば ”, (), [] 。
  • 空のマッピング型。例えば {} 。
  • __nonzero__() または __len__() メソッドが定義されているようなユーザ定義クラスのインスタンスで、それらのメソッドが整数値ゼロまたは bool 値の False を返すとき。

そこで、まず次のようなスクリプトを書いて確かめてみた。

def istrue(val):
    if val:
        print "true"
    else:
        print "false"

print "number 0:", istrue(0)

print "empty string \"\":", istrue("")

print "empty list []:", istrue([])

print "empty tuple ():", istrue(())

print "empty dictionary {}:", istrue({})

print "False:", istrue(False)

print "None:", istrue(None)

実行:

^o^ > python false.py
number 0: false
empty string "": false
empty list []: false
empty tuple (): false
empty dictionary {}: false
False: false
None: false

予想通りすべて false を返した。

もうひとつ、__nonzero__メソッドや__len__メソッドを持ったオブジェクトで試してみよう。Fooクラスは真になり、Barクラスは偽になるようにしてみる。

class Foo:
    def __nonzero__(self):
        return True

class Bar:
    def __len__(self):
        return 0

def istrue(val):
    if val:
        print "true"
    else:
        print "false"

print "Foo:", istrue(Foo())

print "Bar:", istrue(Bar())

実行:

^o^ > python true_or_false.py
Foo: true
Bar: false

これも予想通り、Foo は true を返し、Bar は false を返した。

ところで、__nonzero__ と __len__ の両方を持っていて、返す値が矛盾する場合はどうなるんだろう。Bazクラスは__nonzero__ に対しては True を返し、__len__ に対しては 0 を返す。

class Baz:
    def __nonzero__(self):
        return True

    def __len__(self):
        return 0

def istrue(val):
    if val:
        print "true"
    else:
        print "false"

print "Baz:", istrue(Baz()) 

実行:

^o^ > python true_or_false2.py
Baz: true

結果は true だった。どうやら __nonzero__ のほうが優先するみたいだ。