easy_install

easy_install は Python のパッケージインストーラ、というかコマンドだ。Setuptools や Distribute に含まれている。で、ここがちょっと混乱するところなんだけど、Setuptools を使えばいいのか、それとも Distrubute を使うほうがいいのか、ってこと。Web の情報を見てみると、Setuptools は開発がとまっていて、そこから fork したのが Distribute らしい。

cf. エキPy読書会(第2期) 第1回に行ってきました – 今川館

というわけで、Distribute をインストールしてみた。

Distribute をインストールするには、次のURLからスクリプトをダウンロードして実行すればいい。

http://python-distribute.org/distribute_setup.py

^o^ > wget http://python-distribute.org/distribute_setup.py
^o^ > python distribute_setup.py

なんかいろいろとメッセージが出て、インストールが完了したらしい。
Pythonをインストールしたディレクトリの下にScripts\easy_install.exe というファイルができているので、ここにパスを通せばOK。
これで easy_install コマンドが使えるようになったはず。

^o^ > easy_install

ところが、いざ実行すると「ユーザーアカウント制御」というダイアログがでてきて、「はい」をクリックしても「いいえ」をクリックしてもそのまま終了してしまう。どうやら、Windows 7 のユーザー権限にひっかかっているらしい。どうすりゃいいんだ。

[追記]
どうもファイル名にinstallと入っているのが原因らしい。
cf. http://phpy.readthedocs.org/en/latest/install.html

次のようにすれば回避できるようだ。

^o^ > python -m easy_install
error: No urls, filenames, or requirements specified (see --help)

^o^ > python -m easy_install --help

Global options:
  --verbose (-v)  run verbosely (default)
  --quiet (-q)    run quietly (turns verbosity off)
  --dry-run (-n)  don't actually do anything
  --help (-h)     show detailed help message
  --no-user-cfg   ignore pydistutils.cfg in your home directory

Options for 'easy_install' command:
  --prefix                       installation prefix
  --zip-ok (-z)                  install package as a zipfile
  --multi-version (-m)           make apps have to require() a version
  --upgrade (-U)                 force upgrade (searches PyPI for latest
                                 versions)
  --install-dir (-d)             install package to DIR
  --script-dir (-s)              install scripts to DIR
  --exclude-scripts (-x)         Don't install scripts
  --always-copy (-a)             Copy all needed packages to install dir
  --index-url (-i)               base URL of Python Package Index
  --find-links (-f)              additional URL(s) to search for packages
  --delete-conflicting (-D)      no longer needed; don't use this
  --ignore-conflicts-at-my-risk  no longer needed; don't use this
  --build-directory (-b)         download/extract/build in DIR; keep the
                                 results
  --optimize (-O)                also compile with optimization: -O1 for
                                 "python -O", -O2 for "python -OO", and -O0 to
                                 disable [default: -O0]
  --record                       filename in which to record list of installed
                                 files
  --always-unzip (-Z)            don't install as a zipfile, no matter what
  --site-dirs (-S)               list of directories where .pth files work
  --editable (-e)                Install specified packages in editable form
  --no-deps (-N)                 don't install dependencies
  --allow-hosts (-H)             pattern(s) that hostnames must match
  --local-snapshots-ok (-l)      allow building eggs from local checkouts
  --version                      print version information and exit
  --no-find-links                Don't load find-links defined in packages
                                 being installed
  --user                         install in user site-package
                                 'C:\Users\takatoh\AppData\Roaming\Python\Python2
                                 7\site-packages'

usage: easy_install.py [options] requirement_or_url ...
   or: easy_install.py --help

日本語とファイル入出力

日本語ファイルの入力

前回のエントリに書いたとおり、Pythonの内部ではユニコード文字列を使うのがいいようだ。日本語のファイルを読み書きする場合、読み込んだ文字列をユニコードにデコードし、何らかの処理したあと、出力するときに出力先にあわせてエンコードする、という流れになる。
次の例は、シフトJISで書かれた名前のファイルを読み込んで、挨拶をするプログラムだ。

# coding: utf-8

import sys

input_file = open(sys.argv[1], "r")

for name in input_file:
    uname = name.decode("sjis").rstrip("\r\n")
    msg = u"こんにちは、" + uname
    print msg.encode("sjis")

読み込んだ文字列を10行目でデコードしてユニコード文字列にし、12行目で出力する際にシフトJISにエンコードしている。

実行:

^o^ > type nihongo_sjis.txt
アンディ
ビル
チャーリー

^o^ > python konnichiwa4.py nihongo_sjis.txt
こんにちは、アンディ
こんにちは、ビル
こんにちは、チャーリー

codecsモジュールのopen関数を使うと、ファイルから読み込んだ文字列をデコードする手間が省ける。codecs.open関数には、ファイルのエンコーディングを指定することができる。

# coding: utf-8

import sys
import codecs

input_file = codecs.open(sys.argv[1], "r", "sjis")

for name in input_file:
    name = name.rstrip("\r\n")
    msg = u"こんにちは、" + name
    print msg.encode("sjis")

ファイルをオープンするときにエンコーディングを指定していて、読み込んだ文字列ばデコードしなくてもユニコード文字列になっている。

^o^ > python konnichiwa4a.py nihongo_sjis.txt
こんにちは、アンディ
こんにちは、ビル
こんにちは、チャーリー

日本語のファイル出力

codecs.open関数はファイルに日本語を書き込むときにも使える。

# coding: utf-8

import sys
import codecs

output_file = codecs.open("nihongo_out_sjis.txt", "w", "sjis")

output_file.write(u"こんにちは、世界")
^o^ > python konnichiwa5.py

^o^ > type nihongo_out_sjis.txt
こんにちは、世界

Windows のコマンドプロンプトでちゃんと表示されるってことはファイルの中身がシフトJISになってるってことだ。

日本語のファイル名

Pythonでは、ファイル名を扱うほとんどの関数がユニコード文字列に対応している。引数としてユニコード文字列を渡してやれば、あとはプラットフォームにあわせてうまくやってくれる。

# coding: utf-8

import sys
import codecs

input_file_name = sys.argv[1].decode("sjis")
input_file = codecs.open(input_file_name, "r", "sjis")

for wday in input_file:
    wday = wday.rstrip("\r\n")
    print wday.encode("sjis")

実行結果:

^o^ > type 日本語.txt
月曜日
火曜日
水曜日
木曜日
金曜日
土曜日
日曜日

^o^ > python nihongo.py 日本語.txt
月曜日
火曜日
水曜日
木曜日
金曜日
土曜日
日曜日

ユニコード文字列と8ビット文字列

ユニコード文字列と8ビット文字列

Python の文字列にはユニコード文字列と8ビット文字列がある。8ビット文字列というのが正式な言い方かどうかはわからないけど「みんなのPython」の中ではそう呼んでいる。
で、どう違うかというと、言ってみればユニコード文字列はPythonの内部表現で、8ビット文字列はその他のエンコードを持った文字列といってよさそう。

ユニコード文字列のリテラルには、頭に u をつける。

>>> s = u'abc'
>>> type(s)
<type 'unicode'>

上にあるように、ユニコード文字列の型は unicode になる。これに対して普通の文字列、つまり8ビット文字列の型は str だ。

>>> s2 = 'abc'
>>> type(s2)
<type 'str'>

スクリプトエンコーディング

ASCII文字だけを使うときは気にすることは無いけど、日本語を使うときにはスクリプトのエンコーディングにも気をつけなきゃいけない。といっても、Pythonの場合は UTF-8 にしておくのはほとんどデフォルトのようだ。
スクリプトのエンコーディングは、1行目か2行目に次のように書く。

# coding: utf-8

もちろん、スクリプトファイルをUTF-8で保存するのを忘れずに。

日本語の出力

Windowsで日本語を出力するときには、ユニコード文字列をShift-JISに変換してやる。
エンコードの変換には、文字列の encode 関数が使える。

# coding: utf-8

s = u'こんにちは'

print s.encode('sjis')

実行結果:

^o^ > python konnichiwa.py
こんにちは

実はわざわざsjisにエンコードしなくてもちゃんと出力される。

# coding: utf-8

s = u'こんにちは'

print s

実行結果:

^o^ > python konnichiwa2.py
こんにちは

どうも暗黙のうちにコンソールに合わせてエンコードしてくれているみたいだ。だけど、これだとファイルにリダイレクトするとうまくいかない。

^o^ > python konnichiwa2.py > konnichiwa.txt
Traceback (most recent call last):
  File "konnichiwa2.py", line 5, in 
    print s
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordin
al not in range(128)

‘ascii’ codec がエンコードできないといっている。標準出力のときにはうまくいくのにリダイレクトすると何でだめなのか、謎。

日本語を入力

コマンドラインから日本語を入力することを考える。
スクリプトの外部から来た文字列はすべて8ビット文字列だ。だから、ユニコード文字列に変換してやる必要がある。変換には文字列の decode 関数が使える。

# coding: utf-8

import sys

name = sys.argv[1].decode('sjis')

s = u'こんにちは' + name

print s.encode('sjis')

実行結果:

^o^ > python konnichiwa3.py アンディ
こんにちはアンディ

Pythonで使えるエンコード名

最後に書いておこう。

エンコードPythonのエンコード名
シフトJISshift-jis shift_jis sjis
ISO-2022-JPiso-2022-jp
EUC-JPeuc-jp
UTF-8utf-8

Python Shellのプロンプトの表示を変更する:Windows編

Python Shellのプロンプトの表示を変更する。sys.ps1,sys.ps2を変更する。 ― oneshotlife_tomの日記を読んで、Windowsでもやってみた。
Python Shell のプロンプトってのは↓これのことね。

>>>

上の記事ではWindowsでのやり方はわからなかったように書いてあるけど、結果からいえば同じやり方でできた。つまり、sys.ps1 や sys.ps2 にほかの文字列を代入してやればいい。ちなみに Python 2.7.3。

>>> import sys
>>> sys.ps1 = '$'
$'spam'
'spam'
$sys.ps2 = ']]]'
$for i in range(3):
]]]    print 'spam'
]]]
spam
spam
spam
$

Pythonの文字列操作

いまさらだけど文字列の操作についてメモしておく。

リテラル

文字列のリテラルはシングルクォートかダブルクォートで囲んで作る。シングルクォートの中ではダブルクォートが、ダブルクォートの中ではシングルクォートがそのまま使えるという以外には違いが無いみたい。

>>> 'abc'
'abc'
>>> "abc"
'abc'
>>> "APOSTROPHE(')"
"APOSTROPHE(')"

三重のクォート(ダブルクォートでもシングルクォートでも)で囲むと、エスケープなしで改行を含めることができる。

>>> '''foo
... bar
... baz'''
'foo\nbar\nbaz'

連結

文字列の連結は、+演算子を使う。

>>> a = 'foo'
>>> b = 'bar'
>>> a + b
'foobar'

リテラル同士なら、+演算子なしでも並べるだけで連結される。

>>> 'foo' "bar"'baz'
'foobarbaz'

抽出とスライス

文字列中の文字は、インデックスを使って抽出することができる。

>>> s = 'abcde'
>>> s[3]
'd'

部分文字列の抽出にはスライスを使う。スライスの前または後ろは省略することもできる。このときは文字列の最初または最後が指定されたのと同じ。

>>> s[2:4]
'cd'
>>> s[:3]
'abc'
>>> s[3:]
'de'

大文字・小文字

文字列のupper、lowerメソッドを使う。

>>> 'abcde'.upper()
'ABCDE'
>>> 'ABCDE'.lower()
'abcde'

キャピタライズ。

>>> 'abcde'.capitalize()
'Abcde'

titleという面白いメソッドもある。CDアルバムや書籍のタイトルみたいに各単語をキャピタライズしてくれる。

>>> 'one size fits all'.title()
'One Size Fits All'

部分文字列の検索

findは左から、rfindは右から検索して部分文字列の先頭位置をインデックスで返す。もし見つからない場合は-1が返ってくる。

>>> 'foobarbazfoobarbaz'.find('bar')
3
>>> 'foobarbazfoobarbaz'.rfind('bar')
12
>>> 'foobarbazfoobarbaz'.find('abc')
-1

index、rindexはfind、rfindと同様だけど、部分文字列が見つからなかった場合にはエラーを発生する。

>>> 'foobarbazfoobarbaz'.index('baz')
6
>>> 'foobarbazfoobarbaz'.rindex('baz')
15
>>> 'foobarbazfoobarbaz'.rindex('abc')
Traceback (most recent call last):
  File "", line 1, in 
ValueError: substring not found

countは部分文字列が何回出現するかを返す。

>>> 'foobarbazfoobarbaz'.count('foo')
2

開始文字列と終了文字列

文字列が、部分文字列で始まっているかあるいは終わっているかを調べるには、startswith、endswithを使う。

>>> 'foobarbaz'.startswith('foo')
True
>>> 'foobarbaz'.endswith('foo')
False

置換

部分文字列を置き換えるには、replaceを使う。

>>> 'foobarbaz'.replace('bar', 'BAR')
'fooBARbaz'

部分文字列が複数回現れるときには、すべて置換してくれるようだ。

>>> 'foobarbazfoobarbaz'.replace('bar', 'BAR')
'fooBARbazfooBARbaz'

正規表現で部分文字列を指定するには、reモジュールのre.sub関数が使える。

>>> re.sub('ba.', 'X', 'foobarbazfoobarbaz')
'fooXXfooXX'

リストに分割・リストから組み立てる

リストに分割するには、splitを使う。

>>> 'abcacbcba'.split('b')
['a', 'cac', 'c', 'a']

ところが、1文字ずつに分割しようとしてもうまくいかない。

>>> 'abcacbcba'.split('')
Traceback (most recent call last):
  File "", line 1, in 
ValueError: empty separator

こういうときは、list関数を使えばいい。

>>> list('abcacbcba')
['a', 'b', 'c', 'a', 'c', 'b', 'c', 'b', 'a']

正規表現で分割したいときは、reモジュールのre.splitを使う。

>>> import re
>>> re.split(' +', 'foo bar   baz')
['foo', 'bar', 'baz']

リストからひとつの文字列に組み立てるにはjoin。

>>> ','.join(['foo', 'bar', 'baz'])
'foo,bar,baz'

前後の空白を削除する

stripを使う。

>>> '  foo  '.strip()
'foo'

左だけ、または右だけ削除するにはlstripとrstrip。

>>> '  foo  '.lstrip()
'foo  '
>>> '  foo  '.rstrip()
'  foo'

どのメソッドも削除する文字を指定することもできる。

>>> '--  foo  =='.strip('-')
'  foo  =='
>>> '--  foo  =='.lstrip('-')
'  foo  =='
>>> '--  foo  =='.rstrip('-')
'--  foo  =='

片寄せ・中寄せ

左または右に寄せるには、ljust、rjust。中寄せするにはcenter。

>>> 'foo'.ljust(10)
'foo       '
>>> 'foo'.rjust(10)
'       foo'
>>> 'foo'.center(10)
'   foo    '

文字種を調べる

isalpha、isdigit、isalnum、isspace。

>>> 'abc'.isalpha()
True
>>> '123'.isdigit()
True
>>> 'abc123'.isalnum()
True
>>> '  \t\n'.isspace()
True

isuppre、islower。

>>> 'FOO'.isupper()
True
>>> 'foo'.islower()
True

このくらいかな。

argparse でサブコマンドを実装してみた

argparseモジュールでは、オプションの処理だけでなく、サブコマンドの処理もできる。
マニュアルはここ。
cf. http://docs.python.jp/2/library/argparse.html#id10

サンプル

次のサンプルは、foo と bar の2つのサブコマンドを持っている。

import argparse

def foo(args):
    if args.upcase:
        print 'FOO'
    else:
        print 'foo'

def bar(args):
    for i in range(args.times):
        print 'bar'

parser = argparse.ArgumentParser(description='Sub-command sample')
subparsers = parser.add_subparsers(help='sub-command help', title='subcommands')

parser_foo = subparsers.add_parser('foo', help='foo help')
parser_foo.add_argument('-u', '--upcase', action='store_true', help='upcase')
parser_foo.set_defaults(func=foo)

parser_bar = subparsers.add_parser('bar', help='bar help')
parser_bar.add_argument('-t', '--times', type=int, metavar='N', action='store',
default=1, help='repeat N times')
parser_bar.set_defaults(func=bar)

args = parser.parse_args()

args.func(args)

16行目でサブコマンドを振り分ける(らしき)オブジェクトを作り、18~20行目と22~25行目でそれぞれfooとbarの2つのサブコマンドを作っている。ここでのキモは20行目と25行目、set_defaultsでサブコマンドが選択されたときに実行すべき関数を登録しているところ。
そして、最後の行でその関数を実行している。

実行例

メインコマンドの–helpオプション:

^o^ > python subcommand.py --help
usage: subcommand.py [-h] {foo,bar} ...

Sub-command sample

optional arguments:
  -h, --help  show this help message and exit

subcommands:
  {foo,bar}   sub-command help
    foo       foo help
    bar       bar help

サブコマンド foo:

^o^ > python subcommand.py foo --help
usage: subcommand.py foo [-h] [-u]

optional arguments:
  -h, --help    show this help message and exit
  -u, --upcase  upcase

^o^ > python subcommand.py foo
foo

^o^ > python subcommand.py foo --upcase
FOO

サブコマンドのヘルプも自動的に作ってくれている。

サブコマンド bar:

^o^ > python subcommand.py bar
bar

^o^ > python subcommand.py bar --times 3
bar
bar
bar

argparse を試してみた

コマンドライン引数を処理する argparseモジュールを試してみた。argparseモジュールにはすごくたくさんの機能があってとてもじゃないけどなかなか把握しきれないので、今回は基本的なことだけ。Hello, worldプログラムを元にいくつか引数処理を追加してみた。

まずはベースとなるHello, worldプログラムを書いておく。

print 'Hello, world.'
^o^ > python hello_args0.py
Hello, world.

基本的なフロー

基本的には、

  1. パーサーを作る
  2. 引数を追加する
  3. パースする

という流れになる。パーサーは argparse.ArgumentParser。コンストラクタの引数にもいろいろあるけど、とりあえずはプログラムの概要を示すdescription引数だけ与えておけばよさそうだ。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.parse_args()

print 'Hello, world.'

上の例では引数を定義していない。これでも立派にargparse.ArgumentParserは動いてくれる。

^o^ > python hello_args1.py --help
usage: hello_args1.py [-h]

Hello world program.

optional arguments:
  -h, --help  show this help message and exit

このとおり、–helpオプションは勝手に作ってくれる。

位置引数

argparseでは’-‘または’–‘で始まらない引数を位置引数と呼んでいるらしい。普通はオプションとは言わない引数のことだな。で、これを使って world の代わりに任意の名前を指定できるようにしたものがこれ。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store',
                    help='specify name instead "world".')
args = parser.parse_args()

if args.name:
    print 'Hello, %s.' % (args.name)
else:
    print 'Hello, world.'

5,6行目でadd_argumentメソッドを使って位置引数を追加している。’name’は引数の名前、これはあとで引数の値を参照するときの属性名になる。metavar=’NAME’はヘルプメッセージの中で使う文字列、nargs=’?’は引数が無いか1つだけあることを示している。action=’store’は引数を属性にセットすることを指示している。最後のhelp=~はヘルプメッセージに表示する引数の説明だ。

7行目でコマンドライン引数をパースし、args変数に代入。args変数には指定した引数がname属性としてセットされていて、10行目で参照している。

^o^ > python hello_args2.py --help
usage: hello_args2.py [-h] [NAME]

Hello world program.

positional arguments:
  NAME        specify name instead "world".

optional arguments:
  -h, --help  show this help message and exit
^o^ > python hello_args2.py Andy
Hello, Andy.

値をとらないオプション

‘-‘または’–‘から始まるオプションには値をとるオプションととらないオプションがある。まずは値をとらないオプションから。
‘-m’または’–morning’オプションは、Helloの代わりにGood morningと挨拶する。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store',
                    help='specify name instead "world".')
parser.add_argument('-m', '--morning', dest='morning', action='store_true',
                    help='good morning')
args = parser.parse_args()

if args.morning:
    greeting = 'Good morning'
else:
    greeting = 'Hello'

if args.name:
    print '%s, %s.' % (greeting, args.name)
else:
    print '%s, world.' % (greeting)

dest=’morning’はあとから参照するときの属性名を指定している。これを指定しないと長いオプション名から’–‘を取り除いたものが属性名になるらしい。action=’store_true’は属性にTrueをセットすることを指示している。ちなみにaction=’store_false’とすることもできる。

^o^ > python hello_args3.py --help
usage: hello_args3.py [-h] [-m] [NAME]

Hello world program.

positional arguments:
  NAME           specify name instead "world".

optional arguments:
  -h, --help     show this help message and exit
  -m, --morning  good morning
^o^ > python hello_args3.py --morning Andy
Good morning, Andy.

値をとるオプション

値をとるオプションを作るには、action=’store’とする。こうすると属性に値がセットされる。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store',
                    help='specify name instead "world".')
parser.add_argument('-m', '--morning', dest='morning', action='store_true',
                    help='good morning')
parser.add_argument('-t', '--times', dest='times', type=int, action='store', default=1,
                    metavar='N', help='repeat N times')
args = parser.parse_args()

if args.morning:
    greeting = 'Good morning'
else:
    greeting = 'Hello'

for n in range(args.times):
    if args.name:
        print '%s, %s.' % (greeting, args.name)
    else:
        print '%s, world.' % (greeting)

type=’int’ は値が整数であるべきことを指示している。default=1 は –times オプションが指定されなかったときのデフォルト値だ。

^o^ > python hello_args4.py --help
usage: hello_args4.py [-h] [-m] [-t N] [NAME]

Hello world program.

positional arguments:
  NAME             specify name instead "world".

optional arguments:
  -h, --help       show this help message and exit
  -m, --morning    good morning
  -t N, --times N  repeat N times
^o^ > python hello_args4.py --times 3 Andy
Hello, Andy.
Hello, Andy.
Hello, Andy.

値をとらないオプション(2) action=’store_const’

さて、最後にメインルーチンの部分をもっとすっきりさせよう。何でこんなにごちゃごちゃしてるかといえば、if文が2つもあるからだ。
まずひとつは位置引数nameにデフォルト値を設定する。
それからもうひとつは、–morningオプションで設定する値を True から実際に出力する値に変更する。これには action=’store_const’ という、特定の値をセットするアクションが使える。

import argparse

parser = argparse.ArgumentParser(description='Hello world program.')
parser.add_argument('name', metavar='NAME', nargs='?', action='store', default="world",
                    help='specify name instead "world".')
parser.add_argument('-m', '--morning', dest='greeting', action='store_const',
                    const='Good morning', default='Hello', help='good morning')
parser.add_argument('-t', '--times', dest='times', type=int, action='store', default=1,
                    metavar='N', help='repeat N times')
args = parser.parse_args()

for n in range(args.times):
    print '%s, %s.' % (args.greeting, args.name)

action=’store_const’ とすると、constで指定した値がセットされる。また、オプションが指定されなかった場合には default で指定されている値がセットされる。ここでは const=’Good morning’、default=’Hello’としている。

^o^ > python hello_args5.py Andy
Hello, Andy.

^o^ > python hello_args5.py --morning Andy
Good morning, Andy.

期待通りに動作しているようだ。

その他

argparseにはこのほかにもいろいろな機能があって、たとえばサブコマンドを扱ったりもできる。このエントリでは扱いきれないので、いつか書いてみたい。
なお、詳しくはマニュアルを参照のこと。

cf. http://docs.python.jp/2/library/argparse.html

in 演算子

よく忘れるのでメモ。
シーケンス型やマップ型の中に値が存在するかどうかを確かめるのに、in演算子が使える。

文字列:

>>> 'a' in 'apple'
True
>>> 'b' in 'apple'
False

リスト:

>>> 5 in [1,2,3,4,5,6,7,8,9,10]
True
>>> 12 in [1,2,3,4,5,6,7,8,9,10]
False

タプル:

>>> 1 in (1,2,3)
True
>>> 5 in (1,2,3)
False

setでも使える:

>>> 6 in set([1,2,3,4,5,6,7,8,9])
True
>>> 10 in set([1,2,3,4,5,6,7,8,9])
False

辞書の場合には、キーが存在するか、が返ってくる:

>>> 'python' in {'ruby': 'matz', 'perl': 'larry', 'python': 'guido'}
True
>>> 'php' in {'ruby': 'matz', 'perl': 'larry', 'python': 'guido'}
False

逆順のリスト

小ネタのメモ。

Pythonで逆順のリスト、たとえば10~0のリストを作るとき、今まではこうやっていた。

>>> l = range(11)
>>> l.reverse()
>>> l
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

はっきりいってめんどくさい。で、実はrange関数は第3引数にステップを取ることができるので、-1を与えてやれば逆順のリストもできることに気がついた。

>>> l2 = range(10, -1, -1)
>>> l2
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Pythonでユークリッドの互除法

GCD

mathモジュールに最大公約数(GCD)を求める関数が無いようなので作ってみた。
アルゴリズムの説明はWikipediaに載っている。

cf. http://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3%83%89%E3%81%AE%E4%BA%92%E9%99%A4%E6%B3%95

def gcd(a, b):
    if a < b:
        a, b = b, a
        if b == 0:
            return a
        c = a % b
        return gcd(b, c)


if __name__ == '__main__':
    import sys

    a = int(sys.argv[1])
    b = int(sys.argv[2])
    print gcd(a, b)

実行例:

^o^ > python gcd.py 24 36
12

LCM

ついでに最小公倍数(LCM)もやってみた。

import gcd

def lcm(a, b):
    return a * b / gcd.gcd(a,b)


if __name__ == '__main__':
    import sys

    a = int(sys.argv[1])
    b = int(sys.argv[2])
    print lcm(a, b)

実行例:

^o^ > python lcm.py 24 36
72

追記

よくよく調べたらfractionsモジュールにgcd関数があった。

>>> import fractions
>>> fractions.gcd(24, 36)
12