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

このくらいかな。

favicon を設置してみた

参考にしたページ:
cf. ワードプレスで簡単にファビコンを設定する方法

Windows のペイントで適当に画像を作って、favikonというサイトで適当にトリミングしてfavicon.icoに変換。これだけでfavikon.icoファイルができた。

WordPressに設定するには、All in one Favicon というプラグインを使った。
プラグインをインストールして有効化したら、Settingの中から、ICO Frontend と ICO Backend それぞれににつくったファビコンをアップロードして、変更を保存ボタンを押すだけ。
Frontend がブログページの、Backend が管理ページのファビコンになるみたい。

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]

RubyのArray#*

Ruby の Array#* がこんな挙動をするとは知らなかったよ。いつからだろう。

cf. Rubyにおける算術演算の基礎 – hp12c

試してみる。

irb(main):001:0> [1,2,3,4,5] * ","
=> "1,2,3,4,5"

パラメータが文字列の場合は、それをセパレータとして連結するってこと。Array#join と同じだってマニュアルにも書いてある。

cf. http://doc.ruby-lang.org/ja/1.9.3/class/Array.html

上記のマニュアルには、「(パラメータが)文字列以外のオブジェクトを指定した場合は to_str メソッドによる暗黙の型変換を試みます。」とも書いてある。

ところで Array#* のパラメータに整数を指定すると、配列を指定した整数回繰り返した配列を返す(これは当然知ってた)。で、マニュアルのその項には「(パラメータには)繰り返したい回数を整数で指定します。整数以外のオブジェクトを指定した場合は to_int メソッドによる暗黙の型変換を試みます。」って書いてある。

じゃあ、to_s と to_int の両方を持ったオブジェクトをパラメータにしたらどうなるんだろう。

irb(main):002:0> class Foo
irb(main):003:1>   def to_s
irb(main):004:2>     "3"
irb(main):005:2>   end
irb(main):006:1>   def to_int
irb(main):007:2>     3
irb(main):008:2>   end
irb(main):009:1> end
=> nil
irb(main):010:0> foo = Foo.new
=> 3
irb(main):011:0> [1,2,3,4,5] * foo
=> [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

どうやら、to_int のほうが優先されるみたいだ。

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