2013/02/27

Python Tips:「Code Like a Pythonista」から


Pycon 2007での発表の資料として、「Code Like a Pythonista: Idiomatic Python」というものがあります。David Goodgerという人が作ったもので、いわゆる、Pythonのイディオムを紹介したもの。イディオムとは、ここでは、「よく使う小さなコード」のことを示していて、そのおすすめの書き方がまとめられています。

まずはPEP 20――「Zen of Python」の紹介から始まり、その後さまざまな具体的なアイデアがたくさん紹介されています。

今回はこの「CodeLike a Pythonista」のうち、PEP 20PEP 8に含まれていないものに限定して、さらに、個人的に勉強になったものに絞ってご紹介します。原文にはここに挙げたよりもっとたくさんの例が載っていますので、そちらもよろしければ。

Docstringとコメントの使い分け

  • Docstring HOW TO 使い方
  • コメント WHY & HOW なぜ、どのように動くのか

値の入れ替え → 一行で


b, a = a, b
×
tmp = a
a = b
b = tmp

文字列を連結して長い文字列を作る → join()で


''.join(words)
×
result = ''
for s in words:
    result += s

''.join(fn(i) for i in items)

inが使える処理 → inでシンプルに書く


if key in d:
    # do something
×
if d.has_key(key):
    # do something

辞書の初期値を入れるための処理 → get()やsetdefault()で



d = []
for (e1, e2) in data:
    d[e1] = d.get(e1, 0) + e2
×
d = []
for (e1, e2) in data:
    if e1 not in d:
        d[e1] = 0
    d[e1] += e2

lists = {}
for (e1, e2) in data:
    lists.setdefault(e1, []).append(e2)
×
lists = {}
for (e1, e2) in data:
    if e1 in lists:
        lists[e1].append(e2)
    else:
        lists[e1] = [e2]

2つのリストをいっしょに回す → zip()で


for (e1, e2) in zip(list1, list2):
    # do something

リストが空かどうかの判定 → 判定文に変数そのものを入れる


if items:
    # do something
×
if items == []:
    # do something

ソートのルールをカスタマイズする → keyで


def my_key(item):
    return (item[1], item[3])

to_sort.sort(key=my_key)

モジュールのimport → *は使わず何を使うのかを明示的に示す


import module
module.name
×
from module import *

import long_module_name as mod
mod.name

from module import name
name

モジュールとしても、実行するスクリプトとしても使うコード → 末尾で判定


....
code
....
....

if __name__ == '__main__':
    # script code

以上です。

Pythonを使い始めてから少し経ってからでも、「そんな書き方ができるんだ」と勉強になるものが結構あります。

「Code Like a Pythonista」の中でも「Don't reinvent the wheel」(車輪の再開発をするな=すでにあるものをゼロから作るな)ということが言われていますが、意識せずとも自然にPythonicなコードが書けるようになるには、こういう基礎の部分をときどき見直すのがよいのかもしれません。

2013/02/25

Pythonにまつわるアイデア:オフサイドルール

Pythonにまつわることばに、「オフサイドルール」というものがあります。

「オフサイドルール」(off-side rule)とは、プログラミング言語において字下げによってコードのブロックを表現するスタイルのこと。
for i in range(10):
    if i % 3 == 0:
        print i
Pythonではこのように、for文等を使うとき原則的に字下げをするルールとなっていますが、C言語では「{}」(中括弧)、FORTRANでは「DO~END DO」というように、ひとかたまりのブロックを示す表現は言語によってまちまちです。

ちなみに、「オフサイド」の名前の由来はサッカーのオフサイドだそうです。「ある見えないラインより前にはみ出てはいけない」といったところでしょうか。

私自身はあまり知らないのですが、Haskell、F#、CofeeScriptなどもオフサイドルールを用いた言語としてWikipediaには紹介されています。

オフサイドルール - Wikipedia
Off-side rule

2013/02/22

Pythonにまつわるアイデア:Pythonic その2

この投稿は「Pythonic その1」からの続きです。

引き続き「Pythoic」という概念について見ていきます。今回は具体的な例を見ながら、Pythonicとはどういうことなのかをご紹介します。まずはforループの書き方から。

○がpythonicなもの、☓がそうでないものです。

forループ

×
for i in range(len(alist)):
    print alist[i]

for item in alist:
    print item
カウンタを使うのは、どうしても必要なときだけ、で。

×
i = 0
for item in alist:
    print i, item
i += 1

for i, item in enumerate(alist):
    print i, item
カウンタが必要な場合には、enumerate()を使えばシンプルに書くことができます。


for key, value in adict.items():
    print key, value
辞書のkeyとvalueをあわせて取得したい場合は、items()というメソッドを使うとシンプルに書くことができます。

リスト内包表記/ジェネレータ式

×
newlist = []
for x in oldlist:
    newlist.append(x ** 2)

newlist = [x ** 2 for x in oldlist]
既存のリストから新しいリストを作るときには、forループではなく[]によるリスト内包表記を使うのが便利でシンプルです。


for ele in (x ** 2 for x in oldlist):
    print ele
要素を少しずつ計算していく「ジェネレータ」についても、リスト内包表記と同じような形で書くことができます。ちがいは[]が()になっている点のみです。

アンパック

×
def calc_sin_cos(x):
    return (sin(x), cos(x))

results = calc_sin_cos(1.57)
sinx = results[0]
cosx = results[1]

sinx, cosx = calc_sin_cos(1.57)
関数が複数の要素を返してくる場合は、受けるとき同時に別の変数に分割することが可能です。

値の交換

×
tmp = x
x = y
y = tmp

y, x = x, y
ふたつの変数の値を交換するときには、まとめて一文で。

・・・以上です。


今回取り上げたのは小さな単位での書き方の例でしたが、もっと大きな、変数やモジュールの命名やコード全体の構成といったところにも、「それはPythonicなのかunpythonicなのか」と問うことができるかと思います。

単にコードを書くのではなく、「どう書いたらPythonicなコードになるだろう?」という意識をもって書くことが、よいpythonistaになる道なのではないかと思います。


参考
How to Write "Pythonic" Code
Be Pythonic

2013/02/15

Pythonにまつわるアイデア:Pythonic その1

Pythonにまつわる概念に「Pythonic」(パイソニック)というものがあります。

Pythonの勉強をしていて最もよく見かけるキーワードのひとつではないでしょうか。今回はその「Pythonic」ということについて。

以下、まずは定義をあげ、次に具体例をあげて見ていきます。

定義

「Pythonic」とは、ひとことでいうと「Pythonらしい、シンプルで読みやすいコードの書き方」のこと。

厳密な定義はないようですが、Pythonを長く使っている人たちの間ではほぼ共通の認識があるようです。英語のWikipediaでは、次のように説明されています。
A common neologism in the Python community is pythonic, which can have a wide range of meanings related to program style.
(意訳)
Pythonを使う人たちの間で共有されている造語に「pythonic」というものがあります。「Pythonic」はプログラミングスタイルに関して幅広い意味を含むことばです。
To say that code is pythonic is to say that it uses Python idioms well, that it is natural or shows fluency in the language, that it conforms with Python's minimalist philosophy and emphasis on readability.
(意訳)
あるコードが「pythonicだ」というとき、それは次のようなことを意味します。
  • Pythonのイディオムがうまく使われている
  • Pythonコードの書き方として自然で流暢な感じである
  • Pythonのミニマリスト的哲学とマッチしていて、読みやすさが重視されている
In contrast, code that is difficult to understand or reads like a rough transcription from another programming language is called unpythonic.
(意訳)
逆に、次のようなコードについては、「unpythonic」(アンパイソニック:パイソニックでない)といいます。
  • 理解しづらい
  • 別の言語からそのまま翻訳してきただけのような書き方がしてある

おおまかな意味としては、このあたりで押さえられるかと思います。

つづいて具体例を見ていきます。具体例については、投稿を改めて。

Pythonic その2

2013/02/11

Pythonの変数名のつけ方

Pythonの変数名のつけ方に関するルールについてご紹介します。

Pythonの変数の名前(identifier/name)には
  1. a~zのアルファベット
  2. 数字
  3. _(アンダースコア)
の3種類の文字を使うことができます。文字列の長さに関する制限はないため、100字、200字といった極端に長い名前をつけることも可能です(実用性の面で長い名前にすることはないかとは思いますが)。

また、文字が集まった文字列になったときの制約として次の2つがあります。
  • 最初の文字は数字以外
  • 予約語/キーワードと同じ名前はダメ

「最初の文字は数字以外」というのは、たとえば、次の例を見ていただくとわかりよいかと思います。


x
abc
variable
x1
y1
×
1x
2x
123x

「予約語/キーワードと同じ名前はダメ」というのは、「if」や「for」といった、Pythonの文法上特殊な意味をあらかじめ与えられている単語は使うことができない、という意味です。「if」「for」以外にも、Pythonには
  • and
  • as
  • assert
  • break
  • class
  • continue
  • def
  • del
  • elif
  • else
  • except
  • exec
  • finally
  • for
  • from
  • global
  • if
  • import
  • in
  • is
  • lambda
  • not
  • or
  • pass
  • print
  • raise
  • return
  • try
  • while
  • with
  • yield
などの単語が特殊な意味を持ったもの(いわゆる「予約語」「キーワード」)として登録されているため、これらの単語は使わないようにしましょう。このあたりのことについて詳しくは公式ドキュメントの予約語/キーワードの欄をごらんください。

また、
  • _*: アンダースコアひとつで始まる名前
  • __*__: アンダースコアふたつで始まりアンダースコアふたつで終わる名前
  • __*: アンダースコアふたつで始まる名前
などについても、特殊な意味が与えられているので注意が必要です。_(アンダースコア)から始まる名前を不用意に使うことのないようにしましょう。このあたりのことに関する公式ドキュメントの記述箇所はこちらです。

制限としては以上となりますが、あと2点ほど、実際にコードを書く際に気にとめておきたいポイントがあります。

ひとつは、「慣用的に推奨されている名前づけのルールに従う」ということ。たとえば
  • 関数の名前であればlower_case_with_underscoresにする
  • クラス名はCapitalizedWordsにする
といったルールがスタンダードとなっています。このルールを守らなくてもプログラムが走る上では問題はありませんが、読みやすさ、わかりやすさといった意味で規則を設けておくことは効果的です。このあたりのことについては、PEP 8などでも言及されています。

もうひとつは、「デフォルトの関数を上書きしないように気をつける」ということです。Pythonの場合、デフォルトで関数名に使われている名前なんかも、所詮は名前なので、コード内でほかのオブジェクトに割り当ててしまうことが可能です。

たとえば、もとから用意されている関数名として「id」や「type」というものがあって
x = 10

print type(x)  # int
print id(x)  # 10411020
といった形で名前の型やidを確認するために使える便利な関数なのですが、その名前を
type = 'abc'
id = 5
などとしてもインタプリタはそのまま通してしまいます。いったんこう紐付けしてしまうと、そのスコープ内では「type(x)」、「id(x)」というものが使えなくなってしまうので、そのあたりも注意が必要です。

変数名のつけ方に関する公式ドキュメントのパートはこちらです。よろしければこちらもごらんください。


参考
識別子と名前 - Python公式ドキュメント

2013/02/09

Python で if 文に変数を入れたときの結果

if 文に変数を渡したときの挙動というのは言語ごとにまちまちでなかなか間違えやすいところです。

今回は Python で if 文に変数を渡したときにどういう評価になるのかというところをデータ型別に見てみたいと思います。

以下、次の組み込み型について順番に見ていきます。

  • 数値型
  • 文字列型
  • リスト・タプル・辞書・集合型
  • 論理値型
  • None


数値型

数値型の場合は、0ならFalseで、それ以外ならすべてTrue、となります。

a = 1
b = 0

if a:
    print 'True'  # True

if b:
    print 'True'  # 何も表示されない

ここに挙げたint型だけでなく、float型や虚数型の場合なんかでも「0ならFalse」というのは共通しています。

文字列型


文字列型の場合は、空文字列ならFalse、空文字以外ならすべてTrue、となります。

a = 'Hello'
b = ''

if a:
    print 'True'  # True

if b:
    print 'True'  # 何も表示されない

リスト・タプル・辞書・集合型


いずれも、空(空集合)ならFalse、それ以外ならTrue、となります。リストの場合などには、要素がすべて0でもTrueとなります。

a = [1, 2, 3]
b = []

if a:
    print 'True'  # True

if b:
    print 'True'  # 何も表示されない

a = {'car': 1, 'house': 2}
b = {}

if a:
    print 'True'  # True

if b:
    print 'True'  # 何も表示されない

論理値型


論理値型はもちろんそのままで、TrueならTrue、FalseならFalseとなります。

None


Noneの場合はFalseとなります。

a = None

if a:
    print 'True'  # 何も表示されない

強いてNoneかFalseかどちらなのかを区別したい場合は「is」を使って

if a is None:
    print '%s is None' % a  # None is None

とするとよいようです。

この「 a is None 」というのは「 a == None 」としても同じように動きますが、どちらかといえば「 is None 」の方がよいようです。理由は、1)クラスの定義によっては「 == None 」ではつかまえられないレアケースがあるから、2)「 is None 」の方が実行速度が速いから、の2点だそうです。そのあたりについて書かれたブログ記事がありますので、詳細がきになる方はそちらをご覧になってみてください。

2013/02/06

Pythonでのコメント

Pythonでのコメントについてご紹介します。

Pythonでのコメントは「#」(シャープ)から始めます。
# コメント

# words1をひとつながりのテキストにする
text = ' '.join(words1)

words2 = text.split(' ')  # テキストをスペースで分割する
Python標準のエディタでは
  • Alt+3 コメント化
  • Alt+4 アンコメント
というショートカットが割り当てられています。