「Django(の名前が)カッコイイ!」という事でPythonを始めてみました。
- あくまで個人的に始めたということです
- 本文中のサンプルコードなどは全てPython2.4で実行しています

  • Pythonはじめました
    • 入門書は『初めてのPython』
    • ほか、情報源
  • それでは始めましょう
    • お好みでどうぞ
    • ほかにも
    • ドキュメントを読みましょう
    • 自作モジュールにも使えます
  • 中の事を少し
    • 実装
    • 例えばCPython
    • JIT(Just In Time)コンパイラ
    • フローズンバイナリ
  • それではダイジェストでどうぞ

    • ステートメント
    • 代入ステートメント
      • memo
      • memo
    • ifステートメント
    • whileループとforループ
      • ループ内で利用出来るステートメント
    • forループとイテレータとジェネレータ
    • 全てはオブジェクト
    • 不変性
    • オブジェクト指向とは関係ありません
    • 関数やクラスを書く順序
    • ビルトインオブジェクト
    • 型とリテラル
    • たぷる?
    • ガーベージコレクションとキャッシュ
    • おまけ
    • 演算について
    • インクリメントとデクリメント
    • フックメソッドを利用した演算のオーバーライド
    • 演算と型
    • スコープ
    • 関数まわり
    • 引数
    • デコレータ構文
    • クロージャ
    • クラス
    • 多重継承
    • classmethod, staticmethod
    • ネームマングリング
    • モジュール
    • リロード
    • unittestを忘れずに
  • 挑戦は続く

Pythonはじめました

「Django(の名前が)カッコイイ!」という事でPythonを始めてみました。
- あくまで個人的に始めたということです
- 本文中のサンプルコードなどは全てPython2.4で実行しています

入門書は『初めてのPython』

これを読めば間違いないと思います。
- Amazon.co.jp: 初めてのPython 第2版: 本: マーク ルッツ,デイビッド アスカー,Mark Lutz,David Ascher,夏目 大

ほか、情報源

それでは始めましょう

"""
http://www.ruby-lang.org/ja/
"""
class Greeter:
    def __init__ (self, name):
        self.name = name.capitalize()

    def salute(self):
        print 'Welcome to the world of the indent, %s!' % self.name

g = Greeter("feedforce")
g.salute()

お好みでどうぞ

ワンライナー
$ python -c 'print "Oneliner!"'
Oneliner!
インタラクティブシェル
$ python
>>> print 'Interactive shell!'
Interactive shell!
ファイルを実行
$ python sample.py

ほかにも

ドキュメントを読みましょう

pydocコマンドを利用してヘルプドキュメントを表示する事が出来ます。

$ pydoc urlparse
$ pydoc urlparse.urlparse
$ pydoc -k url

pydocには、HTMLドキュメントを書き出したり、ドキュメントサーバを立ち上げるなどの機能もあります。

自作モジュールにも使えます

sample.py
"""
doc for file
"""

class C:
    """
    doc for class
    """

 def method(self):
        """
        doc for method
        """
        return 0

def func():
    """
    doc for function
    """
    return 0

if __name__ == ' __main__':
    """
    doc for main
    """
    print 'main'
~$ pydoc sample

NAME
    doc - doc for file

FILE
    /Users/mamutaro/work/note/study/doc.py

CLASSES
    C

    class C
     | doc for class
     |  
     | Methods defined here:
     |  
     | method(self)
     | doc for method

FUNCTIONS
    func()
        doc for function

中の事を少し

実装

  • CPython
  • Jython
  • Python.NET
  • など

例えばCPython

CPythonはいわゆるPythonです。Linuxディストリビューションに標準インストールされるようなPythonはCPythonでしょう。

CPythonでは、実行に先立ちソースコードをバイトコンパイルし、出来上がったバイトコードをPVM(Python Virtual Machine)に送り、PVMによってプログラムが動作します。

JIT(Just In Time)コンパイラ

PsycoというJITコンパイラが存在するようです。詳しくありませんので、詳細は省きます。
- Psyco - Home Page
- ジャストインタイムコンパイル方式 - Wikipedia

フローズンバイナリ

単独で実行可能な形式にまとめる事が出来るツールを利用出来ます。
- py2exe (Windows 実行形式に変換)
- PyInstaller - Trac
- Repository - directory - projects: python/trunk/Tools/freeze

それではダイジェストでどうぞ

ステートメント

代入ステートメント

Pythonでは値が代入されていない変数を利用する事は出来ません。利用しようとした場合、NameError例外がスローされます。

代入は=だけで行われるわけではなく、forループや関数・クラスの定義や関数の引数などでは自動的に代入が行われます。

>>> x = 10
>>> y, z = 100, 1000
>>> x, y, z
(10, 100, 1000)
>>> L = [0, 1, 2]
>>> [a, b, c] = L
>>> a, b, c
(0, 1, 2)

memo

Pythonの変数は、C言語のポインタのような働きをします(void型ポインタ, 汎用ポインタ)。

代入によってオブジェクトへのリファンレンスが作られ、それが変数に割り当てられます。

memo

Pythonでは代入は式ではなくステートメントです。このため、代入は値を返しません。つまり、以下のような記述は許されていません。

<?php

$fp = fopen( __FILE__ , 'r');
while (($str = fgets($fp))) {
    print $str;
}

?>

ifステートメント

if a:
    ...
elif b:
    ...
else:
    ...
ブール演算
  • 0、空のオブジェクト、Noneを偽と解釈する
  • 上記以外は真と解釈する
  • データ構造の比較はその構成要素1つ1つに付いて行われる
(1, 2, 3) and (0, 1, 4)
>>> 1 and 'a' and (1)
1
>>> not 0 and not '' and not () and not None
True
>>> (1, 2, 3) and (1, 2, 3)
(1, 2, 3)
>>> (1, 2, 3) and (0, 1, 4)
(0, 1, 4)

whileループとforループ

while cond:
    ...
else:
    ... # elseは省略可能; breakによって終了された場合は実行されない

for n in seq:
    ...
else:
    ... # elseは省略可能; breakによって終了された場合は実行されない
ループ内で利用出来るステートメント
  • break
    • ループを強制終了させる
  • pass
    • なにもしない
  • continue
    • そのループの見出しに戻る
forループとイテレータとジェネレータ

forループで利用するシーケンスオブジェクトはイテレータプロトコルをサポートしている必要があります。

>>> def gen_cube(length):
... for n in range(length):
... yield n ** 3
... 
>>> g = gen_cube(4)

>>> g.next()
0
>>> g.next()
1
>>> g.next()
8
>>> g.next()
27
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
StopIteration
>>> for i in gen_cube(5):
... print i, ':',
... 
0 : 1 : 8 : 27 : 64 :

returnでなくyieldを利用する事で、yieldのたびに処理を停止して値を関数の呼び出し元に渡します。yieldを含む関数が呼び出されると、イテレータプロトコルをサポートする(next()メソッドを持つ)ジェネレータオブジェクトを返します。

全てはオブジェクト

Pythonの世界では全てがオブジェクトです。数値も文字列も配列も関数もクラスもオブジェクトです。

不変性

Pythonには、値を変更出来ないオブジェクトと変更可能なオブジェクトがあり、前者の性質を不変性と呼ぶようです。

数値や文字列、タプルなどは不変性を持ち、リストは値を変更する事が出来ます。

>>> l1 = [1, 2]
>>> l2 = l1

>>> l1[1] = 0
>>> l1, l2
([1, 0], [1, 0])
>>> t1 = (1, 2)
>>> t1[0] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: object does not support item assignment

オブジェクト指向とは関係ありません

全てがオブジェクトで表現されますが、オブジェクト指向のための言語というわけではないようです。

例えば、(厳密な)アクセスコントロールによるカプセル化は出来ません(それらしく振る舞う事は出来るようですが)。

関数やクラスを書く順序

Pythonでは、関数やクラスが登場する位置が意味を持ちます。

例えば、PHPの場合。

<?php

func();

function func()
{
    echo "function\n";
}

?>

上記のように、関数呼び出し後に関数を定義する事が許されています。

一方、Pythonの場合。

OK
def func():
    print 'function'

func()
NG
func()

def func():
    print 'function'

上記のように、先に関数を記述しておかなければ呼び出す事は出来ません。これは、クラスも同様です。利用しようとしている時点では、オブジェクトが存在していないので当然の結果ではありますが、少々戸惑います。

ビルトインオブジェクト

型とリテラル

# 数値
n_short = 100
n_long = 10000000000000000000L
n_double_1 = 1.00
n_double_2 = 1.2e-2
n_oct = 0123
n_hex = 0x1a
n_complex = 3j
# 文字列
s1 = '\tsingle quote'
s2 = "\tdouble quote"
s3 = """\ttriple quote""" + '''\ttriple quote'''
s4 = r'\traw string\n'
s5 = u'unicode'
# タプル
T = (1, 2, 3, (0, 100,))
# リスト
L = [1, 2, 3, [3, 4,]]
# ディクショナリ
D = {'a': 1, T: {}, 100: L}
# ファイル
F = open(path, mode)

たぷる?

固定長の不変性を持つ配列と考えれば良いでしょうか。不変性を持つので、辞書型のキーとして利用可能です。
- 辞書型のキーには、不変性を持つオブジェクトのみ利用可能です

>>> x, y, z = (1, 2, 3)
>>> D = {(1, 2, 3): 100, (4, 5, 6): 200}

>>> print D[(x, y, z)]
100

ガーベージコレクションとキャッシュ

Pythonではガーベージコレクションの機能が用意されているため、どこからも参照されなくなったオブジェクトの為に確保されているメモリ領域は自動的に解放されます。

一方で、桁数が少ない数値や文字数が少ない文字列などはキャッシュして再利用する仕組みもあります。

おまけ

数値は続くよ、どこまでも

整数や実数といった表現は自動的に判断されます。また、長さ(精度)についても同様です。

長整数(long)はメモリ容量が許す限りの長さを表現する事が出来ます。

循環オブジェクト

Pythonでは、自身へのリファレンスをを要素とするコレクションオブジェクトを循環オブジェクトと呼びます。

>>> L = [1,2,3]
>>> L.append(L)
>>> L
[1, 2, 3, [...]]

出力の際に無限ループに陥る事はありません(Python1.5.1以降)。

set型

Python2.4から集合型としてsetを利用出来るようです。

>>> L1 = [1, 2, 3, 4, 5, 5]
>>> L2 = [3, 4, 5]
>>> U = set(L1)
>>> U
set([1, 2, 3, 4, 5])

>>> U.intersection(L2)
set([3, 4, 5])
>>> U.difference(L2)
set([1, 2])
リスト内包表記とジェネレータ式
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> (x**2 for x in range(10))
<generator object at 0x608f0>

演算について

インクリメントとデクリメント

++や--によるインクリメント/デクリメントを行う事は出来ません。

>>> n = 1
>>> n++
  File "<stdin>", line 1
    n++
      ^
SyntaxError: invalid syntax
>>> n += 1

>>> n
2
>>> n--
  File "<stdin>", line 1
    n--
      ^
SyntaxError: invalid syntax
>>> n -= 1
>>> n
1

フックメソッドを利用した演算のオーバーライド

フックメソッドと呼ばれる特別なメソッドをオーバーライドする事で、そのクラスオブジェクトの演算処理を変更する事が出来ます。


>>> class C:
... def __init__ (self, v):
... self.v = v
... def __add__ (self, v):
... return self.v - v
... 
>>> n = C(100)
>>> n + 30
70
演算と型

演算は対象オブジェクトの型によって振る舞いが変わります。

>>> 10 + 100
110
>>> 'abc' + 'xyz'
'abcxyz'

>>> 'aiueo' + 100
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects
>>> 10 * 3
30
>>> 'a' * 10
'aaaaaaaaaa'

スコープ

  1. ビルトインスコープ

    1. __builtin__モジュールのスコープ
  2. グローバルスコープ

    1. モジュールのトップレベルのスコープ
  3. 外側の関数のスコープ(ネストスコープ)

    1. 関数がネストされた際の、最内以外のスコープ
  4. ローカルスコープ

    1. 最内の関数のスコープ
>>> global_scope = 100
>>> 
>>> def outside_func():
... nest_scope = global_scope
... def inside_func():
... local_scope = -1
... print nest_scope
... inside_func()
... print local_scope
... 
>>> outside_func()
100
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 7, in outside_func
NameError: global name 'local_scope' is not defined

関数まわり

引数

>>> def func(a, b=0, *args, **kargs):
... print ' a=', a
... print ' b=', b
... print ' args=', args
... print 'kargs=', kargs
... 
>>> func('x')
    a= x
    b= 0
 args= ()
kargs= {}
>>> func('y', 100, 200, 300, c='?', d=[])
    a= y
    b= 100
 args= (200, 300)
kargs= {'c': '?', 'd': []}
>>> func(b=1000, a='foo')
    a= foo
    b= 1000
 args= ()
kargs= {}
  • 位置と名前によって引数のマッチングが行われます
  • *や**を利用して、可変個の引数を扱う事が出来ます
    • *を利用した場合、該当箇所の引数がタプルで格納されます
    • **を利用した場合、該当箇所のキーワード引数がディクショナリで格納されます
    • *や**は通常の引数より後ろに記述し、*の後に**がくるようにします

デコレータ構文

Python2.4から、デコレータ構文が導入されました。
- Python 2.4 クイックリファレンス
- ITmedia エンタープライズ:2.4への機能強化で広がるPythonの世界 (3/4)

>>> def deco_a(f):
... print 'deco_a'
... return f
... 
>>> def deco_b(f):
... print 'deco_b'
... return f
... 
>>> def deco_c(f):
... print 'deco_c'
... return f
... 
>>> @deco_a
... @deco_c
... @deco_b
... def func(x):
... return x ** 2
... 
deco_b
deco_c
deco_a
>>> print func(2)
4

@<関数名> をデコレート対象関数の直前の行に記述する事でその関数のデコレータとする事が出来ます。デコレータは複数適用させる事が出来ます(下から順に適用)。

デコレータ関数自身を関数を返すラッパーとしている場合、デコレータ構文で引数を指定する事が出来ます。
- 注)誤った解釈をしているかもしれません
- @funcはfuncオブジェクトをデコレータに指定している
- @func(n)はfunc(n)の結果として返ってくる関数オブジェクトをデコレータに指定する事になる
- デコレータが実行される時には引数にデコレート対象の関数オブジェクトが渡される

以下は、デコレート対象の関数をデコレータによって置き換える際の一例です。

01: >>> def decorator(*d_args, **d_kargs):
02: ... print 'decorator', d_args, d_kargs
03: ... def director(target_func):
04: ... print 'replacement'
05: ... def wrapper(*args, **kargs):
05: ... return 'ans = %d' % target_func(*args, **kargs)
07: ... wrapper.func_name = target_func.func_name
08: ... return wrapper
09: ... return director
10: ... 
11: >>> @decorator(1, 's', key='value')
12: ... def func(x):
13: ... return x * 2 + 100
14: ... 
15: decorator (1, 's') {'key': 'value'}
16: replacement
17: >>> print func(3)
18: ans = 106
19: >>> print func(100)
20: ans = 300

  1. デコレータ関数はデコレート対象の関数が作成される際に実行されます

    1. 15,16行目は02行目の実行結果
    2. この時点で、デコレート対象の関数が置き換えられました
  2. 17,19行目で関数を実行しています

    1. decoratorによって置き換えられた関数の結果が出力されています
    2. 今回はフォーマットを整えてみました

クロージャ

>>> def func1():
... in_func1 = 100
... def func2():
... print in_func1
... return func2
... 
>>> action = func1()
>>> action()
100

Pythonでは「内側の関数で外側の関数の変数が有効になる」というルールが存在しています。これは、外側の関数の処理がreturnによって終了していても適用されます。

ラムダ式
>>> f = lambda arg: arg**2
>>> f(100) 
10000

ラムダ式は であるため、defステートメントが利用出来ない場所でも利用可能です。また、lambda式にもスコープが存在しているため、lambda式を利用したクロージャを書く事が出来ます。

>>> def func():
... x = 100
... return (lambda n: n * x)
... 
>>> f = func()
>>> f(2)
200

クラス

>>> class C:
... def __init__ (self, a):
... self.a = a
...     
... def action(self):
... print 'a=%s' % self.a
... 
>>> C('class').action()
a=class
  • classステートメントを利用してクラスオブジェクトを作成する事が出来ます
  • メソッドの第1引数には自動的に自身を指す変数が割り当てられます
    • 慣例的に変数名selfを指定するようです

多重継承

Pythonでは複数の親クラスからなるクラスを作成する事が出来ます。

class Mixed(C1, C2):
    ...

親クラスの並びには意味があり、属性をオブジェクトツリーから検索する際に左にあるクラスが優先されます。

classmethod, staticmethod

>>> class C:
... def instance_method(self):
... print self
...     
... @classmethod
... def class_method(cls):
... print cls
...     
... @staticmethod
... def static_method():
... print 'static'
... 
>>> c = C()
>>> c.instance_method()
< __main__.C instance at 0x60a08>
>>> c.class_method()
__main__.C
>>> C.static_method()
static

  • classmethodやstaticmethodは関数を引数にとるデコレータ関数です
  • classmethodは第1引数がインスタンスではなくクラスになります
  • staticmethodは第1引数にインスタンスが渡らなくなります

ネームマングリング

classステートメント中で、__から始まる属性(終わりにはつけない)は、ある規則で名前が置き換えられます。

>>> class C:
... def __init__ (self, a, b):
... self.__a = a
... self.b = b
...     
... def public_method(self):
... print 'public: ', self.__a, self.b
...     
... def __private_method(self):
... print 'private: ', self.__a, self.b
... 

>>> c = C(1, 2)
>>> c.public_method()
public: 1 2
>>> c.__private_method()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: C instance has no attribute '__private_method'
>>> c._C__private_method()
private: 1 2
>>> c.b
2
>>> c.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: C instance has no attribute '__a'
>>> c._C__a
1

  • クラスXの__xという属性名は_X__xに自動的に置き換えられます
    • 外部から__xという属性名でアクセスする事が出来なくなります
    • 擬似的なprivateとして考えることも出来ますが、本質的には『名前が変わる』機能です
    • 継承ツリー内での名前の衝突を防ぐことが出来ます
>>> class D(C):
... def public_method(self):
... self._C__private_method()
... self.__private_method()
... 
>>> d = D(3, 4)

>>> d.public_method()
private: 3 4
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 4, in public_method
AttributeError: D instance has no attribute '_D__private_method'

モジュール

以下のような使い方をする場合があります。

>>> import urlparse
>>> urlparse.urlparse('http://tech.feedforce.jp/')
('http', 'tech.feedforce.jp', '/', '', '', '')

urlparse.urlparse()に違和感を感じる場合、以下のように書く事も出来ます。

>>> from urlparse import urlparse
>>> urlparse('http://tech.feedforce.jp/')
('http', 'tech.feedforce.jp', '/', '', '', '')

Pythonではモジュールごとに名前空間(グローバルスコープ)が分かれるため、単純なimportの場合にモジュール名から始まる絶対パスでモジュールの属性にアクセスしなければなりません。

リロード

reloadステートメントを利用する事で、実行中にモジュールを再読み込みする事が出来ます。

unittestを忘れずに

Pythonでは、unittestというユニットテストフレームワークが標準モジュールとして付属しています。

pydocより
Simple usage:

 import unittest

 class IntegerArithmenticTestCase(unittest.TestCase):
        def testAdd(self): ## test method names begin 'test*'
            self.assertEquals((1 + 2), 3)
            self.assertEquals(0 + 1, 1)
        def testMultiply(self):
            self.assertEquals((0 * 10), 0)
            self.assertEquals((5 * 8), 40)

 if __name__ == ' __main__':
        unittest.main()

挑戦は続く

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

新しいブログへ移行しました!こちらもよろしくお願いします。