ホーム > Python

Python アーカイブ

Pythonista見習いから、少しだけステップアップするための9クエスチョン from Quora

Python question

Pythonistaとしてステップアップするには何が必要か
それは、

Pythonの弱みと強みを知ること

でしょう。

これは他言語にも言えます。

Pythonを含め、プログラミング言語は道具です
道具を使うためには、その道具の最適な使い道を知ってる必要があります。

ただ、このような情報は、なかなか手に入りません。
ブログの記事は、書いた人の好みのバイアスが掛かっているため、鵜呑みに出来ません

道具を使いこなすには、自分自身で長時間、その道具に触れること
そして、他の人がどのように使っているのか、知ることが必要です。

そうして、その道具の強みと弱みを見出していきます。

前者は、あなたがひたすらに、時間を費やすしかありません。
しかし、後者に関しては良い方法があります

それが、Quoraです。

今日は、QuoraからPythonに関する質問をピックアップしていきます。

Pythonの弱みは、何か?

まずは、Pythonの弱みに関する質問です。
Pythonを使っていて微妙に思う点“がまとまっています。

スコープに関する話、インデントに関する話、lambda式に関する話…。
一度見ておくと、Pythonの弱みが見えてきます。

なぜ、PythonはPHPより良いのか?

1-byte.jpで取り上げる、もう一つの言語といえばPHPです。
つまり、この質問は外せません。

PHPとPythonの比較“がまとまっています。

PHPとPythonを両方使ったことがある人は、ここに書いてある内容に納得してしまいます。
確かに、Pythonではこんなことが出来て、PHPでは出来ないな…と。

どのプログラミング言語を始めるべきか、悩んでいる人も読んでおくと良いかと。

PHPにない、Pythonの便利な機能は?

こちらは、”PHPとPythonにおける機能面の比較“です。
先程の記事とは違い、コードベースでPHPとPythonが比較されています。

Pythonにはない、Rubyの良さは?

この質問を読むと、RubyとPythonがよく似た言語であることがわかります。
その中でも、”Pythonにはない、Rubyの使いやすい機能“がまとまっています。

これも、どの言語を始めるべきか、悩んでいる人にお勧めです。

ただし、Pythonistaがこれを読んでいると、Rubyも触りたくなるので、注意が必要です。

比較演算子を2回書かずに、値をn以上m以下で比較できる言語は?

私は、この観点で言語を比較したことがありませんでした。
確かに、

if (1 < $a and $a < 10) {
	print 'True'
}

と書くのは野暮ったいですよね。

Pythonのデコレータの主な使い道は?

デコレータを理解すれば、Pythonistaとしての幅が広がるはずです。
簡単な例と共に、デコレータの使い方が紹介されています。

まだ、デコレータなんて使ったことがない、という人にもお勧めです。

皆が嫌いな言語は?

ブログでも良く、この手の話が議論されますね。
俺はこの言語が嫌いだ。俺はPHPが嫌いだ。PHPなんて…(ry

筆頭に上がるのはPHPでしょうが、それ以外の言語もたくさん解答されています。
言語の強みと弱みを知るために、読んでおくと良いかもしれません。

どんな企業がPythonを使ってる?

この質問は、Quoraの真骨頂です。
Quora上には、有名な企業に務めるエンジニアがたくさんいます
そのため、こういった質問には多くの答えが返ってきます。

どんな企業が、Pythonを活用しているのか。
そして、自分たちがどの言語を使うべきなのか、見定めるのに参考になります。

なぜ、QuoraはPythonを選んだのか?

最後はQuoraに関する質問で閉めましょう。
Quoraは冒頭で述べたとおり、Pythonで出来ています。

この質問では、Quora創設者のAdamがPythonと他言語と比較しながら、Pythonの優位性を語っています。
Pythonの強みを知ることが出来ます。

いかがでしょうか。

ここに上げた質問はいずれも、ブログの記事として取り上げられません。
また、あったとしても、それはその人の意見です
一度に、これほどたくさんの意見を得ることは出来ません

また、QuoraがPythonで作られているせいなのか、たくさんのPythonistaがいます
新たに質問を作っても、すぐに答えてくれます。

これは私が試しに投げた質問ですが、1時間で4つの解答が得られました

ぜひ、Quoraを活用してください。
そして、一緒に、Pythonistaとしてステップアップしていきましょう。

おまけ

Pythonに限らず、こんな質問も上がってたり。

A Hard Days Nightのイントロのコードって、ビートルズの謎の一つなんですよね。

gmailを、Pythonスクリプトでメールサーバとして使うtips

Gmail

久々の更新です。
現在、ブログのリニューアルのために、時間を割いています。
しかし、肝心の記事が書けていないっていうのはダメですね。
読者があってこそのブログです。

そんなこんなで、少し時間がありません。
今日はちょっとだけ(本当に少しだけ)、使えるtipsを紹介します。

gmailを、Pythonスクリプトを使ってメールサーバとして使ってみます。

5分で出来るインストールと送信テスト

インストールと初めの送信はとても簡単です。
Ubuntu 10.04 TLSでインストールする手順をまとめます。
インストール先は、”/usr/lib/gmail”とします。
必要に応じて、ルート権限で実行してください。

apt-get install -y libyaml-0-2 python-setuptools # easy_installとYAMLライブラリのインストール
easy_install PyYAML # PyYAMLのインストール
cd /usr/lib
git clone git://github.com/tfmagician/gmail.git # ソースのダウンロード
cd gmail/
cp config.yaml.example config.yaml
vi config.yaml # gmailアカウントの設定
ln -s /usr/lib/gmail/gmail.py /usr/bin/gmail

ここまで無事に実行できたら、以下のコマンドを実行して送信テストします。

gmail --subject 'Hello world.' --text --body 'Read me.' --config ./config.yaml [あなたのメールアドレス]

コマンドラインの応答には、1〜2秒掛かります。
応答が返ってきたら、[あなたのメールアドレス]で設定したメールの受信ボックスを確認してください。

  • 送信元 : config.yamlで設定したメールアドレス
  • タイトル: Hello world.
  • 本文: Read me.

このようなメールが受信出来れば、成功です。

何に使うか

正直なところ、”簡単な用途“にしか使えません。
例えば、以下の用途です。

  • ログの定期確認
  • logwatchのレポート確認
  • 自作スクリプトの動作確認

監視サーバのアラートなど、対応に緊急を要する用途には使えないと思います。
それは、gmailスクリプトがこんなに簡単なコードだからです。

#!/usr/bin/python
# -*- coding: utf-8 -*-

# import
import sys
import os
import smtplib
import yaml
import optparse
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate

# globals
VERSION = '0.10'

def read_config(config_file):
    """
    Read configuration YAML file.

    account and password keys are required in the file.
    """

    config = yaml.load(open(config_file).read())
    not_exist = [setting for setting in ('account', 'password') if not config.has_key(setting)]
    if not_exist:
      print "Could not read %s setting from configration file." % ", ".not_exist
      sys.exist(1)
    return config

def parse_options():
    """
    Parse options from arguments.
    """

    usage = "usage: %prog [-s subject] [-b body] to-addr..."
    ver = "%s %s" % ("%prog", VERSION)
    parser = optparse.OptionParser(usage, version=ver)
    parser.add_option("-s", "--subject", dest='subject',
                      default="",
                      metavar="STRING", help="Specify subject on command line")
    parser.add_option("-b", "--body", dest='body',
                      default="",
                      metavar="INPUT_FILE", help="Specify email body read from file")
    parser.add_option("-t", "--text", dest='text',
                      default=False,
                      action="store_true", help="--body argument as text not file")
    parser.add_option("-c", "--config", dest='config',
                      default="config.yaml",
                      metavar="INPUT_FILE", help="Setting file to use gmail server")
    opts, args = parser.parse_args()
    if args:
      files = ['config']
      if not opts.text: files.append('body')
      for file in files:
        if opts.__dict__[file] and not os.access(opts.__dict__[file], os.R_OK):
          print "Could not read %s from %s." % (file, opts.__dict__[file])
          sys.exit(1)
      return opts, args
    else:
      parser.print_help()
      sys.exit(0)

def create_message(from_addr, to_addrs, subject='', body='', encoding='utf-8'):
    """
    Create a mail body to send.
    """

    msg = MIMEText(body, 'plain', encoding)
    msg['Subject'] = Header(subject, encoding)
    msg['From'] = from_addr
    msg['To'] = ",".join(to_addrs)
    msg['Date'] = formatdate()
    return msg

def send_via_gmail(account, passwd, to_addrs, msg):
    """
    Send an email through SMTP server of Gmail.
    """

    s = smtplib.SMTP('smtp.gmail.com', 587)
    s.ehlo()
    s.starttls()
    s.ehlo()
    s.login(account, passwd)
    s.sendmail(account, to_addrs, msg.as_string())
    s.close()

def main():
    """
    Main function.
    """

    opts, to_addrs = parse_options()
    settings = read_config(opts.config)
    if opts.body and not opts.text:
      opts.body = open(opts.body).read()
    msg = create_message(settings['account'], to_addrs, opts.subject, opts.body)
    send_via_gmail(settings['account'], settings['password'], to_addrs, msg)

if __name__ == '__main__':
    main()

エラー処理をしていません。
gmailが落ちていたり、ネットワークが落ちていても、再送しません
失敗したらそれっきりです

それでも、5分でインストール出来て、メールを送信できるのは魅力です
サーバからのメール送信用にMTAを自前で用意しても良いのですが、色々と設定が面倒です。
例えば、Postfixを使って、gmail経由でメールを送る例です。

色々と用意されているUbuntuでも、これだけ手間が掛かります
メールサーバの設定って面倒なんですよね。

用途によって使い分けると良いでしょう。

注意事項

gmailのSMTPサーバには、制限があります

  • 送信回数: 500回/日
  • 宛先数: 500宛先/メール
  • メール数: 2000メール/日

下記の記事が詳しく、参考になります

  • http://d.hatena.ne.jp/dacs/20080627/1214569620

このgmailスクリプトで関係あるのは、500回/日の送信回数制限ですね。
ただ、500回/日であれば問題ないでしょう。
この制限に掛かるようであれば、あなたは確実に用途を間違っています:(

Pythonスクリプトを書くための参考URL

ここまで、このスクリプトを自分で書いたように振舞ってきたのですが、実は以下の記事を参考にしています。

Pythonでメールを送信したい人のためのサンプル集“の記事を元に、”ログ解析ツールを作る“で紹介されたoptparseモジュールを使ってオプションを加え、YAMLファイルで設定を変えられるようにしました。
あとは、それをgithubに載せて、どこのサーバでも簡単に使えるように。

Pythonで簡単な管理スクリプトを書いて、gitリポジトリを作る。
そして、それをgithubに載せて、管理する。
こうしておけば、いつでもどこでも、設定不要でスクリプトが再利用できます
これがモダンな自作スクリプトの管理法でしょうか。

これもPythonの勉強がてらに書いたスクリプトですが、地味に使えます。
皆さんも使ってみてください。

Pythonの開発環境をperlbrewのように切り替えるpythonbrew

pythonbrew.png

Pythonの勉強をしていて、複数のバージョンで動作をテストしたいことがあったのでメモ。
Pythonのバージョンを簡単に切り替えることができる”pythonbrew“を紹介します。

pythonbrewとは

pythonbrewとはUTAHTAさんが開発したツールです。

上記の記事を読んでもらえれば、pythonbrewとは何者かがわかります。
簡単に言えば、Pythonのバージョンを簡単に切り替えることが出来るツール。

pythonbrew switch Python-2.6.6
pythonbrew switch Python-2.5.5

切り替えはこのコマンドを打つだけです。
簡単ですよね!

素敵なツールをありがとうございます。

インストールする

インストールは簡単です。
ビルドの時間を除けば、5分と掛からずにインストール完了です

curl -LO http://github.com/utahta/pythonbrew/raw/master/pythonbrew # pythonbrewをダウンロード
chmod +x pythonbrew # pythonbrewに実行権限を与える
./pythonbrew install # pythonbrewをインストール
~/python/pythonbrew/bin/pythonbrew init # pythonbrewを初期化
echo "source $HOME/python/pythonbrew/etc/bashrc" >> ~/.bashrc # pythonbrewの読み込みを.bashrcに設定
source ~/.bashrc # .bashrcを再読み込み
pythonbrew install Python-2.5.5 # Python-2.5.5のインストール
pythonbrew install Python-2.6.6 # Python-2.6.6のインストール

詳しくは先ほどの記事を見て頂くとして、これだけでPython-2.5.5とPython-2.6.6の環境が手に入ります
あとは冒頭に書いた”pythonbrew switch“で切り替えるだけ。

こんなに簡単で良いんですかね…。

モジュールをインストールする

pythonbrewでは自動でeasy_installもインストールしてくれます
つまり、PyPIにある外部モジュールも簡単にインストール出来るってこと。

pythonbrew switch Python-2.6.6
easy_install PyYAML

これでPython-2.6.6の環境にYAMLのモジュールが入りました。
簡単過ぎてブログに書くこともありませんね。

スクリプトを組む

これも当たり前ですが、スクリプトでpythonbrewでインストールしたバージョンを使うときは、シェバンを”#!/usr/bin/env python“にします。
#!/usr/bin/python“だとシステムにインストールされたPythonを使ってしまうので。

#!/usr/bin/env python
import sys
print sys.version
# スクリプトが使っているPythonのバージョンが表示される。

ありがたく使わせてもらおうと思います。

ちなみに、Pythonはバージョンによって”正しい書き方“が変わる言語です。
入門書には”このバージョンではこうだった、けどこのバージョンではこうだ。“と書かれていることがよくあります。
Pythonを勉強し始める前にこのpythonbrewを入れると良いかもしれませんね。
ターミナルでのPythonの操作性とこの切り替えのしやすさを組み合わせたら、簡単にコードをテスト出来る

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

このpythonbrewを使って、是非、楽しいPythonライフを送ってくださいな。

“初めてのPython”を、1週間で攻略する6つのポイント

calender_python.jpg

以前に”Pythonの勉強をまったく始められないあなたと私に送る8つのタスク“という記事を書きました。

この記事を覚えていますか?
皆さん、この通りにタスクを消化出来ているでしょうか

自分で目標を立てておいて申し訳ないのですが、自分はステップ1で早速遅れが生じました
初めてのPython“はなかなか手強く、このタスクを消化するのに2週間を使ってしまいました。

そもそも、この”初めてのPython“に挫折した人も多いはず。
前回の記事についたはてブのコメントを見てそれを感じました。
総ページ数が768、付録を抜いても700ページはあります。

これを2週間で読了するには”作戦”が必要です。

私自身、”初めてのPython“には一度挫折しています。
あの記事を書いたのが2回目の挑戦であり、3度目の正直、ではなく2度目の正直でした。

その経験と、個人的に知っている読書スキルを踏まえて、”1週間で初めてのPythonを攻略する6つのポイント“をまとめてみます。
皆さんは1週間で”初めてのPython“を読めるように。

1. 計画を立てる

まず、計画を立てましょう。
計画とは期限と、その期限を守るためにタスクを分割すること。

実は”初めてのPython“は1週間で読むのに、ちょうど良く構成されています

  1. Pythonの基礎知識
  2. ビルドインオブジェクト
  3. ステートメント
  4. 関数
  5. モジュール
  6. クラスとオブジェクト指向プログラミング
  7. 例外

7部構成になっているんです。
つまり、単純に1日1部読めば良いんです

それぞれボリュームがあります
しかし、それは残り4つのポイントを踏まえれば読める量だと思います。

2. 読まない

速く読む方法、それは”読まない“ことです。
読まなければ、”読む時間“は掛かりませんからね。

とは言え、読まなければ知識は得られません。
そこで、以下の二つを念頭に置きながら読んでいきます

  1. 2回読んで理解出来ない部分は読み飛ばす
  2. コードを中心に理解する

これを実践するだけで読むのは格段に速くなるはず。

2回読んで理解出来ない部分は読み飛ばす

理解出来ない点を読み飛ばすのは不安ですが、2回読んで理解出来ないところは”自分の実力が足りない“か、”理解するのに情報が足りないか“のいずれか。

自分の実力が足りないのは、もうどうしようもありません。
諦めましょう(笑)
そのうち理解出来るようになります。

理解するのに情報が足りないのは、その先を読めば理解出来ます。
本において情報はいくつかの文や段落で成り立ちます。
作者はいくつかの文や段落を通して、1つのことを伝えようとしています。
つまり、理解出来ない部分で悩むのは無駄
どんどん先に進みましょう。
そのうち、理解出来ますよ。きっと。

コードを中心に理解する

Pythonはある意味、”WYSIWYG“な言語です。
つまり、長々と説明されるよりも、コードを見た方が早いことが多いです。
ざっと説明を読み、腹落ちさせるのはコード。
初めてのPython“には分かりやすいコードがたくさん載っています。
 
私自身、こういった読み方で”初めてのPython“を攻略しました。

3. メモをとる

これだけの分量の本です。
ものすごく記憶力の良い方であれば良いのですが、私を含めた凡人には読みながら記憶することは出来ないでしょう
だからこそ、メモを取ります
ポイントを書き出すのではなく、各部毎にまとめたメモを作ります。

私はマインドマップで1部ずつまとめていきました。
雑ですが、これで十分かと。

learning_python_mindmap.png

とにかく時間との勝負なので、ざっと必要なところをまとめます。
あとで見直すので、ページ数もメモすると良いと思います。
私はページ数のメモを忘れて、対象のページを探すのに時間が掛かりました…。
付箋を使うのもありですね!

4. Pythonの中心を把握する

学生時代、教官になんて言われたか覚えていますか?
特に中学かな。

必ず、授業の予習/復習してこいよ。

大人になると、これを忘れがちです。
“Pythonの中心を把握する”というのは、”Pythonを予習する“ってことです。

Pythonの中心、それは

クラスはもちろん、関数やモジュールが全てオブジェクトである。

ということです。
初めてのPython“ではこの話が中心となり、Pythonの説明が書かれています。
これを念頭に置くだけで理解が早まるはずです。

あら、もう予習出来てしまいましたね!

5. 手早く予習する

各部を読んでいくのも、必ず予習します
全体像をつかんでから、読み進めます。
こうすると、自分が必要としているところだけを読むことが出来ます。

予習に使うのは目次1-byte.jpです。
(ブログの宣伝ではないですよ:D)

まず、各部を読む前に”目次“に目を通し、どんなことが書かれているか把握します。
ほう、なるほど。こんなことが書かれているのか。“と。

それが終わったら、1-byte.jpで各部をまとめた記事があるので、それに目を通します。

これだけで十分な気がしてくる?
これは個人的な勉強メモに過ぎません。
それぞれ必要とする情報は違うはず。
あくまで”どんなことが書かれているか”の把握に使ってください。

そして、本当に本当に、本当にあなたが必要な情報を”初めてのPython“から抜き取ってください。

6. 実践する

時間があれば、理解を深めるためにコードを書いてください

部の最後に”まとめの問題集“があるのですが、これは読み飛ばしてOKです。
それよりも、自分でまとめた内容を自分で実践してみてください。
そうすると、理解が間違っていた点が浮き彫りになります。

学習は”トライ & エラー“だと思います。

“実践する”ということはまさにそれで、理解を深めます。
私はブログにまとめる“という方法で、これを実践しました。
ブログにまとめるために、かなり間違ったコードを書きました。

ただ、ここまでやると時間が足りなさそうな、そんな気がします。
ここは、別ステップに分けても良いかもしれませんね

どうですか?
1週間で”初めてのPython“が読める気がしてきましたか?

あれだけ厚い本です。
頭から読もうとしないでください
自分が必要なところだけ読んでしまえば良いんです。
頭から読もうとすると、読む前に心が折れてしまいます

Pythonの勉強をまったく始められないあなたと私に送る8つのタスク“の記事はたくさんの方に読んでもらい、とても嬉しかったです。
あれだけのはてブがついたのも初めてでした。
(実はあの記事のせいで、ブログの更新がちょっと止まってしまったのですが…それは別の話)

タスクとその期限を決めておいて、放置するのも無責任だと思ったので、”初めてのPython“の総括がてら、こんな記事を書いてみました。

Pythonはすごく良い言語だと思います。
一人でも勉強する人が増えれば個人的には嬉しいです。
一緒に勉強出来ますしね。

引き続き、Pythonista目指して頑張りましょう!

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

初めてのPython(7) 例外に関する簡単なまとめ

先週はPHP Matsuriの準備と後片付けでPythonを勉強する時間が取れませんでした…。
かなりの時間を使ったと思うのですが、その分楽しめたと思います。
どんなイベント?“と少しでも興味があるかたはこちらを。

PHP Matsuriのまとめも書いていきたいのですが、その前に勉強したPythonの例外をまとめておきます。
忘れないうちにやっておかないと、綺麗さっぱり頭の中から消え去ってしまうので…。

ちょっと期間が空いてしまいましたが、Pythonの例外についてまとめます。

初めてのPython VII部

初めてのPythonのVII部には”例外“がまとめられています。
構文が中心に、少しだけその設計に関することも書かれています。

例外処理というのは奥が深いものです。
そもそも、簡単なWebアプリケーションを作っているだけでは、例外に触れる機会がありません
PHPもPythonも、簡単なアプリケーションでは設計者が気にしなくても、”良きに計らって“くれますからね。

それだけに、”エラーが起こりえる“プログラムを書くときにどう書けば良いかわからなくなってしまうんです。
簡単なWebアプリケーションから、より深い部分に触れるプログラムを書き始めたときにエラー処理さえ書かないこともしばしば(それは自分のことです :D)。

そういった”エラーが起こりえる状況への対処“を認識する上で、このVII部は重要だと思います。
どんなときに例外を使うのか、どんなときにtry,else,finalyの構文を使うのか。
初めてのPython“のVII部を読めば、このイメージをつかめます。

プログラマーとしてのレベルを上げるためにも読んでおきたいところです。

ここでは簡単な構文と、その動作についてまとめます。
例外の設計を語るほど、プログラマーとしてのレベルは高くないので…。

例外の種類

Pythonには2種類の例外があります。

  • 文字列例外
  • クラス例外

これらには、読んで字の通り、例外の内容が”文字列“か、”クラス“かの違いがあります。
コード数は”文字列例外“のほうが少なくなります。
しかし、色々と使い勝手が良いのは”クラス例外“です。

# 文字列例外
raise "This is Exception string."

# クラス例外
class EggException(BaseException): pass
raise EggException()

クラス例外は”クラス“を別に定義する必要がある分、コードが長くなります
文字列例外は”文字列“を例外として渡すだけなので、コードが短くなります

しかし、例外を捕まえ、事後処理をする場合の話は別です。
この場合、この二つの例外でコードの長さはほとんど変わりません
結局、同じロジックを書くことになるためです。
問題はどこにそのロジックを書くか。

# 文字列例外
stringException = "This is Exception"
try:
	raise stringException
except e:
	print "Got Exception."

# クラス例外
class ClassException(BaseException):
	def printException(self):
		print "Got Exception."

try:
	raise ClassException()
except ClassException, E:
	E.printException()

文字列例外では例外を捕まえる度にロジックを書きます
クラス例外ではクラスの1メソッドとして、ロジックを書いておき、そのメソッドを呼び出します
演算子のオーバーロードをうまく使えば、メソッドを呼び出す必要もありません。

ここで違いが出るのは、コードの見やすさですね。
クラスに例外処理を全てまとめることができ、見やすいコードが出来上がります。

その他には”関連する(継承した)クラスを捕まえることが出来る”こともクラス例外の特徴として上げられます。

# 親クラスの定義
class ParentClassException(): pass
# サブクラスの定義
class SubClassException(ParentClassException): pass

# 親クラスを指定して、サブクラスの例外を捕まえることが出来る。
try:
	raise SubClassException()
except ParentClassException:
	print "Got Exception."

そして、ここまで説明しておいて申し訳ないのですが、文字列例外は2.6以降のバージョンでは廃止されています
今まで見てきた通り、クラス例外のほうが使いやすいためです。
文字列例外は”簡単に書ける“こと以外にメリットがありませんから。
Pythonでは構文を統一させるために、こういった仕様変更がよく起こるようですね。

クラス例外では、これらの特性を利用して、色々な例外処理を設計出来そうです。
例外の設計を別に勉強したいですね。
アーキテクチャ系の本を読めば良いのかな?

例外を捕まえる

先ほどから簡単な例外を捕まえる例を書いています。
しかし、Pythonには他にも例外を取り扱う構文があります。

  • raise
    • 例外を発生させる。
  • assert
    • 条件が合致した場合にだけ、例外を発生させる。
  • try/except
    • tryの中で例外があった場合に、exceptのロジックを実行する。
  • try/finally
    • tryの中での例外の有無に関わらず、finalyのロジックを実行する。
  • with/as
    • コンテキストマネージャと呼ばれる高度な例外処理を実行する。

ここで重要な構文はraisetry/except、それにtry/finallyでしょう。

class EggException(BaseException):
  what = 'This is EggException'
class SpamException(BaseException):
  what = 'This is SpamException'

try:
	# この中で起こった例外が補足される。

	### 以下のいずれかのraiseのコメントを外して実行する。
	# EggExceptionのインスタンスを例外として投げる。
	#raise EggException() #これを実行すると、exceptの中が実行される。
	# SpamExceptionのインスタンスを例外として投げる。
	#raise SpamException() #これを実行すると、elseの中が実行される。
	# EggExceptionクラスのオブジェクトを例外として投げる。
	#raise EggException #これを実行すると、exceptの中が実行される。

	# 例外が発生した場合、ここは実行されない
	print 'Not execute'

# EggExceptionを補足する。他の例外は無視。補足した例外を変数eに入れる。
except EggException, e:
	print e.what

# BaseExceptionを継承した例外を補足する。補足した例外は変数eに入れる。
else BaseException, e:
	print e.what

# 例外の有無に関わらず、実行する。
finally:
	print 'every time'

これが基本形ですね。
例外として投げることが出来るのは、”オブジェクト“です。
従って、クラスのインスタンスやクラスそのものを投げることが出来ます。
先ほどの例では、”raise EggException”がこれにあたります。

そのため、先ほど”文字列例外は廃止された”と書きましたが、文字列のオブジェクトを例外として投げることは出来ます。

try:
	raise 'This is test.'
except:
	print 'Got Exception.'

その他に数字もOK。

try:
	raise 1000
except:
	print 'Got Exception.'

さらに、tryがない場合はどうなるのか?

“例外が発生したオブジェクトを呼び出しているオブジェクト”に例外が渡されます。

一番簡単な例はこれです。

def egg():
	raise BaseException

try:
	egg()
except:
	print 'Got Exception'

この例ではegg関数の中にtry/exceptはないのですが、それを呼び出しているメインモジュールにはtry/exceptがあります。
そのため、このメインモジュールで例外が補足されています。
これはどれだけネストが深くなっても同じです。
もちろん、クラスでも、モジュールでも。

python_exception.png

例外に関してはこんなところでしょうか。
もっと色々と説明したいのですが、長くなってしまいます。
詳しく知りたい方は是非、初めてのPythonを買ってください。

これで、Pythonの基礎は一通り終わりです。
次は指にPythonを慣れさせる段階ですね!

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

初めてのPython(6) Pythonでオブジェクト指向プログラミングを

初めてのPython“も残り1部です。
ここまで長かった…ですが、とても勉強になりました。
Pythonを学び、”PHPには何が足りないのか“を自分の中で消化出来たような気がします。
それではラストスパートに入って行きましょう。

初めてのPython VI部

初めてのPython“のVI部には”Pythonにおけるオブジェクト指向プログラミング“がまとめられています。
クラスメソッドの構文はもちろんのこと(これがないとクラスを書けませんしね…)、オブジェクト指向の考え方まで。

has-a関係やデリゲーション、ファクトリといった”オブジェクト指向プログラミングの基本的な設計“までもまとめてあります。
このあたりの話はPythonに限ったことではないので、”オブジェクト指向プログラミング“の経験がある方は読み飛ばせると思います。
ただ、単純な構文だけでなく、このような根本の考え方を含めて学べるのは”初めてのPython“の良いところかな、と。

これまでの内容を理解しているのであれば、VI部の内容を理解するのは容易いでしょう。
構文だけを学び、ソースコードを眺めるだけでも良さそうです。

ここでは”オブジェクト指向プログラミング“の経験があることを前提に、Pythonでのオブジェクト指向プログラミングをまとめていきます。

クラスとインスタンス

Pythonではクラスも”オブジェクト“です。
そしてインスタンスも”オブジェクト“です。

全てオブジェクトで扱う。それがPythonです。

そして、クラス宣言も”オブジェクト“を生成し、それを”クラス名の変数“に入れる操作にしか過ぎません。

class Spam:
	def printMe(self):
		print self

SpamCopy = Spam
spamIns = SpamCopy()
spamIns.printMe()
# spamInsがSpamのインスタンスであることが表示される。
# <__main__.Spam instance at 0x7f779b318518>

このように書くことで、クラスを別の変数に入れることも出来ます。

また、インスタンスの生成も変わっていますよね。
関数の呼び出しと同じ宣言の仕方でインスタンスが出来てしまいます。

メソッドも関数と同じ”def“を使います。
ただし、第一引数に”self“を使う点が異なります。
先ほどの例で分かったと思いますが、この”self“には呼び出した自分自身(インスタンス)が入ります。

PHPやJavaで言うところの”this“ですね。
Pythonではこれを明示的にメソッドに示します。
これが結構分かりやすい。

class Spam:
	num = 0
	def add(self):
		self.num += 1
	def getNum(self):
		return self.num

spamIns = Spam()
spamIns.add()
print spamIns.getNum()
# 1が表示される
spamIns.add()
print spamIns.getNum()
# 2が表示される

self“が何者かだけ分かっていれば、扱いやすいと思います。
クラスオブジェクト自身が持つメソッドを使うことだって出来ますよ。

class Spam:
	num = 0
	def add(self):
		self.num += 1
	def getNum(self):
		return self.num

spamIns = Spam()
Spam.add(spamIns)
print Spam.getNum(spamIns)
# 1が表示される
Spam.add(spamIns)
print spamIns.getNum(spamIns)
# 2が表示される

つまり、インスタンスのメソッドを呼び出すときは、Pythonが自動でクラスオブジェクトのメソッドの第一引数を埋めてくれているんです
内部的に何か訳の分からないことをやられるよりは、こっちのほうがずっと分かりやすいです。
クラスとインスタンスの関係を知っていれば良いんですから。

クラスって何?“と聞かれれば、”それは単なる名前空間にしか過ぎない。“と答えることが出来そうです。
class宣言の後には、”名前空間“が出来上がります。
その中に”変数( = 属性)“や”関数( = メソッド)“を宣言していると考えれば良いんです。
だからこそ、”def add(self):“や”num = 0“といった今までと同じ書き方が出来る。

話は戻りますが、クラス宣言も単なる”オブジェクトの生成“と”そのオブジェクトへのリファレンスを持った変数の生成“にしか過ぎません。
そのため、こんなことも出来ます。

class Spam:
	num = 0
	def add(self):
		self.num =+ 1

class Spam:
	num = 0
	def sub(self):
		self.num =- 1
	def getNum(self):
		return self.num

spamIns = Spam()

spamIns.sub()
print spamIns.getNum()
# -1が表示される

spamIns.add()
# エラーが発生する

PHPだと”クラスの二重宣言だ!“って怒られてしまいますよね。
この柔軟性がPythonの素敵なところです。

他にPythonのクラスの特徴として、属性が”ディクショナリ“で管理されていることが上げられます。

class Spam:
	def attributes(self):
		self.egg = 'egg'
		return self.__dict__

spam1 = Spam()
print spam1.attributes()
# {'egg': 'egg'}が表示される。

演算子のオーバーロード

Pythonのクラス宣言で嬉しいのが、この”演算子のオーバーロード“です。
これは何か?“というとインスタンスに対する演算子の処理を独自に定義出来ます
つまり、プラス(+)やマイナス(-)がインスタンスに対して実行出来るんです。

class Company:
	workers = 0
	def __add__(self, other):
		newCompany = Company()
		newCompany.workers = self.workers + other.workers
		return newCompany

	def __sub__(self, other):
		newCompany = Company()
		newCompany.workers = self.workers - other.workers
		return newCompany

company1 = Company()
company1.workers = 10
company2 = Company()
company2.workers = 20

company3 = company1 + company2
print company3.workers
# 30が表示される

company4 = compony2 - company1
print company4.workers
# 10が表示される

ただし、同じクラスのインスタンス同士の演算でない場合、対象のインスタンスが演算子の左右どちらかに書かれるかで動作が変わってきます。
自分自身、あまり理解していないのと、まずは基本形だけ覚えれば良いと思うので、ここではこれだけにしておきます。

Pythonでは”コンストラクタ“も演算子のオーバーロードの一つです。
インスタンスを生成するときに使用する”引数指定“をオーバーロードするイメージでしょうか。

class Company:
	def __init__(self, workers = 0):
		self.workers = workers

company1 = Company()
print company1.workers
# 0が表示される

company1 = Company(20)
print company1.workers
# 20が表示される

演算子のオーバーロードを使えば、クラスに”指定された属性“がない場合の処理も簡単に書けます。

class Company:
	def __getattr__(self, name):
		if name == 'name':
			return '1-byte.jp'
		else:
			# 属性がない場合の例外を発生させる
			raise AttributeError

company1 = Company()
print company1.name
# 1-byte.jpが表示される
print company1.type
# エラーが発生する

その他に、”リスト表記“にも対応することが出来ます。

class Spam:
	def __getitem__(self, num):
		return num * 2

spam1 = Spam()
print spam1[1]
# 2が表示される
print spam1[11]
# 22が表示される

しかし、便利ですね。
色々活用のアイディアが広がります。

継承と多重継承

Pythonではもちろん、”多重継承“も出来ます。
(PHPでは出来ないんですがね…)

class Person:
  name = None
  sex = None

  def __init__(self, name, sex):
    self.name = name
    self.sex = sex

  def getName(self):
    return self.name

  def getSex(self):
    return self.sex

class Printer:
  def __str__(self):
    return str(self.__dict__)

class Worker(Person, Printer):
  job = None

  def __init__(self, name, sex, job):
    Person.__init__(self, name, sex)
    self.job = job

worker1 = Worker('John', 'male', 'engineer')

print worker1
# {'job': 'engineer', 'name': 'John', 'sex': 'male'}が表示される
print worker1.getName()
# 'John'が表示される
print worker1.getSex()
# 'male'が表示される

しっかり継承されていることがわかります。
と、ここで気づいたのですが、(多重継承でない)継承の説明をしていませんでしたね。

class クラス名(継承するクラス名):

このステートメントを書くことで、クラスを継承出来ます。
先ほどのコードで分かったと思いますが、多重継承の場合は、

class クラス名(継承するクラス名1, 継承するクラス名2, 継承するクラス名3, …):

と書きます。
属性“や”メソッド“の検索順は書いた順番です。つまり、左側に書いたクラスから順に検索されます

class Spam1:
	name = 'Spam1'

class Spam2:
	name = 'Spam2'

class Spam3(Spam1, Spam2): pass

class Spam4(Spam2, Spam1): pass

spam = Spam3()
print spam.name
# 'Spam1'と表示される

spam = Spam4()
print spam.name
# 'Spam2'と表示される

これだけ理解していれば基本は大丈夫です。

ちょっと複雑な話をすると、継承する形によって検索順が問題になることがあります。
いわゆる”ダイアモンド継承“と呼ばれる形です。

多重継承したクラスを先に検索するのか?それとも、親クラスを先に検索するのか?

このあたりの話は面倒なので、ここには書きません。
知りたいという方は、SIBUKAWAさんの以下の記事が参考になります。

長くなったので、”Pythonのオブジェクト指向プログラミング”の話はここで終わりにしましょう。
本当は”どうやってスタティックメソッドを作るのか“、”情報を隠蔽するのか“など、細かい話は尽きません。

私自身、”初めてのPython“を読みましたが、全てを理解し、活用出来るようになった訳ではありません。
このあたりになってくると、テクニック的な話が多くなりますね。
その辺は自分でアプリケーションを作りながら、ソースコードを読みながら勉強しましょう。

次で”初めてのPython“シリーズは最後です。
ブログ書きながら読んだら2週間かかっちゃいましたね…。

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

初めてのPython(5) importとfromとreloadでモジュールを使いこなす

初めてのPython“も佳境ですね。
あと2部読み進めればおしまいです。
初めてのPython“を全て読むことが出来れば、Pythonistaとしての第一歩を踏み出せたことになります(自分の中で)。

初めてのPython V部

初めてのPython“のV部は、”モジュール“に関してまとられています。
Pythonにおけるモジュールのインポートの仕方や、再読み込みその扱いまで。

と、”その前にモジュールって何?“という方のためにWikipediaから引用しておきます。


プログラミングにおいて、一連の機能をひとまとまりになる複数の機能:モジュールに分割し、それぞれ別に開発する場合がある。こうすることで、全体として完成を早めることが出来る上、モジュール単位でテストしたりすることが可能になり、モジュールの入れ替えで機能を高めたり補修したりすることができるようになる。

モジュール – Wikipediaより

そして、モジュールとクラスの違いは、以下の通り。


ジュールとクラスの違いは以下の通りである。
クラスにはインスタンスとしてオブジェクトを生成する機能がある。
クラスは他のクラスの動作やデータを継承することができる。
ポリモーフィズムにより、クラスのインスタンス間の関係は実行時に変化するが、モジュール間の関係は静的である。
モジュールとクラスの類似点は以下の通りである。
どちらも実装の詳細を外部から隠蔽する。
どちらも階層(モジュール階層とクラス階層)を形成することができる。

モジュール – Wikipediaより

普段、PHPを使っているのでこの”モジュール“という概念が頭にありませんでした。
PHPではインポート(require)すると、C言語のインポート(include)と同じで、”ファイル自体がそこに読み込まれる“という動作をしますからね。
インポートだけでは、名前空間が別に分かれたりしないわけです。

こういう構造もあってか、PHPではモジュールをすっ飛ばしてクラスを使います。
そのため、私自身、”モジュール“は初体験でした。

このあたりの話になってくると、色々と絡み合ってくるので、読むのに苦労しました。
最後の応用の部分は理解出来たか、自身がありませんが、基本は押さえたので、これをまとめていきます。

モジュール

Pythonにおける”モジュール”とは、

ファイル

です。

これが一番分かりやすいでしょう。”モジュール” = “ファイル“なのです。

以前の記事を覚えているでしょうか?

この記事からスコープの画像を引用します。

このファイルにあたる部分が、Pythonでは”モジュール“と呼ばれます。

この図から分かる通り、Pythonではファイル間でスコープを共有しません
そのため、ファイルごとに機能を独立してコードを記述することが出来るのです。
機能を独立して記述できる…だから、Pythonではファイルが”モジュール“なのです。

Pythonではこの”モジュール”も”オブジェクト“として扱います。
モジュールを読み込むためにはimportステートメントを使います。

オブジェクトである証拠をソースコードで示しましょう。
ファイル構造は次の図の通りとします。

python_module_import.png

これらのファイルの中身は次のソースコードとします。

spammer.py

import spam

print spam.x

spam.py

x = 20

egg.py

import spam

spam.x = 10

import spamer

egg.pyを実行した結果として出力されるのは”20“ではなく、”10“です。
モジュールがオブジェクトとして扱われるために、このような動作となります。

今までの”可変性/不変性“や”リファレンス“の考え方を理解していれば、この動作はしっくり来ます。
importステートメントは“モジュールオブジェクト”へのリファレンスを作り出す操作なのです。

また、説明していませんが、モジュール内の変数へはドット(.)を使って自由にアクセス出来ます。
モジュール内にある変数を呼び出すときは、

[モジュール名].[変数名]

関数を呼び出すときは、

[モジュール名].[関数名](引数)

という形を取ります。
クラスと同じような使い方なので、オブジェクト指向プログラミングをした人にとっては理解しやすいかと。

モジュール = ファイル
分かりやすいし、書きやすいと思いませんか?

import

先ほど説明した通り、importステートメントは、”モジュールオブジェクトへのリファレンス“を作り出します。

1度目のimportステートメントは“モジュールオブジェクト”を生成し、そのリファレンスを設定します。
2度目以降のimportステートメントは生成済みの”モジュールオブジェクト”へのリファレンスを設定します。

spam.py

x = 30

egg.py

import spam

spam.x = 10

# spamモジュールをsecond_spamという名前でインポートする
import spam as second_spam

print second_spam.x

egg.pyを実行した結果は”10“です。
モジュール内の変数が上書きされてしまっていることが分かります。
importステートメントで設定した変数はあくまで、”モジュールオブジェクトへのリファレンス“にしか過ぎません。

python_multiple_import.png

importの基本はこれだけです(多分)。

from

fromはimportの拡張です。

spam.py

x = 10

egg.py

from spam import x
print x

これだけです。

え、わからない?

モジュールの中の”変数”や”関数”を指定して、読み込んでいるんです

importステートメントはモジュールをまるごと”オブジェクト”として読み込む方法でした。
fromステートメントは、そのオブジェクト内にあるオブジェクトへのリファレンスを、インポート元のスコープに設定します。

python_from_import.png

このため、先ほどのimportと同じようなコードを書いても、同じ結果にはなりません(変数が上書きされてしまうからね)。

spam.py

x = 30

egg.py

from spam import x

x = 10

# eggモジュールのxオブジェクトをyという名前でコピーする
from spam import x as y

print y
# 30が表示される

しかし、可変性のオブジェクトだと話は別です。
こんなことが出来ます。

spam.py

x = []

egg.py

from spam import x

x.append(1)

# eggモジュールのxオブジェクトをyという名前でコピーする
from spam import x as y

print y
# [1]が表示される

どうでしょう。
なぜこうなるか、わかりますか?
説明するのが難しいですね…これまた、”オブジェクト“と”リファレンス“の関係です。
理解出来ない“という方は前の記事を読んでみてください。

reload

個人的に面白い使い方が出来そうと思ったのが”reload“です。
名前の通り、モジュールを再読み込みします。

先ほど、importは”1度目の呼び出し時だけオブジェクトを生成する“と説明しました。
これは、”1度目の呼び出し時だけソースコードをコンパイルしてオブジェクト化する“ことを示します。
つまり、ソースコードはimportを使う限り、1度だけしか読み込まれないんです

これを2度、3度と読み込むのが”reload“です。

例えばこんなコードを書いてみます。

spam.py

print 'spam'

egg.py

import time
import spam

while 1:
  reload(spam)
  time.sleep(5)

egg.pyを実行すると、5秒おきにspam.pyを読み込みます。
放っておくと、永遠と”spam”と言い続けるでしょう。

これをターミナルで実行した上で(プログラムを強制終了させずに)、spam.pyを書き換えます

spam.py

print 'spam and egg'

あら不思議、”spam and egg”を連呼するようになりました(うるさいのは変わりないですけどね)。

この”reload“、使い道を間違えなければ強力な武器になりそうです。
プログラムを止めずに、モジュールを差し替えることが出来るんですから

モジュールについて簡単にまとめてみました。
実はまだたくさん書いていないことがあります。
例えば、モジュール内の変数の隠蔽モジュールの自己診断パッケージのインポートなどです。

初めてのPython“のV部は”簡単なのに、難しい“です。
このあたりは理解よりも、慣れが大きいんでしょうね。

あぁ、Pythonでプログラムが書きたくなってきた!

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

初めてのPython(4) 関数でさえオブジェクトであるPython

やっと、”初めてのPython“のIV部を読み終わりました。
初めてのPython“の読書はすごく順調に進んでいます。
そう、すごく順調に…。

そこには触れないようにして、今日のPythonのまとめに行きましょう。

初めてのPython IV部

初めてのPython“のIV部は、”関数“についてまとめてあります。

え、関数なんて余裕だよ。“という人も、読み飛ばさずに読んでください。
Pythonにおける関数も他の言語とちょっと変わっています

PHPの関数宣言functionにあたる宣言がPythonではdefです。
この宣言を使って、Pythonではこんなことが出来ちゃいます。

def create_memory(x, y):
	def memory():
		return x, y
	return memory

mem = create_memory(2, 3)
print mem()
# (2, 3)が表示される

関数をネストして、値をネストした関数内に保持させています。

これが出来るのも全て”関数がオブジェクト“だからです。

IV部ではこの”関数がオブジェクト“であることのメリットや、その使い方を説明しています。

関数オブジェクト

先ほども述べた通り、Pythonでは関数でさえオブジェクトとして扱います。
それはこのコードを見れば簡単に分かるでしょう。

def add(x, y):
	return x + y

print add(1, 2)
# 3が表示される

my_add = add
print my_add(1, 2)
# 3が表示される

print my_add
# <function add at 0x7f0be1e095f0>が表示される。0x7f0be1e095f0は実行時によって違う。

関数を作っておき、それ自体(関数)を変数に代入する“ということがPythonでは簡単に出来てしまいます。

Pythonは本当に何から何までオブジェクトです。

柔軟性の高い引数

Pythonで関数を指定する場合は、次のように引数を渡します。

  • 通常
    • 一般的な引数の順番で指定する方法。
  • キーワード
    • 引数の名前と共に値を指定する方法。順序関係なし。
  • デフォルト
    • 一般的な方法。デフォルト値を引数に設定出来る。ただし、Pythonならではの癖がある。
  • 可変
    • 可変引数も指定可能。指定された引数をタプルまたはディクショナリで受け取ることが出来る。

Pythonでは柔軟性が高く、かつコードが少なくなるように設計されています。
キーワード引数や可変引数は色々と活用出来そうです。

通常“はイメージにお任せします。
恐らく私とあなたが抱いているイメージは同じでしょう。

キーワード“はこのような指定が出来ます。

def spam(x = 1, y = 1, z = 1):
	return x, y, z

print spam(y = 2)
# (1, 2, 1)が表示される。
print spam(y = 2, x = 10)
# (10, 2, 1)が表示される。

デフォルト“は先ほどのキーワードの例でも使いましたが、引数に”デフォルト値“を設定する方法です。

def spam(x = 10):
	return x

print spam()
# 10が表示される。
print spam(20)
# 20が表示される。

何度もしつこいですが、Pythonでは全てがオブジェクトです。
それによって起きる癖のあるコードを紹介します。

def spam(y, x = []):
	x.append(y)
	return x

print spam(y = 1)
# [1]が表示される。
print spam(y = 2)
# [1, 2]が表示される。
print spam(y = 3)
# [1, 2, 3]が表示される。

Pythonではデフォルト値でさえ、オブジェクトとして扱ってしまうのです。
“x”に指定しているデフォルト値は”空のリスト“です。
リストは可変性のオブジェクト
可変性のオブジェクトは、変更を加えてしまうと、それを参照している変数全てに影響があります。

Pythonは関数オブジェクト作成時にデフォルト値のオブジェクトも作成します
このデフォルト値にオブジェクトを持つ関数オブジェクトを何度も呼び出すことで、例のように内容がどんどん追加されることになります。

可変“は次のように可変長の引数を渡すことが出来ます。

def spam(*x):
	return x

print spam(1)
# (1, )が表示される
print spam(1, 2)
# (1, 2)が表示される
print spam(1, 2, 3)
# (1, 2, 3)が表示される

可変で渡ってきた引数は、タプルとして引数に代入されます。

キーワード引数を”可変“にすることも出来ます。

def spam(**x):
	return x

print spam(z = 1)
# {'z':1}が表示される
print spam(z = 1, y = 2)
# {'z':1, 'y':2}が表示される
print spam(z = 1, y = 2, x = 3)
# {'y':2, 'x':3, 'z':1}が表示される

これらの引数指定は便利だと思いませんか?
なんだか、色々なことが出来そう…と想像してしまいます。

スコープを表すLEGBルール

関数を使う上で”スコープ“を理解することは重要です。
スコープって何?“と言う方はいないと思いますが、Wikipediaから引用しておきます。


プログラミングでのスコープとは、ある変数や関数が特定の名前で参照される範囲のこと。ある範囲の外に置いた変数等は、通常、その名前だけでは参照できない。このときこれらの変数はスコープ外である、「見えない」といわれる。

スコープ – Wikipediaより

簡単に言ってしまうと、”名前の影響範囲“ですね。

PythonではLEGBルールと名付け、その範囲が次のように決まっています。

  • L: Local scope
    • その関数内のみで有効なスコープ。
  • E: Enclosing function’s scope
    • その関数の外側内側の関数でのみ有効なスコープ。
  • G: Global scope
    • ファイル全体で有効なスコープ。
  • B: Built-in scope
    • プログラム全てで有効なスコープ。

図で示すと、このような形です。

python_scope.png

[2010/09/30 追記] 上記の図は間違っています。正しくはこちら。

python_scope.png

Local“は簡単に分かると思うので説明しません。
Pythonが他と少し違うのは”Enclosing function’s scope“と”Global scope“です。

Enclosing function’s scope“は一番始めに示したコードです。

def create_memory(x, y):
	def memory():
		return x, y
	return memory

mem = create_memory(2, 3)
print mem()
# (2, 3)が表示される

これはcreate_memory関数にネストされたmemory関数が、create_memoryの変数である”x”を利用しています。
ネストされた関数がネストしている関数の変数を使うのが、”Enclosing function’s scope“です。

Global scope“は名前の通り、グローバルなスコープなのですが、Pythonではこのグローバルの範囲が違います。
Pythonのグローバルはあくまで、”ファイル単位“です。
ファイルの内部でしか、その変数は有効になりません。
ファイルの外からその変数を使いたい場合は、importなりなんなりをしないといけない。

x = 10

def spam():
	global x
	return x

print spam()
# 10が表示される

x = 20

print spam()
# 20が表示される

“x”はソースコード内でのみ、有効です。
この他にこんなソースコードを書いても”x”は使えません。

# 先ほどのソースコードをspam.pyとする
import spam

print x
# エラーが表示される

スコープはこんなところでしょうか。
スコープ“と”関数は、やはり”Pythonは全てがオブジェクトである“ということを理解していれば、大概のコードは理解出来そうです。

ステートを保持するジェネレータ

個人的に面白いと思ったのが、”ジェネレータ“です。
これもソースコードを見た方が早いでしょう。

def spam():
	for x in 1, 2, 3, 4:
		yield x

x = spam()
print x.next()
# 1が表示される
print x.next()
# 2が表示される
print x.next()
# 3が表示される
print x.next()
# 4が表示される

print x.next()
# エラーが表示される

これは、関数の”return“の代わりに”yield“を指定することで”その時点での値を返し、関数内の処理を一時停止する“イメージです。
yield“の代わりに”return“を使ってしまうと、この関数は全て”1“を返してしまいます(一般的な関数の動作ですね)。

しかし、”yield“を使うと、”どこまで処理が進んだか“をPythonが保存しておいてくれるのです。
それを次に進ませるために”next”メソッドを呼んでいます。

処理が全て終了すると”これ以上、返す値がないよ“ということで、エラーが表示されます。

これもうまく使いこなせば色んなコードが書けそうです。

初めてのPythonにはまだまだ、たくさんのことが書いてあるのですが、とりあえず基本だけまとめてみました。
しかし、徐々に難しくなってきましたね。
以前にまとめたことをしっかり理解していないと、このあたりの動作を理解するのに苦労しそうです。

どの章でもそうですが、

Pythonはあらゆるものをオブジェクトとして扱う

これを常に念頭において読み進めるのが良さそうです。

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

初めてのPython(3) ここが違うぞ、Pythonさん

皆さんはPythonの勉強は順調に進んでいますか?
私はやっと”初めてのPython“のIII部に入れました。

タスクでは”初めてのPython”読了を1週間としていましたが、正直キツいですね。
読むだけなら良いのですが、ブログにまとめながらなのでなおさら。

しっかり読みたいしっかり理解したいのであれば”2週間“ほど掛かるかもしれません(仕事しながらなら)。

ただ、しっかり読んでも細かい部分は確実に忘れてしまうので、ポイントだけ押さえた読み方をお勧めします。
私も今、”Pythonとは何か?“に焦点を置いて、読み進めています。

初めてのPython III部

初めてのPython“のIII部には何が書かれているか?
Pythonの特徴とも言える基本構文について、がっつりと書かれています。

Pythonの特徴である”スペース“や”タブ“が意味を持つこと、少し変わった”while“と”for“。
そしてそれをどう使うか、例題を踏まえて。

さらに、”どの書き方が最もPython的で最も最適か“まで書かれているので、冒頭に述べた”Pythonとは何か?”の答えが見つけやすい。
これまでI部、II部を読みましたが、一番、”なるほど!“と腹落ちしたのがこのIII部です。

I部で全体像を理解し、II部で内部構造を理解し、III部でPythonの特徴を理解する。

初めてのPython“はこんな章立てになっています。

読み終わったら”初めてのPython 読書ガイド“を記事にしたいですね。

この記事では特徴的なPythonの構文に焦点を当て、取り上げて行きます。

Pythonとは何だ

まずはおさらいです。

以前の記事で、Pythonについて私はこう書きました(“初めてのPython“をまとめただけですが)。


Perlは芸術家のための言語、そしてPythonはエンジニアための言語。

初めてのPython(1) 基礎知識まとめ“より

Pythonはエンジニアのための言語です。
そのため、ミニマリスト主義、つまり誰が書いても同じ書き方になるように言語が作られています。

始めはぼやっとしか、この概念が理解出来ませんでした。
なるほど、誰が書いても同じように…大事だね。“程度です。
III部を読み、これがどういう意味を持つのか、少しずつ明確になってきました。

これは、

同じ種類の構文を使ったときに、様々な書き方を生まれないようにPythonが制約を作ること

だったんです。

Pythonの特徴である”インデント“もそうです。

Pythonはなぜ、”インデント“でブロックを分けるのでしょうか?
この答えも、さきほどの答えの中に含まれています。

例えば、PHPのif文はこのように書きます。

if($a == 1) {
	print 'a is one.';
	$a = 2;
}

恐らく皆さん、こういった書き方をすると思うのですが、これはこんな書き方も出来ます。

if($a == 1) {
print 'a is one.';
$a = 2;
}

こんな書き方だって出来ちゃいます。

if($a == 1) { print 'a is one.'; $a = 2; }

Pythonでは、“インデント”がブロックであり、“改行”がその文の終わりを示します。
この基本ルールに則ってやれば、先ほどのプログラムはPythonではこうなります。

if a == 1 :
	print 'a is one.'
	a = 2

“インデント”がブロックであり、”改行”がその文の終わり

これが決められてしまっているので、この書き方しか出来ないんです(もちろん例外的な書き方もありますが)。

普通、他の言語だと、こういったことはコーディング規約で決めます。
しかし、Pythonは言語自身がコーディング規約を決めています。

このように言語側でコーディング規約があれば、新しい開発メンバーが入ってきた場合も、その開発チーム”独自ルール“に悩まされなくて良さそうです。

気に入りましたよ、Pythonさん。

条件分岐

Pythonで条件分岐は、他の言語と同じく”if“を使います。

しかし、ちょっとだけ違うのが”else if“です。
Pythonでは”else if“を”elif“と書きます。

タイピングが少なくする工夫でしょう。

elif“…良いですね。

繰り返し

Pythonで繰り返しは、これも他の言語と同じ”while“と”for“を使います。

しかし、”do until“あるいは”do while“はありません
do until“がない理由も、書き方の統一のためでしょう。
全て”while”でことが足りますからね。

先ほど、他の言語と同じ”while“と”for“と言いましたが、厳密には違います。

“for”の構文がfor($i = 0; $i < 10; $i ++)ではないのです
どちらかと言うと、foreachに似ています。

Pythonではタプルやリスト、文字列などのシーケンスを使って”for“を書きます。

for x in [1, 2, 3]
	print x

じゃ、for($i = 0; $i < 10; $i ++)を書きたいときはどうするか?
range関数を使って書きます。

for x in range(0, 10):
	print x

ミニマリスト主義を徹底してますね...。

その他に面白いのは"while"と"for"に"else"が使えることでしょうか。
次のコードを両方実行してみてください。

for x in range(0, 10):
	if x == 5:
		break
else:
	print 'break is not executed.'
for x in range(0, 10):
	print x
else:
	print 'break is not executed.'

"break"文が実行されなかった場合に"else"ブロックが実行されます。
何に使うかというと、"フラグを使う必要があるとき"です。

繰り返しで何かを検索するときなど、"if"文の中にフラグと"break"文を入れますね。
これが繰り返しのelse文を使うことですっきりと書けます。

先ほどのソースをフラグを使って書いた例です。

flag = False
for x in range(0, 10):
	if x == 5:
		flag = True
		break

if flag != True:
	print 'break is not executed.'

"for"や"while"に"else"、なかなか面白い発想ですよね!

リスト内包表記

一番特徴的なのはこれです。

print [x * 2 for x in (1, 2, 3)]
# [2, 4, 6]が表示される。

リストの中に"for"を入れることで、繰り返し分のリストを作ってしまいます。
なかなか説明しにくいですが、構文的にはこうです。

[リストに入れたい値 forの構文]

なので、こんな書き方も出来ます(意味があるのかは別、教科書にはそんなソースコードがたくさん出てくるし)。

print [1 for x in (1, 2, 3)]
# [1, 1, 1]が表示される。

さらに"if"文も組み合わせて使えるようです。

print [x * 2 for x in (1, 2, 3) if x == 1 or x == 2]
# [2, 4]が表示される。

シンプルで見やすいし、理解出来れば打ちやすい。

Pythonを書くときは内包表記もがっつり使っていきましょう。
速度的にも優れているようです。
その辺りは"初めてのPython"を読んでください。
嫌というほど説明されていますから。

とりあえず、こんなところですね。
まだまだたくさん紹介したい構文があるのですが。

"初めてのPython"を参考に、"Pythonで他の言語と比べて、ここが変わっている"という点を取り上げてみました。
どうです?Pythonを使いたくなりましたか?

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

初めてのPython(2) ビルトインオブジェクトのまとめ

どんどん、Pythonを勉強して行きますよ。
今日は初めてのPython II章をまとめます。

前回の記事はこちらです。

初めてのPython II部

初めてのPythonのII部には”ビルトインオブジェクト“がまとめられています。


まず知っておかねばならないのは、Pythonではデータがオブジェクトとして扱われるということです。オブジェクトは、大きく、Python言語にあらかじめ用意されているビルトインオブジェクトと、PythonやCのツールを使ってプログラマ自らが作成するオブジェクトに分かれます。

初めてのPython“(p69)より

プログラム内でモジュールなどを何も定義することなく、利用出来るオブジェクトがビルトインオブジェクトです。
主要なビルトインオブジェクトとして、”初めてのPython“では以下のものが紹介されています。

  • 数字
  • 文字列
  • リスト
  • ディクショナリ
  • タプル

まだ、”初めてのPython“を最後まで読んでいませんが、Pythonを理解する上でこのII部が最も重要だと感じました。

始めに上記のオブジェクトの概要が、次にそのオブジェクトの詳細な説明と使用例が、最後に紹介したオブジェクトの中でも重要な部分がまとめられています。
つまり、同じオブジェクトがII部の中で3度も説明されています。
それだけ、ビルトインオブジェクトの理解が重要ということでしょう。

また、このII部を読み進めるうちに、”PHP脳を変えなければいけないな“とも感じました。
PHPPython、あまりに勝手が違いすぎる。
構文が違うだけじゃなく、このPythonの”ビルトインオブジェクト“が大きな違いを生んでいます。

II部の中では特に低水準言語であるC/C++とPythonの比較が出てきます。


ガーベージコレクションが行われるメリットは、プログラマが逐一そのためのコードを書かなくても、メモリ領域が解放されるということです。そのため、CやC++といった低水準のプログラムに比べ、プログラムの機能に直接関係のないコードをかなり減らすことが出来ます。

初めてのPython“(p123)より

こういった比較が出来るということは、Pythonがよく考えられた言語であり、様々な使い方が出来る証拠ではないでしょうか。
エンジニアがしっかりとした骨組みを考え、曖昧さを残さないように組み立てる。
それがPythonである気がします。

本文中にコード例やメソッドがいくつも出てきます。
それらを全て紹介は出来ないので、それぞれのオブジェクトの基本的な考え方をまとめてみます。

オブジェクトの基本

まず、Pythonの変数を理解します。

python_vars.png

Pythonにおいて変数はオブジェクトへのリファレンスでしかありません。
C言語で言うところのポインタのようなものですね。

これを頭に入れた上で、オブジェクトの種類を理解します。

Pythonのビルトインオブジェクトはカテゴライズされています。
そのカテゴリごとに操作が似通ってきます
これを理解すれば、かなり頭の中が整理されます。

python_object_categorize.png

まず、可変性と不変性です。

  • 可変性
    • 内容を変更出来るオブジェクト
  • 不変性
    • 内容を変更出来ないオブジェクト

簡単ですが、これだけです。
対象が”オブジェクト“であることに注意です。
先ほどの変数の説明で出てきた”リファレンス“ではありません。

例えば、”リスト“と”タプル“は同じシーケンスというカテゴリに属するのですが、可変性と不変性の点で異なっています。

# リストの例
a = [1, 2, 3]
a[0] = 10
print a
# [10, 2, 3]が表示される。[1, 2, 3]というリストの中身を変更出来る。

# タプルの例
b = (1, 2, 3)
b[0] = 10
# エラーが表示される。(1, 2, 3)というタプルの中身は変更出来ない。

# 変数の置き換え
b = a
print b
# [10, 2, 3]が表示される。エラーは表示されない。リファレンスを変えただけで、オブジェクトを変更した訳ではないため。

次に、オブジェクトのカテゴライズです。

  • 数字
    • そのまま数字です。整数や浮動小数点もこれです。
  • シーケンス
    • 簡単に言ってしまえば、配列です。連想配列ではなく、ただの配列。順序を持っています。文字列もこのシーケンスに分類されます。
  • 写像
    • こちらは連想配列です。キーに文字列などを指定出来ます。順序を持っておらず、これが保証されません。

数字は良いと思うので、問題は”シーケンス“と”写像“ですね。
どちらも配列のようなものなのですが、順序を持っているか持っていないかの違いがあります。

PHPだと連想配列でも順序を持っている(foreachで順番に参照出来る)のですが、Pythonだとそうもいかないようです。
さらに、PHPだと配列連想配列の区分は気にしなくて良いのですが、これがPythonだと明確に分けられています

# リスト(シーケンス)の例
a = [1, 2, 3]
a[10] = 10
# エラーが表示される。順序があるため、いきなり10番目の配列に代入は出来ない。代入したい場合は0...10の配列を作った後に、この操作を行う。

# ディクショナリ(写像)の例
b = {"a":1, "b":2, "c":3}
b[10] = 10
print b
# {'a':1, 'b':2, 'c':3, 10:10}が表示される。順序がないため、好きなキーに値を代入出来る。また、順序も保証されない("10:10"が先頭にいってしまうかも...)。

オブジェクトの基本はこんなところだと思います。

同じカテゴリの属するオブジェクトは基本的に同じ操作が出来る“ということも頭に入れておいてください。

あとはそれぞれの構文を覚えればOK!

数字

100

これが数字のオブジェクトです。

この他に、以下も全て数字のオブジェクトです。

  • 10.00010
  • 11111111111111111111111111111111111L
  • 0x9ff
  • 3+4j

Pythonでは以下の数字が使えます。

  • 短整数
    • 1234, -24, 0
  • 長整数
    • 1111111111111111111111111111L
  • 8進数, 16進数
    • 0177, 0x9ff, 0XAA
  • 複素数
    • 3+4j, 3.0+4.0j, 3J

中の処理を説明しだすと、長いので(そしてさほど理解出来ていないので)省略します。
数字に関しては、一般的なスクリプト言語と同じように使えそうです。

数字は不変性のオブジェクトなので、変更出来ません(というか数字を書き換えるような操作って出来ないよね、きっと)。

文字列

‘Spam’

これが文字列のオブジェクトです。
書き方は他のスクリプトと一緒でも、Pythonはひと味違います。

print 'Spam'[1:3]
# "pa"が表示される。

こんなことが出来ちゃいます。

Pythonにおいて文字列はシーケンスに属します。
シーケンスは順序を持った配列なので、スライシング(特定の部分だけ取り出す)インデキシング(一つだけ取り出す)が可能です。

今回、行ったのはスライシングです。
2〜3文字目の文字を取り出して、表示してみました。

文字列のオブジェクトをUnicodeで扱うことも、文字列のオブジェクトの先頭にuをつけることで指定できます。
その上でスライシングも期待通り動作します

print u'こんにちは'[1:4]
# "んにち"が表示される。

mb_hogehoge()要らず“ですね。

文字列は不変性のオブジェクトです。
以下のような変更は出来ないので注意。

a = 'test'
a[0] = 'b'
# エラーが表示される。

文字列は便利なメソッドを色々持っています。
例えば文字列を置き換えるメソッド。

a = 'This is test.'
print a.replace('test', 'cat')
# This is cat.が表示される。
print a
# This is test.が表示される。'This is test.'のオブジェクトが書き変わった訳ではない。

これも’This is test.’の文字列オブジェクトが書き変わった訳ではないので注意が必要です。
‘This is test.’を書き換え、‘This is cat.’という新しい文字列オブジェクトをPythonは生成したのです

文字列はこれらが基本だと思います。

文字列のオブジェクトは”シーケンス“カテゴリで”不変性“であること。
これを覚えておくとばっちりです。

リスト

[1, 2, 3]

これがリストです。

リストは”配列“と考えて良いと思います。
リストの値にアクセスするには以下のコードを使います。

print [1, 2, 3][2]
# 3が表示される。
a = [1, 2, 3]
a[0] = 10
print a
# [10, 2, 3]が表示される。

もちろん、入れ子にも出来ます。

print [1, 2, [3, 4, 5]][2]
# [3, 4, 5]が表示される。
a = [1, 2, [3, 4, 5]]
a[2][0] = 10
print a
# [10, 2, [10, 4, 5]]が表示される。

リストは”シーケンス“カテゴリに属します。
文字列と同じく、スライシングやインデキシングの操作が出来ます。

print [1, 2, [3, 4, 5]][1:3]
# [2, [3, 4, 5]]が表示される。

リストは”可変性“です。
オブジェクトに対して変更を加えることが出来ます。
appendやpop、removeといったメソッドを持っており、オブジェクトに対してこれらの操作を実施します。

a = [1, 2, 3]

a.append(4)
print a
# [1, 2, 3, 4]が表示される。

a.remove(1)
print a
# [2, 3, 4]が表示される。

a.pop()
print a
# [2, 3]が表示される。

print a.append(5)
# [2, 3, 5]ではなく、Noneが表示される。元々存在するオブジェクトに対する操作のため、返り値は、他の言語のNULLに相当するNoneが返る。

リストのオブジェクトは”シーケンス“カテゴリで”可変性“です。
文字列のオブジェクトと比較してみてください。
それで、さらに理解が深まるかと。

ディクショナリ

{‘cat’:'neko’, ‘dog’:'inu’, ‘spam’:'unknown’}

これがディクショナリです。

ディクショナリは”写像“というカテゴリに属するため、リストや文字列とは操作が異なります
例えば、リストや文字列で行ったスライシングの操作をディクショナリで行うとエラーが表示されます。

{'cat':'neko', 'dog':'inu', 'spam':'unknown'}[1:3]
# エラーが表示される。

{'cat':'neko', 'dog':'inu', 'spam':'unknown'}['cat':'dog']
# エラーが表示される。

逆にリストでは出来なかった操作が出来ます。
文字列キーの使用と、存在しないキーへの代入です。

print {'cat':'neko', 'dog':'inu', 'spam':'unknown'}['cat']
# nekoが表示される。

a = {'cat':'neko', 'dog':'inu', 'spam':'unknown'}
a['new'] = 'atarashii'
print a
# {'new': 'atarashii', 'spam': 'unknown', 'dog': 'inu', 'cat': 'neko'}が表示される。

リストと同じく、ディクショナリも”可変性“のオブジェクトなので、オブジェクトに対して代入が出来ています。

さらに”不変性“のオブジェクトであればどんなものでも、ディクショナリのキーに設定出来ます。
例えば、タプルをキーに設定出来ます。

print {(1, 2, 3):'tuple', 'dog':'inu', 'spam':'unknown'}[(1, 2, 3)]
# tupleが表示される。

かなり複雑な構成もディクショナリとタプルの組み合わせで実現出来そうですね。

ディクショナリのメソッドとして、PHPのarray_keys()にあたるkeys()や、array_values()にあたるvalues()があります。
また、PHPではキーチェックはisset()で行うのですが、Pythonではディクショナリに対してhasKey()メソッドが使うことで実現出来ます。

ディクショナリだけ、リストや文字列とちょっと違う”可変性”のオブジェクトだと覚えておくと良いかもしれません。

タプル

(1, 2, 3)

これがタプルです。

タプルは”シーケンス“カテゴリに属する”不変性“のオブジェクトです。
ここまでしっかり理解出来た方であれば、何が出来るか想像出来ると思います。

ソースコードの例だけ書いておくので、後は省略させてください。

a = (1, 2, 3)
a[0] = 10
# エラーが表示される。

print (1, 2, 3)[0]
# 1が表示される。

print (1, 2, 3)[1:3]
# (2, 3)が表示される。

ビルトインオブジェクトはこんなところですね。
初めてのPythonにはもっとたくさんのことが書いてあるのですが、基礎中の基礎をまとめてみました。

Pythonのチュートリアルが終わったらもう一度、II部を読まないとダメかな。

初めてのPython 第3版 初めてのPython

  • 著者: Mark Lutz (著), 夏目 大 (翻訳)
  • 出版社: オライリージャパン
  • 発売日: 2009/02/26

ホーム > Python

スポンサードリンク
書いている人
つぶやき
  • setup.pyのinstall_requiresにgitのリポジトリを指定したい。どうすればいいかな。 1 day ago
  • ツイッタークライアントを久々に開いたけど、なんか違和感。 3 days ago
  • 最近、イベント参加出来てないな(_ _) 3 days ago
  • More updates...
RSS 気になるニュース
過去の記事

ページの上部に戻る