Webtech Walker

Python初心者によるPythonのいいところ、はまりどころのまとめ

Python勉強し始めて一ヶ月くらいたったんで一度復習を兼ねてまとめてみようと思います。僕が今までPHPとかPerlとかJavaScriptを使っていて、Pythonはこうやるのかーとか、これは便利だなーと思ったところ、開発していてはまったところなどピックアップしてみました。

初めてのPythonを読んで初心者向け勉強会に参加した程度の知識です。とりあえず初めてのPythonがかなりいいのでこれ読むだけで大体基礎は習得できた気がします。基本的な文法の説明だけでなく、大事なことは何回も繰り返し書いてあったり、Pythonの思想などにも触れているのでなぜこういう実装になっているかということも理解できます。これオススメ。

尚、このエントリーではPythonのバージョンは2.5をベースにしてます(主にGoogleAppEngineで使ってるので)。間違えなどあったらツッコミお待ちしてます。

文法、演算子など

インデント

これはPython知らない人でも知ってると思いますけど、Pythonはブロックをインデントで制御します。{ } とか do end みたいのはありません。

if x:
    ...
else:
    ...

def x:
    ...

これは最初キモイなーと思ってたけど慣れるとこっちのほうが見やすいと思ってくるから不思議なもんです。言語レベルでインデントが強制されるので必然的にコードが統一されるのはいいなと思います。

変数展開

文字列の中で変数を展開するのはPerlとかPHPだとダブルクォートを使ったりしますが、Pythonではシングルクォートとダブルクォートは同じ扱いです。どうやって変数展開するかというと、以下のような書き方をします。

x = 'foo'
y = 'bar'
print 'hoge %s' % x         #=> hoge foo
print 'hoge %s %s' % (x, y) #=> hoge foo bar
print 'hoge %003d' % 1      #=> hoge 001

sprintfみたいな感じですね。以下のように名前を指定してdictを指定する書き方も可能で、これはかなり便利です。

print 'hoge %(x)s %(y)003d' % {'x': 'foo', 'y': 1} #=> hoge foo 001

locals()という関数を使うとローカルスコープの変数をdictで返してくれます。これを使うと以下のような書き方も可能です。

x = 'foo'
y = 1
print 'hoge %(x)s %(y)003d' % locals() #=> hoge foo 001

値の範囲

複数の比較を連結できるのが便利です。以下のようなxが1と10の間の値という比較を書くことができます。

if 1 < x < 10:
    ...

これができるのは結構嬉しい。

print

printはPHPやPerlに比べるとリストや辞書(ハッシュ)なども見やすく出力してくれるのがいいです。

print 'foo'                 #=> foo
print 'foo', 'bar', 'baz'   #=> foo bar baz
print ['foo', 'bar', 'baz'] #=> ['foo', 'bar', 'baz']
print {'foo': 'bar'}        #=> {'foo': 'bar'}

自動で改行してくれたりカンマ区切りにしたとき自動で空白を入れてくれたりと、場合によってはありがた迷惑な感じに感じるときもあります(自動改行は最後にカンマ入れると抑止可能だけどスペースが入る)。

Python3からはステートメントではなく関数になり、空白とか改行を引数で指定して制御できるので今よりはだいぶ使いやすくなりそうです。

is演算子

is演算子は同じ参照を指しているときにTrueを返します。

x = ['foo']
y = x
print x is y #=> True

x = ['foo']
y = ['foo']
x is y #=> False

上記の例は正しいのですが、文字列の比較でまれにおかしくなることがあるらしく、以下のようなことがあります。

x = 'foo bar'
y = 'foo bar'
x is y #=> False これは正しい

x = 'foo'
y = 'foo'
print x is y #=> True !?

短い文字列のときは高速化のためにオブジェクトをキャッシュすることがあるらしく、キャッシュしたオブジェクトを参照するとis演算子でTrueになるということです。

in演算子

in演算子は色々便利。

# 文字列に含まれるか
print 'f' in 'foo' #=> True

# dictのキーが存在するか(値でなはい)
print 'foo' in {'foo': 'bar'} #=> True
print 'bar' in {'foo': 'bar'} #=> False

# リストの要素に存在するか
print 'foo' in ['foo', 'bar'] #=> True

含まれないという条件のときはnot in。

print 'a' not in 'foo'             #=> True
print 'f' not in 'foo'             #=> False
print 'hoge' not in ['foo', 'bar'] #=> True

改行

基本的にPythonは改行が文の終わりになりますけど、バックスラッシュか括弧を使うと改行ができます。

print 'foo' + \
'bar' #=> 'foobar'

print ('foo' +
'bar') #=> 'foobar'

if (x
    && y):
    ...

x = [
    'foo': 'bar',
    'hoge': 'fuga',
]

三項演算子

三項演算子は他の言語とかなり違うので戸惑いました。

x = 'foo' if <条件式> else 'bar'

慣れればわかりやすそうな気もしますけど。ネストもできます。

多分岐

Pythonではswitchがないのでifでがんばります。

x = 'foo'
if x == 'foo':
    print 'x is foo'
elif x == 'bar':
    print 'x is bar'
elif x == 'baz':
    print 'x is baz'
else:
    print 'x is unknown'

#=> x is foo

分岐の中の処理が簡単なものならdictで以下の様にするなど。

x = 'bar'
print {
    'foo': 'x is foo',
    'bar': 'x is bar',
    'baz': 'x is baz',
}.get(x, 'x is unknown')

#=> x is bar

もしくは三項演算子のネストなど。

x = 'baz'
print 'x is foo' if x == 'foo' else \
      'x is bar' if x == 'bar' else \
      'x is baz' if x == 'baz' else \
      'x is unknown'

#=> x is baz

インクリメント、デクリメント

i++みたいなインクリメントがないみたいです。これで。

i += 1 # インクリメント
i -= 1 # デクリメント

リスト内包表記

map関数やfilter関数のようなリストをつくる動作を簡潔に書けるのがリスト内包表記です。

以下の例はmap関数のような挙動になります。

foo = [1, 2, 3]
print [2 * x for x in foo] #=> [2, 4, 6]

ifの条件分岐でfilter関数のような挙動も。

foo = [1, 2, 3, 4, 5, 6]
print [x for x in foo if x % 2 == 0] #=> [2, 4, 6]

ネストも可能。

foo = [1, 2, 3]
bar = [10, 20, 30]
print [x + y for x in foo for y in bar] #=> [11, 21, 31, 12, 22, 32, 13, 23, 33]

Pythonにしてはあんまり可読性がよくない気がするんですけど慣れなのかな。

forループ

PHPでいう以下のような表記はPythonではできません。

for ($i = 0; $i < 10; $i++) {
    echo $i;
}

こういう場合はrange関数やxrange関数を使います。range関数はリストをつくり、xrangeはオブジェクトをつくります。xrangeのほうが省メモリになるようです。

for i in xrange(10):
    print i #=> 0 1 2 3 4 5 6 7 8 9 (実際は改行される)

pass文

何も処理しないんだけど文法的に何か文が必要というときにpass文を使います。これはPythonならではだなと思いました。

例えば例外キャッチして何もしないとか。

try:
    ...
except:
    pass

実装途中にメソッドの定義だけ先にしておきたいときとかにも使えそうです。

ループにelse

Pythonにはforやwhileにもelseがあります。ループが終了するときにelse句が処理されます。ただしbreakでループを抜けた際は実行されないので、breakで抜けたかどうかを判定することができます。

例えば以下のようなコードを簡潔に書くことができます(これはin演算子でできますけどforを使った場合)。

# foo というリストにhogeという要素があったらfound、なかったらnot foundを出力する
found = False
for x in foo:
    if x == 'hoge':
        print 'found'
        found = True
        break
if not found:
    print 'not found'

これをforとelseを使うとこうなります。見つかった場合はbreakで抜けるのでelseが実行されません。見つからなければbreakが実行されないのでelseに書いた処理が実行されます。

for x in foo:
    if x == 'hoge':
        print 'found'
        break
else:
    print 'not found'

これは結構便利かも。

ドキュメント

help関数やpydocで出力されるドキュメントはすごく簡単に書けます。モジュールや関数の最初に文字列を指定するだけです。トリプルクォーテーションで複数行書くことも可能です。

# foo.py

'this module doc'

x = 'foo'

def foo(y):
    'foo method doc'
    return y

これをpydocやhelp関数で出力するとこんな感じになります。

Help on module foo:

NAME
    foo - this module doc

FILE
    /Users/hokamura/tmp/py/foo.py

FUNCTIONS
    foo(y)
        foo method doc

DATA
    x = 'foo'

書式が簡単だし、関数の中にドキュメント書けるのはコード読むときにわかりやすそうだなと思いました。

文字コードの指定

Pythonのプログラム内でマルチバイト文字列を扱うときは文字コードの宣言をする必要があります。utf-8にするときはファイルの先頭に以下の記述を追加します。

# -*- coding: utf-8 -*-

Perlでいう use utf8 みたいなもんです。

組み込みの関数、メソッド

dir関数

dir関数を使うと変数に定義されている属性やメソッドを調べることができます。

x = 'foo'
print dir(x) #=> ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

xは文字列オブジェクトなので文字列オブジェクトのメソッドや属性の名前が全て羅列されます。

help関数

help関数でhelpを表示できます。これはいいですねー。僕はインタラクティブシェルでよく使ってます。dir関数と違ってprintで出力する必要はありません。

help(str.join)
Help on method_descriptor:

join(...)
    S.join(sequence) -> string

    Return a string which is the concatenation of the strings in the
    sequence.  The separator between elements is S.

isinstance関数

isinstance関数で型判定ができます。第二引数はタプルで複数指定可能なのが便利げです。複数指定した場合はどれかがマッチすればTrueを返します。

print isinstance('foo', str)             #=> True
print isinstance(u'foo', str)            #=> False
print isinstance(u'foo', (str, unicode)) #=> True
print isinstance([1, 2, 3], list)        #=> True
print isinstance([1, 2, 3], tuple)       #=> False

str.join

joinメソッドが文字列オブジェクトに定義されているのは結構違和感ありました。リスト以外にも文字列なども引数にとれます。

print '/'.join(['foo', 'bar', 'baz']) #=> foo/bar/baz
print '/'.join('foo') #=> f/o/o

また、自動で型変換してくれないので、リストにint型などの要素があると例外を投げます。

print '/'.join(['foo', 1]) #=> TypeError: sequence item 1: expected string, int found

何がくるかわからないものをjoinするときはstrにキャストしといたほうがよさげです。

x = ['foo', 1]
print '/'.join(map(str, x)) #=> foo/1

dict.get

辞書の値にアクセスするのはx[’foo’]という形式で取得する方法と、x.get(’foo’)で取得する方法があります。前者はキーが存在しない場合例外を投げます。後者はキーが存在しない場合はNoneを返し、例外を投げません。getメソッドの第二引数が指定してあればその値を返します。

x = {'foo':'bar'}
print x['foo']              #=> bar
print x.get('foo')          #=> bar
print x['hoge']             #=> KeyError: 'hoge'
print x.get('hoge')         #=> None
print x.get('hoge', 'fuga') #=> fuga

dict.setdefault

キーが存在しないときだけ要素を追加するときはsetdefaultを使うと便利です。

if 'foo' not in x:
    x['foo'] = 'bar'

このようなコードは以下のように書けます。

x.setdefault('foo', 'bar')

tuple

Pythonにはタプルという型があって、ほとんどリストと同じなのですが、リストと違ってタプルは変更することが不可能になっています。

x = ('foo', 'bar') # タプルは丸括弧でつくる
print x[0]         # => foo
x[0] = 'hoge'      # => TypeError: 'tuple' object does not support item assignment

このようにタプルの値を変更しようとうすると例外が投げられます。メソッドなどもリストと同じように使うことができます。タプルは辞書のキーに指定できたりします(リストはできない)。

丸括弧はタプルをつくる以外にも用途があるので、注意が必要です。一つの要素を丸括弧で囲んでもタプルになりません。そのときはカンマを最後につけます。空のタプルは丸括弧のみで作ることができます。

x = (1)
print type(x) #=> <type 'int'>

x = (1,)
print type(x) #=> <type 'tuple'>

x = ()
print type(x) #=> <type 'tuple'>

set

set型は重複を許さないリストのようなもので、集合を表します。集合の和や差を以下のように書くことができます。

s1 = set([1, 2, 3, 4])
s2 = set([1, 3, 5])

print s1 | s2 #=> set([1, 2, 3, 4, 5]) 和
print s1 - s2 #=> set([2, 4])          差
print s1 & s2 #=> set([1, 3])          積
print s1 ^ s2 #=> set([2, 4, 5])       排他的論理和

関数

関数に属性

以下のように関数オブジェクトには属性を追加できます。

def x(): pass
x.foo = 'bar'
print x.foo #=> bar

JavaScriptっぽいですね。ちなみに文字列とかリストなどのオブジェクトには追加できません。

x = []
x.foo = 'bar' #=> AttributeError: 'list' object has no attribute 'foo'

関数引数の取り方

Pythonの引数の取り方は本当にいいなと思いました。

デフォルト値を設定しないものは必須で、デフォルト値を設定したもの省略できます。これはPHPと同じですね。

def foo(x, y = 0, z = 0):
    print x, y, z

foo(1, 2, 3)  #=> 1 2 3
foo(1, 2)     #=> 1 2 0
foo(1)        #=> 1 0 0
foo()         #=> TypeError: foo() takes at least 1 argument (0 given)

Pythonでは関数呼び出しのときにキーワードで引数を指定できるのがポイントです。これができると、何がいいかというと、まず関数呼び出しのコードの可読性がよくなります。この場合引数の順番は関係ありません。

foo(z = 3, y = 2, x = 1) #=> 1 2 3

また、yはデフォルト値を使いたいけどzに値を入れたいときに便利です。

foo(1, z = 3) #=> 1 0 3

可変長引数は、どんな引数をいくつ受け取るかわからない場合に使います。頭に*をつるとタプルで、**をつけるとキーワード指定した値を辞書で受け取ります。

def foo(*args):
    print args

foo(1, 2, 3) #=> (1, 2, 3)

def foo(**kwargs):
    print kwargs

foo(x = 1, y = 2) #=> {'y': 2, 'x': 1}

def foo(*args, **kwargs):
    print args
    print kwargs

foo(1, 2, foo = 'bar', hoge = 'fuga') #=> (1, 2) \n {'foo': 'bar', 'hoge': 'fuga'}

デフォルト値の罠

引数のデフォルト値にリストや辞書などの可変性のオブジェクトを指定して、その変数を関数内で変更した場合、2回目以降に関数を呼び出したとき、変更した値が引き継がれます。

def foo(x = []):
    x.append(1)
    print x

foo() #=> [1]
foo() #=> [1, 1]
foo() #=> [1, 1, 1]

xはデフォルトで空のリストを指定しますが、関数内でappendしているので、2回目以降呼び出したときデフォルト値が空ではなく前回呼び出したときの値になってます。

これを回避するには以下の様に一度引数の値をチェックする必要があります。うまく使えば便利そうですけど、あまり直感的でないので正直あまり好きじゃない挙動です。

def foo(x = []):
    if not x: x = []
    x.append(1)
    print x

foo() #=> [1]
foo() #=> [1]
foo() #=> [1]

classのメソッドで別インスタンスから呼び出しても同じなの注意が必要。

class Foo():
    def x(self, y = []):
        y.append(1)
        print y

foo1 = Foo()
foo1.x() #=> [1]

foo2 = Foo()
foo2.x() #=> [1, 1]

無名関数

無名関数はlambdaを使います。ボディ部分に指定した式が返り値になります。

foo = lambda x: x * 2
print foo(5) #=> 10

引数はdefと同じようにキーワード引数や可変長引数を指定可能です。

foo = lambda x = 0, y = 0: x + y
print foo(x = 10, y = 20) #=> 30

foo = lambda *args: '/'.join(args)
print foo('foo', 'bar', 'baz') #=> foo/bar/baz

しかしPerlやJavaScriptと違って、lambdaは単一の式しか書けません。ifとかforとかprintみたいなステートメントが書けませんし、複数の式も書けません。普通の関数(def)のreturnに書くところだけ書けるというイメージです。

デコレータ

デコレータを使うと関数を関数でラップするコードを簡潔に書くことができるようになります。例えばスタティックメソッドを定義するstaticmethod関数はデコレータを使わないと以下のようになります。

class Foo():
    def x():
        ...
    x = staticmethod(x)

これをデコレータを使うと以下のように書けます。

class Foo():
    @staticmethod
    def x():
        ...

こっちのほうが直感的だし可読性も上がります。これは簡単な例ですけど、自分でデコレータを定義して複雑な処理を実装することも可能です。

モジュール

モジュールのインポート

モジュールのインポートにはimportを使います。.pyを省略したファイル名を指定します。

# foo.py
bar = 'baz'
import foo
print foo.bar #=> baz

ディレクトリの区切りはドットで表します。

# path/to/foo.py
bar = 'baz'
import path.to.foo
print path.to.foo.bar #=> baz

fromを使うと以下のようにfromで指定下モジュールからimportで指定した変数をインポートできます。

from path.to.foo import bar
print bar #=> baz

これは以下と同じです。

import path.to.foo
bar = path.to.foo.bar

また、asを使うと違う名前でimportすることができます。

from path.to.foo import bar as hoge
print hoge #=> baz

__init__.py

モジュールを階層化する場合はディレクトリに__init__.pyおく必要があります。全ての階層のディレクトリに__init__.pyは必要で、例えばpath/to/foo.pyをimportするときはpath/__init__.py、/path/to/__init__.pyが必要です。__init__.pyの中身は空でもかまいません。

もし__init__.pyに処理を書いたときはimportした際に上から順に実行されます。

# path/__init__.py
print 'path init'
# path/to/__init__.py
print 'to init'
# path/to/foo.py
print 'foo'
import path.to.foo
#=> path init
#=> to init
#=> foo

__init__.pyがあればディレクトリ名でimport可能です。

# path/__init__.py
foo = 'bar'
import path
print path.foo #=> bar

モジュールのサーチパス

モジュールのサーチパスを追加するには以下のような方法があります。まず、PYTHONPATHを指定する方法ですが、これは単に環境変数を設定するだけなので簡単です。

$ export PYTHONPATH=path/to/dir

次にsys.pathに追加する方法。サーチパスはsys.pathで管理しているのでこれに追加すればよいです。

import sys
sys.path.append('path/to/dir')    # サーチパスの最後に追加
sys.pash.insert(0, 'path/to/dir') # サーチパスの先頭に追加

また*.pthというファイルがサーチパス上に置いてあればそのファイルに書いてあるパスをサーチぱすに追加します。

動的インポート

モジュールを動的にインポートしたいとき以下のように書くとエラーになります。

module_name = 'sys'
import module_name # => ImportError: No module named module_name

まあ当たり前ですね。動的インポートには__import__を使います。

module_name = 'sys'
sys = __import__(module_name)
print sys #=> <module 'sys' (built-in)>

クラス

継承

継承はclass定義のとき()の中に親クラスを指定します。最初見たときclassのインスタンスの引数かと思って一瞬混乱しました。

class Foo(Bar): pass

多重継承する場合はカンマで区切ります。簡単。

class Foo(Bar, Baz): pass

親クラスがないときはobjectを継承する

継承するクラスを指定しなかった場合とobjectを継承した場合で微妙に挙動が違うらしく、何も継承しない場合はobjectを継承することが推奨されているようです。

Python3からは継承を指定しない場合は標準でobjectを継承するようになるようです。

class C1(): pass
print dir(C1) #=> ['__doc__', '__module__']

class C2(object): pass
print dir(C2) #=> ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__']

プライベート変数

アンスコ二つを先頭につけるとプライベートチックになります。ただし参照不可能になるわけではないので正確にはプライベートではありません。

正確にはアンスコ二つをつけた属性やメソッドは_{classname}__{var}という形で参照できるようになります。

class C(object):
    __foo = 'bar'

c = C()
print c._C__foo #=> bar
print c.__foo   #=> AttributeError: 'C' object has no attribute '__foo'

PerlとかJavaScriptでもプライベートな属性はないし、プライベートメソッドをテストするときやりやすいので個人的にはこっちのほうがいいです。

staticメソッド

関数のデコレータのときにもでてきましたが、状態に依存しない静的な関数をつくるときは@staticmethodを指定します。こうすると第一引数にselfをとりません。

class C(object):
    @staticmethod
    def foo(): print 'bar'

C.foo() #=> bar

親クラスのメソッド呼び出し

親クラスのメソッドを呼び出すのはsuper関数を使います。あんまりいけてない感じもするけど。

class C1(object):
    def foo(self):
        print 'super'

class C2(C1):
    def foo(self):
        super(C2, self).foo()
        print 'child'

c = C2()
c.foo()
#=> super
#=> child

以上、Python初めて勉強してみてのまとめでした。たまにいけてないと思うところもあるけど全体的にわかりやすくてすごく言語だなあと思いました。しばらくPython使ってみます。

このエントリーをはてなブックマークに追加