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 」の続きです。

引き続き「 Pythonic 」という概念について見ていきます。今回は具体的な例を見ながら 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 なのかどうか」を問うことができるかと思います。

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

参考

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 」(アンパイソニック:パイソニックでない)といいます。
  • 理解しづらい
  • 別の言語からそのまま翻訳してきただけのような書き方がしてある

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

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

2013/02/11

Python の変数名のつけ方

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

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

また、名前の文字列としての制約として次の 2 つがあります。
  • 最初の文字を数字にすることはできない
  • 予約語/キーワードと同じ名前は使えない

「最初の文字を数字にすることはできない」というのは、たとえば、次の例を見ていただくとわかりよいかと思います。

OK:
x
abc
variable
x1
y1

NG:
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 点ほど、実際にコードを書く際に気をつけたいポイントがあります。

ひとつは、「 慣用的に推奨されている命名ルールに従う 」ということ。たとえば次のようなルールが Python コミュニティでは標準となっています。
  • 関数の名前であれば lower_case_with_underscores にする
  • クラス名は CapitalizedWords にする
このルールを守らなくてもプログラムの動作という意味では特に問題は生じませんが、読みやすさ・わかりやすさに影響します。このあたりのところについては、 PEP 8 などでも言及されているので興味のある方は深掘りしてみてください。

もうひとつ大事なことは、「 デフォルトの関数を上書きしない 」ということです。 Python の場合、デフォルトで用意されている関数名等も所詮は名前なので、コード内で他の用途に使用することができます。

たとえば、デフォルトで用意されている関数に使われている名前に「 id 」や「 type 」というものがあります。
x = 10

print type(x)  # => int
print id(x)  # => 10411020
これらに別の値を代入しても、インタプリタはそのまま通してしまいます。
type = 'abc'
id = 5
表面上は問題無いように見えますが、これをしてしまうと、そのスコープの中では以後 type() 関数 id() 関数が使えなくなってしまうので、このようなことが無いように注意が必要です。

変数名のつけ方に関する公式ドキュメントの記述はこちらにあります。そちらもあわsてご覧ください。


参考

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 アンコメント
というショートカットが割り当てられています。