PHPしか書けなかった私がPythonを始めた頃に思った12点

昨日の話ですが、とても共感出来る記事を見つけたので紹介します。

PHPしか書けなかった僕がRubyを始めたころに思ったこと10点
PHPから見たRubyが良くまとまっています。
Rubyは 書いていて楽しくなる工夫が詰まっている という一文で締めくくられているのが良いですね。

これを読んだとき、同じことを感じた自分が居ることに気づきました。
ただし、Rubyではなく Python で。

AV女優.com では、表側でPHPを、裏側でPythonを活用しています。
もちろん、私はPHPからPythonに移行した一人です。

そこで、この記事では、 PHPから見たPython をまとめます。

1. ;も{}も$もないシンタックスが簡素で、わかりやすい

これはPythonを書いたことがない人でもご存知かと思います。
一番わかりやすいPythonの強み でもあります:

for i in range(0, 10):    print "Hello world (" + i + ")"

PHPでは、何を書くにも;と{}と$が必要です:

<?phpfor ($i = 0; $i < 10; $i ++) {    print "Hello world (" . $i . ")"}

このプログラムはfor文内のコードが1行なので、{}は省略出来ます。
しかし、PHPを長く使ってきた人であれば、読みにくくなるために{}は基本的に省略しないでしょう。

{}を省略するために、Pythonでは インデント が意味を持ちます。
インデントで字下げした部分が、{}で囲われたコードブロックと同義なのです [1]

また、この項目と内容がずれてしまいますが、このコードで利用した + もわかりやすい理由の一つです。
PHPでは文字結合に . を使用しますが、Pythonでは + を使用します。
演算子の意味からしても、理解しやすく、わかりやすいコードが書くことが出来ます。

打つコード量が少なく、後から見たときにコードも理解しやすい。
これがPythonに持った印象です。

2. Pythonでは全てforで書ける

Pythonにはforとforeachの二つは存在しません。
全て、 for を使って記述します:

for i in range(0, 10):    print "for loop"items = ["like", "a", "foreach"]for item in items:    print itemfh = open("/tmp/sample.txt", "r")for line in fh:    print line

上記のPythonのコードを順に、PHPに直すとこうなります:

<?phpfor ($i = 0; $i < 10; $i ++) {    print "for loop";}$items = array("like", "a", "foreach");foreach ($items as $item) {    print $item;}$fh = fopen("/tmp/sample.txt", "r");while (($line = fgets($fh, 4096)) !== false) {    print $line;}

PHPではfor、foreach、whileを活用する必要があります。
しかし、Pythonではそのほとんどがforを使って書けます。

その状況に応じて forやforeach、whileを選ばなくて良い のは楽なものです。

3. 関数をオブジェクトとして扱える

Pythonでは関数を オブジェクト として扱えます:

def spam():    print "Spam! " * 5 + "..."clone_spam = spamclone_spam()

Pythonで関数を定義するのは、変数を定義するのと似ています。

def spam(): を使ってspamという関数を定義すると、spamという変数に関数が入る。

こう考えるとわかりやすいでしょう。
そのため、別の関数名に置き換えることが簡単です。
上記はその例です。

PHPにはこのような機能がありません。
そのため、Pythonを始めたときに、この機能には感動しました。
テストがもっと簡単に書けるんじゃないか 、と。

4. リスト内包表記で配列処理を簡素に書ける

Pythonには リスト内包表記 と呼ばれる特殊な書き方が存在します:

# 出席番号1から31までの全員にサイコロを振らせる例import randomdices = [(n, random.randint(1, 6)) for n in range(1, 32)]print dices  # [(1, 2), (2, 4), ... ]

これがPHPでは、for文を使った普通の書き方になります:

<?php// 出席番号1から31までの全員にサイコロを振らせる例$dices = array();for ($i = 1; $i <= 31; $i ++) {    $dices[] = array($i, rand(1, 6));}var_dump($dices);  # array(31) { [0] => array(2){ [0] => int(1), ... }

リスト内包表記を使いこなせる人はカッコいいですね。

また、横道に逸れますが、Pythonのprint文も便利です。
PHPのvar_dumpと同じような効果があり、オブジェクトの中身を文字列として書き出してくれます。
インスタンスの中身を知りたいときは、var_dumpのほうが便利ですが。

5. スライスを使って楽に、配列の一部分を取り出せる

Pythonでは配列の一部を スライス を使って簡単に取り出せます [2]

no = range(1, 32)dices = [(n, random.randint(1, 6)) for n in no[3:6]]print dices  # [(4, 1), (5, 4), (6, 6)]

PHPではarray_slice()でしょうか:

<?php$no = range(0, 31);$select = array_slice($no, 4, 3);$dices = array();foreach ($select as $one) {    $dices = array($one, rand(1, 6));}var_dump($dices);  # array(3) { [0] => array(2){ [0] => int(3), ... }

PHPは配列の長さを指定しなければならない分、若干わかりにくいです。
Pythonのほうが直感的です。
スライスの使いこなしもPythonistaステータスの一部ですね。

6. 全てのエラーは止まる

Pythonでは変数が定義されていない場合や、型が間違っている場合もプログラムの実行が 停止 します:

print hello_world  # NameError: name 'hello_world' is not definedjoin = '1' + 1  # TypeError: cannot concatenate 'str' and 'int' objects

しかし、PHPではWarningを出すだけで、そのまま実行してしまいます。
型に至っては非常に緩く、自動で変換します:

<?phpprint $hello_world;  # PHP Notice:  Undefined variable: hello_world<?php$add = '1' + 1;print $join  # 2$join = '1' . 1;print $join  # 11

私はこのPHPの緩さが非常に気になっていました。
PHPには、このWarningを無視する @ という演算子があるぐらいです。

変数が定義されていない場合は、潔くプログラムには止まって欲しいのです。
どうせ、PHPでもWarningは潰すのだから。

7. easy_installで簡単に拡張出来る

easy_install さえあれば、欲しいライブラリのほとんどが手に入ります:

easy_install sphinx scrapy blogofile dateutil y_serial

PHPにも同様にpearがありますが、あまり使われないのが現状だと思います。
直接、ソースコードをダウンロードして、プロジェクトのディレクトリ配下に置くことも多いでしょう。

Pythonは優れたパッケージングライブラリの存在と、その構造のおかげでライブラリを共有するのが簡単です。
そのため、検索して見つけたライブラリのほとんどは、easy_installでインストールすることが出来ます。

まさに、Easy Installです。

8. インタラクティブシェルで簡単にコードを試せる

Pythonは、別にインストールせずとも インタラクティブシェル を使うことが出来ます:

>> pythonPython 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)[GCC 4.4.3] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> name = "sunomaru">>> name'sunomaru'

PHPには、Facebookが開発したphpshがあり、これをインストールすることでインタラクティブシェルを実現出来ます:

>> phpshStarting phptype 'h' or 'help' to see instructions & featuresphp> $name = "sunomaru"php> print $namesunomaru
phpsh
Facebookが開発したライブラリです。
インストールすることで、補完機能付きのPHP用インタラクティブシェルが手に入ります。
コマンドラインベースで開発している人には必須でしょう。

Pythonのインタラクティブシェルはそのままでも十分便利ですが、 iPython というライブラリをインストールするともっと便利です。
これもeasy_installでインストール出来ます:

easy_install ipython

こちらはカラーリング、補完、ヒストリの保存まで出来ます:

>> ipythonPython 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)Type "copyright", "credits" or "license" for more information.IPython 0.10.1 -- An enhanced Interactive Python.?         -> Introduction and overwrite of IPython's features.%quickref -> Quick reference.help      -> Python's own help system.object?   -> Details about 'object'. ?object also works, ?? prints more.In [1]: name = "sunomaru"In [2]: nameOut[2]: 'sunomaru'

Pythonはそのシンタックスのおかげで、インタラクティブシェルと相性が抜群です。
;や{}、$といった余計な記号がないため、気軽にコードが試せます。
実際に、phpshよりも、ipythonの実行回数のほうが多くなりがちです。
PHPはブラウザで試す方が気軽ですから。

9. 配列のキーにオブジェクトを指定出来る

Pythonでは 配列(正確にはディクショナリ)のキー に、オブジェクトを指定出来ます:

class Spam():    passspammer = Spam()dic = {spammer: "I'm spammer!" + "Spam! " * 5}print dic[spam]

PHPで、これはエラーです:

<?phpclass Spam {}$spammer = new Spam();$array = array($spammer => "I'm spammer!");  # PHP Warning:  Illegal offset type

使いどころは考えますが、便利な機能です。

10. ファイルとディレクトリが意味を持つ

PHPと まったく異なる概念 がこれです。
ファイルとディレクトリがPythonではそれ以上の意味を持ちます:

/project|-- __init__.py|-- first_module.py|-- second_module.py`-- inner_dict    |-- inner_first_module.py    `-- inner_first_module.py

上記のディレクトリ構造の場合、外からモジュールを使うには以下のように書きます(本来は全て書く必要がありません):

import project.first_moduleimport project.second_moduleimport project.inner_dict.first_moduleimport project.inner_dict.second_module

インポート時のパスの問題は、ここで説明するには複雑なので置いておきます。
重要なのは、PHPがパスを直接指定してファイルを単体をインポートするのに対して、Pythonはディクショナリやファイルを箱として見ていることです:

# /project/first_module.pyvar = "This is my variable."# 外部スクリプトimport project.first_moduleprint project.first_module.var  # This is my variable.print var  # NameError: name 'var' is not defined

PHPでは、直接変数を参照出来ます:

<?php# /project/first_module.php$var = "This is my variable.";<?phpinclude("/project/first_module.php");print $var;  // This is my variable.

Pythonは、ファイルを作れば それ自身がモジュール になります。
このことが決まっているからこそ、安心してプログラムを組むことが出来ます。

11. 可変と不変を区別して考える

PHPを使っていて、基本的に考えないのが、この項目です。
PHPでは基本的に値をコピーされ、渡されます。
つまり、渡された先では 渡した元の変数を変更すること が出来ません。

しかし、Pythonでは常にこのことを考える必要があります:

def overwrite(d):    d[0] = 'overwrote'var_list = [1, 2, 3, 4]var_tuple = (1, 2, 3, 4)overwrite(var_list)print var_list  # ['overwrote', 2, 3, 4]overwrite(var_tuple)print var_tuple  # 'tuple' object does not support item assignment

詳しい説明は省きますが、可変なオブジェクトの値を変更してしまうと、他にも影響します。
これを防ぐために、Pythonではオブジェクトをコピーしてから値を変更します:

def non_overwrite(d):    cp = d[:]    cp[0] = 'overwrote'var_list = [1, 2, 3, 4]non_overwrite(var_list)print var_list  # [1, 2, 3, 4]

PHPでは普通に渡しただけでは、値の変更は他に影響しません:

<?phpfunction non_overwrite($array) {    $array[0] = 'overwrote';}$array = array(1, 2, 3, 4);non_overwrite($array);var_dump($array);  // array(4) { [0]=> int(1), [1]=> int(2), [2]=> int(3), ... }

参照渡しにすると、同様のことが起こります:

<?phpfunction overwrite(&$array) {    $array[0] = 'overwrote';}$array = array(1, 2, 3, 4);overwrite($array);var_dump($array);  // array(4) { [0]=> string(9) "overwrote", [1]=> int(2), ... }

PHPの感覚でPythonを使えば、変数(リスト)の中身が急に変更され、ハマります。
一刻も早く、可変と不変に慣れることです。
慣れてしまうと、この考え方が普通になります。

12. テストが標準で付いてくる

最後にテストの話をしましょう。
Pythonには、 標準ライブラリ としてunittestが付いてきます:

import unittestimport spamclass SpamTestCase(unittest.TestCase):    def setUp(self):        self.spam = spam.Spam()    def tearDown(self):        self.spam = None    def test_echo(self):        result = self.spam.echo("Spam!", 3)        expected = "Spam! Spam! Spam!"        self.assertEqual(result, expected);

また、doctestモジュールを使って、簡単なテストをコメントとして書くことも出来ます:

def echo(word, repeat):    """    Echo like a spammer.    >>> echo('Spam!', 3)    'Spam! Spam! Spam!'    """    return word * repeatimport doctestdoctest.testmod()# 実行結果"""**********************************************************************File "echo.py", line 5, in __main__.echoFailed example:    echo('Spam!', 3)Expected:    'Spam! Spam! Spam!'Got:    'Spam!Spam!Spam!'**********************************************************************1 items had failures:   1 of   1 in __main__.echo***Test Failed*** 1 failures."""

PHPであれば、テストのためにPHPUnitか、SimpleTestを入れるところでしょう。

PHPUnit
PHPのテストライブラリのでファクトスタンダードです。
CakePHP 2.xでもこのライブラリが使われています。
自動化のためのツールも豊富に揃っています。
SimpleTest
CakePHP 1.xで使用されていたライブラリです。
PHPUnitのほうがサポートするツールが多く、かなり遅れを取っている印象です。

しかし、これらは外部ライブラリであり、使い始めるには若干の手間が掛かります。
Pythonにこうしたテストツールが始めから入っているのは、とても心強いものです。
Pythonのドキュメントにもしっかりと、使い方が書いてありますしね。

参考記事

おまけです。
PHPとPythonの比較で、参考になる記事を載せておきます。

Python VS PHP
短い記事ですが、シンタックスの観点からPythonとPHPを比較しています。
PythonとPHPを比較したときに、一度は思うことです。
Python as a PHP replacement?
PythonはPHPの代わりになるか、という点に着目した比較です。
PHPに比べ、Pythonが優れているところがまとまっています。
Why is Python better than PHP?
何故、PythonはPHPよりも良いのか、という質問がQuoraに書き込まれています。
こちらは、Pythonの良さが端的にまとめられています。
PHP開発者のためのPythonの基本
PHPer向けに書かれたPython入門です。
PHPをメインに使ってきた人は、PHPと比較しながらの解説がわかりやすいでしょう。

ちょっと、 始めた頃思った というのが怪しい記事になりましたね。
しかし、勉強し始めた頃、これらの項目に着目したのは確か。
初めてのPython を読みながら、へぇ、ほほう、なるほど!とつぶやいていた記憶があります。

Rubyが 書いていて楽しくなる工夫 をしているのであれば、Pythonは すっきりと書ける工夫 をしています。
シンタックスが簡素であるのは、その一部にしか過ぎません。
使えば使うほど、Pythonの設計思想がそこにあることが見えてきます。

PHPからPythonに乗り換える人、あるいはPythonistaがPHPを使うときの参考なれば幸いです。

[1] このインデントが嫌いな人も居ることを注記しておきます。インデントを強制されるのが苦手、コードブロックを把握しにくい等、理由は様々なようです。
[2] 正確にはPHPの配列は、Pythonでリスト、あるいはディクショナリと呼ばれます。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中