Hatena::Grouppython

HM python

Pythonについて覺えたことや疑問に思ったこと、及び參考にしたリソースをメモして行く感じで。面倒なのでマーク附けは最小限に。

2006-06-01

PythonでW3C-DTF……はちと面倒かも……否、さうでもなかった

W3C-DTFな文字列が欲しいな、と思った。W3C-DTFと一口に言っても幾つかあるけど、欲しいのは次の形:

  • YYYY-MM-DDThh:mm:ss.mm+HH:MM
  • 例: 2006-06-01T23:17:36+09:00

datetimeモジュールdatetimeオブジェクトにはisoformat()がある。W3C-DTFはISO 8601互換だから、これで行けるかな。

isoformat([sep])
日付と時刻を ISO 8601 形式、すなわち YYYY-MM-DDTHH:MM:SS.mmmmmm か、microsecond が 0 の場合には YYYY-MM-DDTHH:MM:SS で表した文字列を返します。 utcoffset() が None を返さない場合、 UTC からのオフセットを時間と分を表した (符号付きの) 6 文字からなる 文字列が追加されます: すなわち、YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM となるか、 microsecond が ゼロの場合には YYYY-MM-DDTHH:MM:SS+HH:MM となります。(後略)
4 datetime オブジェクト

ふむ。試してみよう。

from datetime import datetime

print datetime.now().isoformat()

實行例:

2006-06-01T21:21:43.537281

むー。datetime.datetime.now()は現在のローカルな日付および時刻*を返すとなってゐるのに、肝腎のタイムゾーンが無い。

from datetime import datetime

print datetime.now().utcoffset()
print datetime.now().tzinfo

實行例:

None
None

W3C-DTFでは時刻まで記述する場合タイムゾーン指定子(TZD)が必須なのでこれでは困る。ローカルな日付および時刻*なのになんでタイムゾーンを自動的にセットしてくれないんだらう、と思ったら、Python ライブラリリファレンス 10 datetime -- 基本的な日付型および時間型に次のやうに:

datetime モジュールでは具体的な tsinfo クラスを提供していないので注意してください。必要な詳細仕様を備えたタイムゾーン機能を提供するのはアプリケーションの責任です。世界各国における時刻の修正に関する法則は合理的というよりも政治的なものであり、全てのアプリケーションに適した標準というものが存在しないのです。

10 datetime -- 基本的な日付型および時間型

アプリケーションの責任て言はれてもなあ。6 tzinfo オブジェクトをぱっと見た感じ、面倒臭い

とりあへず、UTCで我慢しようか。UTCの場合のTZDは Z なのでtzinfo オブジェクトを云々しなくても文字列を足すだけで良く簡單。UTCの現在時刻はdatetime.datetime.utcnow()で取得出來る:

from datetime import datetime

print datetime.utcnow().isoformat() + 'Z'

實行例:

2006-06-01T12:25:15.777536Z

2006-06-02追記:

pythonグループ - faerieの日記 - JSTを讀んで勘違ひに氣づきました。どういふ勘違ひをしてゐたかといふと――

  • datetime.datetime.now(tz)は、タイムゾーン情報は持つが時刻自體はシステムのタイムゾーン設定に基づいたものを返す……と思ってゐた。

それで面倒臭いとか言ってゐたんだけど、そんなわけはないよねえ。tzinfoオブジェクトを渡せばシステムの設定に關係無くtzinfoに基づいた時刻を返すと思ふのが普通だらうに、なんでこんな變な思ひ込みをしたんだらう。つふかdatetime.datetime.now()に引數tzを渡した場合現在の日付および時刻は tz のタイムゾーンに変換されます4 datetime オブジェクトてちゃんと書いてあるのに、良く讀め自分。

とにかく、それだったら然程面倒ではない。pythonグループ - faerieの日記 - JSTのコードを使って早速確認:

# file learningpython.py

import datetime

class JST(datetime.tzinfo):
    def utcoffset(self, dt):
        return datetime.timedelta(hours=9)
    def dst(self, dt):
        return datetime.timedelta(0)
    def tzname(self, dt):
        return "JST"

print datetime.datetime.now().isoformat()
print datetime.datetime.now(JST()).isoformat()

datetime.tzinfoクラスのサブクラスの名前は、なるほど、タイムゾーン名にするとnow()の呼出しが綺麗に書けますね。さてこれを、タイムゾーンをUTCに設定した状態の環境で實行してみると:

% LANG=C date && python learningpython.py
Fri Jun  2 02:40:13 UTC 2006
2006-06-02T02:40:13.632184
2006-06-02T11:40:13.632934+09:00

おお。これですよこれ、欲しかったのは。どうもありがたうございます。

參考:

PythonでW3C-DTF 邪道篇

import os

print os.popen('ruby -r time -e "puts Time.now.iso8601(2)"').read().strip()

實行例:

2006-06-01T21:25:35.49+09:00

參考:

2006-05-31

モジュールとimport文

Rubyではモジュールをモジュール定義式Rubyリファレンスマニュアル - クラス/メソッドの定義で定義するが、Pythonでは一つのファイルが一つのモジュールに對應し、ファイル名の擴張子を除いた部分モジュール名となる。この邊、單純明解で良い感じ。

モジュールを取込むのにはimport文を使ふ。たとへば次のプログラムをmymodule.pyといふ名前で保存したとする。

# file mymodule.py

foo = 'foo'

def bar():
    print 'bar'

これを、たとへば同じディレクトリの別のファイルmymain.pyから取込んで使ふには、次のやうにする。

# file mymain.py

import mymodule

print mymodule.foo
print mymodule.bar
mymodule.bar()

mymain.pyの實行結果:

foo
<function bar at 0x4021cd4c>
bar

以下覺書。

import module
モジュールmoduleを取込む。取込んだモジュール内のオブジェクトには module.name といふ形式でアクセス出來る。
module はモジュールのファイル名から擴張子を取除いた物。擴張子が .py 又は .pyc でないファイルはimport出來ないみたい。moduleはコンマで區切ることで複數記述出來る。
import sys, re
import sys
import re
と(多分)同じ。
from module import name
moduleからnameを取込む。
例:
>>> from xml import dom
>>> dom.getDOMImplementation()
<xml.dom.minidom.DOMImplementation instance at 0x404f9d2c>
import name as localename
nameを別名localenameとして取込む。
例:
>>> import Tkinter as Tk
>>> Tk.Button
<class Tkinter.Button at 0x4058a23c>
別名で取込むと、オリジナルの名前は定義されない:
>>> import Tkinter as Tk
>>> Tkinter
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'Tkinter' is not defined

モジュールに關しては他にも色々あるみたい(サブモジュール云々とか __all__ とか)だけど、とりあへずこの邊で。

參考:

re.sub()――正規表現による文字列置換

re.sub()は、第三引數の文字列の内、第一引數のパターン(正規表現)に一致した部分を第二引數の文字列に置換する。第四引數は置換する囘數。省略した場合一致した箇所を全部置換する。

import re

s = 'foofoofoo'
r = re.compile('foo')
print re.sub(r, 'bar', s)
print re.sub(r, 'bar', s, 2)
print re.sub(r, 'bar', s, 1)

實行結果:

barbarbar
barbarfoo
barfoofoo

參考:

Pythonの正規表現の ^ と $ と MULTILINEモード

Pythonの正規表現の ^ は通常、「文字列の先頭」に一致し、「改行の直後」には一致しない。(Rubyでは同じ正規表現が「文字列の先頭」と「改行の直後」どちらにも一致する)。

import re

print re.sub('^', '#', '\nfoo\nfoo\n')

實行結果:

#
foo
foo

Rubyのそれのやうに「改行の直後」にも一致させるには、正規表現をMULTILINEモードにする。次の例ではre.compiile()の第二引數で指定してゐる。

import re

r = re.compile('^', re.MULTILINE)
print re.sub(r, '#', '\nfoo\nfoo\n')

re.MULTILINEre.M でも良い。以下實行結果:

#
#foo
#foo
#

正規表現となる文字列にモードの指定を埋込む事も出來る。

import re

print re.sub('(?m)^', '#', '\nfoo\nfoo\n')

實行結果:

#
#foo
#foo
#

改行は三つなのに # が四つになるのは、「文字列の先頭」にも一致してゐるから。文字列先頭が一つと改行の直後が三つ、計四箇所が置換されたといふこと。

$ は通常、「文字列の末尾」及び「末尾の改行の直前」に一致する。「末尾の改行の直前」以外の「改行の直前」には一致しない。

import re

print re.sub('$', '#', '\nfoo\nfoo\n')

實行結果:


foo
foo#
#

その他全ての「改行の直前」にも一致させたい場合は、MULTILINEモードの正規表現を使ふ。

import re

print re.sub('(?m)$', '#', '\nfoo\nfoo\n')

實行結果:

#
foo#
foo#
#

參考:

2006-05-30

組込みのクラスにメソッドを追加とか再定義とかしたかったらしい

Rubyで言ふところの

class String
  def f
    p self
  end
end

みたいなこと(組込みクラスへのクラスの定義の追加Rubyリファレンスマニュアル - クラス/メソッドの定義)がしたい。new.instancemethod()を使へば出來るかも……?

>>> import new
>>>
>>> def f(self):
...     print self
...
>>> str.f = new.instancemethod(f, None, str)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: can't set attributes of built-in/extension type 'str'

出來なかった。殘念。

參考

2006-05-29

函數定義

函數はdef文で定義する。Rubyも函數定義はdefなのでちょっと親近感。

def hello(arg = 'world!'):
    print 'Hello,', arg

hello('Python!')
hello()

實行結果:

Hello, Python!
Hello, world!

まだ定義されてゐない函數を呼ぶことは出來ない。

hello('Python!')

def hello(arg = 'world!'):
    print 'Hello,', arg

實行結果:

Traceback (most recent call last):
  File "learningpython.py", line 1, in ?
    hello('Python!')
NameError: name 'hello' is not defined

參考:

正規表現――reモジュール

正規表現であれこれするにはreモジュールをimportしてあれこれする。正規表現のリテラルは無いみたい。ちと吃驚。

import re

s = '2006-05-29'
r = re.compile('([0-9]{4})-([0-1][0-9])-([0-3][0-9])')
m = re.search(r, s)
if m:
    print 'Year:', m.group(1)
    print 'Month:', m.group(2)
    print 'Day:', m.group(3)

實行結果:

Year: 2006
Month: 05
Day: 29

re.search()はマッチに成功するとMatchObjectのインスタンスを、マッチに失敗した場合は None を返す。

正規表現を使ひ囘す必要が無ければ、いちいちコンパイルせずに、正規表現關聯の函數に文字列で渡しても良い。

re.search('([0-9]{4})-([0-1][0-9])-([0-3][0-9])', s)

といふ具合。

關聯記事:

正規表現はraw stringで書かう

raw stringは文字列リテラルの頭にr(又は大文字でR)を附けた文字列のこと。raw stringでは殆どのバックスラッシュが特別な意味には解釋されない(つまり r'\n' は改行文字ではなく \n といふ文字列となる)。

raw stringは正規表現を書くのに都合が良い。といふか正規表現はraw stringで書いたはうが良い。

import re

s = '\\n'
print s
print re.search('^\\n$', s)
print re.search('^\\\\n$', s)
print re.search(r'^\\n$', s)

實行結果:

\n
None
<_sre.SRE_Match object at 0x402232f8>
<_sre.SRE_Match object at 0x402232f8>

'\\n' は文字列リテラルなのでバックスラッシュが解釋されて\n となる。\n は正規表現では改行にマッチするメタ文字だ。つまり re.search('\\n', s)sに對して改行を檢索してゐるが、この例では s に改行は含まれてゐないのでマッチせず None が返る。バックスラッシュにマッチさせるには文字列リテラルにおけるバックスラッシュの意味と、正規表現でのバックスラッシュの意味の兩方をエスケープする必要がある。そのためバックスラッシュにマッチする正規表現は通常の文字列リテラルだと'\\\\' なんて書かなければならない。が、raw stringならバックスラッシュに特別な意味は附加されないので、エスケープするのは正規表現のバックスラッシュだけで良い。――といふ按排。

參考:

3 * 'a'

[ruby-list:19387] Re: オブジェクトの代入について:

Pythonでも文字列の結合は+ですが、"a"*3 == 3*"a" == "aaa" になってますね。

[ruby-list:19387

へぇ×3

>>> 3 * 'Hee'
'HeeHeeHee'

ふむふむ……。

3 * 'a' が出來るなら 3 + 'a' も、と思ふのが人情といふもの(さうなの?):

>>> 3 + 'Hee'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unsupported operand type(s) for +: 'int' and 'str'

あー、やっぱり駄目?

2006-05-23

代入文は値を返さない

import sys

while line = sys.stdin.readline():
    print line,

このプログラムは文法違反でエラーとなる:

  File "learningpython.py", line 3
    while line = sys.stdin.readline():
               ^
SyntaxError: invalid syntax

Pythonの代入文は値を返さないので(正確には「Pythonの代入は式ではなく文なので」と言ふのかな?)、if文やwhile文の條件には出來ない。上記プログラムは(while文を使ふなら)次のやうに書く必要がある。

import sys

line = sys.stdin.readline()
while line:
    print line,
    line = sys.stdin.readline()

或いは:

import sys

while True:
    line = sys.stdin.readline()
    if line:
        print line,
    else:
        break

こっちのはうが綺麗かな。

關聯記事:

2006-05-22

doctest――ドキュメンテーション文字列を元にテストコードを生成、實行する

Rubyの __END__ に相當するものはPythonには無いのかしらとググったら、The RWiki - スクリプト言語の比較::__END__ない。ただし各モジュール、クラス、関数の最初に記述するドキュメント文字列中に、利用例としてテストケースを記述し、自動テストを実施するdoctestという仕組みがある*とあった。

doctestについてはライブラリリファレンスの2 doctest -- 対話モードを使った使用例の内容をテストする、及びそのSubsections*以下に詳しく書かれてゐる。

さっそく……

# file fib.py

def fib(n):
    """Return the Fibonacci number of n.

    >>> for i in range(11):
    ...     print fib(i),
    ...
    0 1 1 2 3 5 8 13 21 34 55
    """
    if n <= 0:
        n = 0
    elif n == 1:
        n = 1
    else:
        n = fib(n - 1) + fib(n - 2)
    return n

if __name__ == '__main__':
    import doctest
    doctest.testmod()

函數やクラス内(或いはスクリプト)の最初に文字列があると、それはその函數やクラスのドキュメンテーション文字列(docstring)となる。doctestはこのドキュメンテーション文字列から對話モードの書式で書かれた部分を見つけ、それを元にテストを實行する。

上記例では

>>> for i in range(11):
...     print fib(i),
...

がテストされる部分で、その直後の

0 1 1 2 3 5 8 13 21 34 55

が期待される結果。

テストに適合した場合は何も出力されない。python fib.py -vのやうにスクリプトに-vオプションを渡して實行すると、テスト經過が表示される:

Running __main__.__doc__
0 of 0 examples failed in __main__.__doc__
Running __main__.fib.__doc__
Trying:
for i in range(11):
    print fib(i),
Expecting: 0 1 1 2 3 5 8 13 21 34 55
ok
0 of 1 examples failed in __main__.fib.__doc__
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.fib
1 tests in 2 items.
1 passed and 0 failed.
Test passed.

いいね。

標準入力から一行づつ讀込み、一行づつ出力したい

STDIN.each_line do |line|
  puts line.upcase
end

これはRubyスクリプト。實行するとこんな感じ(以降、^DCtrl+ Dを入力した箇所):

python
PYTHON
hoge
HOGE
^D

標準入力からの入力が一行ある度にエコーされる(この例では出力をupcaseさせてゐる)。EOF(Ctrl + D)で終了。これと同じことをPythonでやりたい。

とりあへず適當に書いてみた。

import sys

for line in sys.stdin:
    print line.upper(),

これを實行するとこんな感じ:

python
hoge
^DPYTHON
HOGE
^D

んー。一行入力毎に繰返してほしいのだけど。ファイルオブジェクトのイテレータはさうなってゐないんだらうか。をかしいな……と思ったらライブラリリファレンスの9 ファイルオブジェクトにこんな事が:

ファイル内の各行に対する for ループ (非常によくある操作です) を効率的な方法で行うために、next() メソッドは隠蔽された先読みバッファを使います。

9 ファイルオブジェクト

先讀み? ちゃんと一行づつ繰返してゐる? ……確認してみる。

import sys

lineno = 1
for line in sys.stdin:
    print lineno, ':', line.upper(),
    lineno += 1

實行結果:

a
b
c
^D1 : A
2 : B
3 : C
^D

lineno の値がちゃんと一行毎に増してゐる。慥かに(複數行まとめて line に代入してゐるのではなく)一行づつ繰返してゐるらしい。……でも今囘は一行入力したら直ぐに出力させたいのだから、このやうに出力に遲延が生じては困る訣で。さてどうしようか。

2006-05-23追記:

for文ではどうも上手く行かないぽいので、while文とreadline()メソッドを使ってみる。

import sys

line = sys.stdin.readline()
while line:
    print line,
    line = sys.stdin.readline()

これで目的は達せられる。でも、line = sys.stdin.readline()を二つ書かなければならないのがどうも落着かない。while line = sys.stdin.readline():て書きたいなあ。まあ、かういふインタラクティヴな出力が必要でなければ(そして大抵はさうだ)、for文で良いのだけど(關聯: 代入文は値を返さない)。

2006-05-25追記:

pythonグループ - faerieの日記 - Re: pythonグループ - 獨習Python - 標準入力から一行づつ讀込み、一行づつ出力したいよりつっこみを頂戴(ありがたうございます):

こうするとできます。

import sys
for line in iter(sys.stdin.readline, ""):
    print line,
pythonグループ - faerieの日記 - Re: pythonグループ - 獨習Python - 標準入力から一行づつ讀込み、一行づつ出力したい

なるほどー。iter()は第二引數があると、第一引數のオブジェクトを呼び出すイテレータを作るのね。終了する(例外StopIterationを投げる)條件は第一引數のオブジェクトを呼出した返値と第二引數が一致した場合、と。

1 組み込み関数:

iter(o[, sentinel])
イテレータオブジェクトを返します。2 つ目の引数があるかどうかで、最初の引数の解釈は非常に異なります。2 つ目の引数がない場合、 o は反復プロトコル (__iter__() メソッド) か、シーケンス型プロトコル (引数が 0から開始する __getitem__() メソッド) をサポートする集合オブジェクトでなければなりません。これらのプロトコルが両方ともサポートされていない場合、TypeError が送出されます。2 つ目の引数 sentinel が与えられていれば、o は呼び出し可能なオブジェクトでなければなりません。この場合に生成されるイテレータは、next() を呼ぶ毎に o を引数無し で呼び出します。返された値が sentinel と等しければ、 StopIteration が送出されます。そうでない場合、戻り値がそのまま返されます。バージョン 2.2 で 新たに追加された仕様です。
1 組み込み関数

關聯リンク

2006-05-21

コルーチン?

Pythonのジェネレータのやうなものはコルーチン(coroutine)と呼ばれるものらしい。Rubyも同じキーワードyieldを使って似たやうな事(反復)を(も)するが、しかし兩者のyieldはどうも本質的に異るみたい。

[Python-ml-jp 1990] Re: ネストしたgenerator:

yield 文は Ruby にもありますが,これは大雑把にいえば関数引数の呼び出しの単なるシンタックスシュガーです。Python のものは,いわば (知る人ぞ知る) CLU の正統後継者と言いましょうか,コルーチンを作ります。

[Python-ml-jp 1990

參考:

2006-05-20

フィボナッチ數列

おやくそく其の二。(其の一はHello, world!)

import sys

def fib(n):
    if n <= 0:
        n = 0
    elif n == 1:
        n = 1
    else:
        n = fib(n-1) + fib(n-2)
    return n

for n in range(20):
    print fib(n),
    sys.stdout.flush()

實行結果:

0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181

リンク:

ジェネレータとは何ぞや

pyletの日記 - ジェネレータ版を讀んで「ジェネレータとは何ぞや」といふ疑問。

とりあへずリンクのみ。

ちょっと長くなりさうなので見出しを分けた(まとめて讀む場合は2006-05-20を)。

イテレータ

9 クラス8 イテレータ (iterator)のあたり:

背後では、for 文はコンテナオブジェクトの iter() を呼び出しています。この関数は next() メソッドの定義されたイテレータオブジェクトを返します。 next() メソッドは一度コンテナ内の要素に一度に一つづつアクセスします。コンテナ内にアクセスすべき要素がなくなると、next() は StopIteration 例外を送出し、for ループを終了させます。

9 クラス
>>> i = iter([1, 2, 3])
>>> i
<listiterator object at 0x4021faac>
>>> print i.next()
1
>>> print i.next()
2
>>> print i.next()
3
>>> print i.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
StopIteration

for文は、イテレータオブジェクト(この例ではiに代入した物) を取得し、そのnext()メソッドを例外が投げられるまで繰返してゐる、といふことらしい。

イテレータでフィボナッチ數列

9 クラス 8 イテレータ (iterator):

自作のクラスに イテレータとしての振る舞いを追加するのは簡単です。__iter__() メソッド を定義して、next() メソッドを持つオブジェクトを返すようにしてください。 クラス自体で next() を定義している場合、__iter__() では 単に self を返すようにできます

9 クラス

クラスの作り方はまだよくわからないんだけど、とりあへず同文書にあるサンプルを元に見よう見まねで書いてみる。

class fibonacci:
    def __init__(self, remit):
        self.a = 0
        self.b = 0
        self.n = 0
        self.remit = remit

    def __iter__(self):
        return self

    def next(self):
        if self.b == 0:
            self.a = 0
            self.b = 1
        else:
            self.a, self.b = self.b, self.a + self.b

        self.n += 1
        if self.n > self.remit:
            raise StopIteration

        return self.a

for i in fibonacci(12):
    print i,

實行結果:

0 1 1 2 3 5 8 13 21 34 55 89

ジェネレータでフィボナッチ數列

def fibonacci(n):
    a, b = 0, 1
    for i in range(n):
        yield a
        a, b = b, a + b

for i in fibonacci(12):
    print i,

實行結果:

0 1 1 2 3 5 8 13 21 34 55 89

yield

9 クラス:

ジェネレータは通常の関数のように書かれますが、何らかのデータを返すときには yield 文を使います。 next() が呼び出されるたびに、 ジェネレータは以前に中断した処理を再開します (ジェネレータは、全てのデータ値と 最後にどの文が実行されたかを記憶しています)。

9 クラス

yieldで値を返す函數がジェネレータ、といふことかしら。

dW : Linux : 魅力的なPython:イテレーターとシンプル・ジェネレーター:

Python 2.2+では、関数本体のどこかに1つ以上のyieldステートメントが存在する場合に、関数はジェネレーター・ファクトリーになります。

dW : Linux : 魅力的なPython:イテレーターとシンプル・ジェネレーター

ふむ。さうみたい。

ジェネレータでフィボナッチ數列の例ではよくわからんので單純化してみる。

def f():
    n = 0
    yield n

    n += 1
    yield n

    n += 1
    yield n

g = f()
for i in g:
    print i,

實行結果:

0 1 2

for文を使はずに見てみる:

def f():
    n = 0
    yield n    # (a)

    n += 1
    yield n    # (b)

    n += 1
    yield n    # (c)

g = f()
print g.next() # (1)
print g.next() # (2)
print g.next() # (3)
print g.next() # (4)

實行結果:

0
1
2
Traceback (most recent call last):
  File "learningpython.py", line 15, in ?
    print g.next() # (4)
StopIteration
  1. (1) の next() では (a) までが實行される(yield nnnext()の返値となる)
  2. (2) の next() では (a) の直後から (b) まで
  3. (3) の next() では (b) の直後から (c) までが實行される。
  4. (4) の next() では (c) の直後から「再開」されるが、yieldがもう無いのでStopIteration例外が投げられる――といふより、yieldで戻らなかった場合に例外が發生する、やうだ。だからreturn文で戻ることで例外が發生する(下記)。
    1. for文のループはこの例外を受取って終了する

最初のnext()に對して直ぐに例外StopIterationを投げるジェネレータ:

def f():
    return
    yield None

g = f()
print g.next()

實行結果:

Traceback (most recent call last):
  File "learningpython.py", line 6, in ?
    print g.next()
StopIteration

2006-05-19

當初海馬日記(現MORIYAMA Hiroshi's Diary)で書いてゐた「獨習Python」を、pythonグループで書くことにしました。どうぞよろしく。

覺えたことや疑問に思ったこと、及び參照したリソースをメモして行く感じで。面倒なのでマーク附けは最小限に。

Hello, world!

おやくそく。

print 'Hello, world!'

實行結果:

Hello, world!

對話型實行環境

既にPythonがインストールされてをり且つパスが通ってゐる環境で、コマンドラインからpythonと、引數無しで實行するとPythonインタプリタが對話モードで起動される。以前、Rubyのirbのやうに何か專用のコマンドがあるのだと思って探した記憶がある。

% python
Python 2.3.5 (#2, Sep  4 2005, 22:01:42)
[GCC 3.3.5 (Debian 1:3.3.5-13)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

>>>が對話モードのプロンプト。この後に入力して改行を送ると、入力が逐次實行され返値が適當な形で出力される、やうだ。

>>> 123456 + 654321
777777
>>> "Hello," + " world!"
'Hello, world!'

文法違反は、當然エラーになる。

>>> Hello, world!
  File "<stdin>", line 1
    Hello, world!
                ^
SyntaxError: invalid syntax

終了方法はCtrl-D

>>> quit
'Use Ctrl-D (i.e. EOF) to exit.'
>>> exit
'Use Ctrl-D (i.e. EOF) to exit.'
>>> 
% 

ふと思ひついたので試してみる――

>>> import sys
>>> sys.exit
<built-in function exit>
>>> sys.exit()
% 

ふむふむ。

2. Python インタプリタを使うの冒頭に書いてあった。

ファイル終端文字 (Unixでは Control-D、DOS や Windows では Control-Z) を一次プロンプト (primary prompt) に入力すると、インタプリタは終了状態ゼロで終了します。もしこの操作がうまく働かないなら、コマンド: import sys; sys.exit() を入力することでインタプリタを終了することができます。

2. Python インタプリタを使う

アプリケーションの説明書は、起動方法の直後に終了方法の説明があるべきだと思ふ。その點、この文書はイイ。

print

標準出力に文字列を書く場合、手取り早いのはprint。たぶん。

print 'Hello, world!'

實行結果:

Hello, world!

printは末尾に改行が無い場合自動的に改行を附加するさうだ。Rubyで言ふところのputsメソッドみたいなものかしら。末尾に改行を附加させたくない場合末尾にカンマを入れると*良いらしい。

print 'Hello, world!',

實行結果:

Hello, world!

Pythonのprintは空白を適宜補ふ。つまり――

print 'Hello', ',', 'world', '!',

實行結果:

Hello , world !

末尾に「,」を入れると改行を附加しない、ならばかうしたら空白を入れないでくれたりするだらうか?

print 'Hello',, 'world',, '!'

實行結果:

  File "<stdin>", line 1
    print 'Hello',, 'world',, '!'
                  ^
SyntaxError: invalid syntax

駄目だね、やっぱり。

いろいろ試してみる。

print 'Hello' ',' 'world' '!'
print 'Hello', ',', 'world', '!'
print 'Hello, ' 'world!'
print 'Hello, ''world!'
print 'Hello, world!'
print 'Hello,', 'world!'
print('Hello, ' 'world!') 
print('Hello, world!') 
print('Hello,', 'world!')
print(('Hello,', 'world!'))
print((('Hello,', 'world!')))

實行結果:

Hello,world!
Hello , world !
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
('Hello,', 'world!')
('Hello,', 'world!')
('Hello,', 'world!')

print 'Hello' ',' 'world' '!'のやうに引數(?)をコンマで區切らないと空白を插入しないみたい。括弧の扱ひが謎(そのうち調べよう)。

ところでprintて「何」なのだらう。組み込み関数……ではないみたい。

「文」らしい。

隣接する文字列

print 'Hello' ',' 'world' '!'のやうに、print文に文字列を複數、コンマで區切らずに記述すると、空白を挾まずに出力された。しかし6.6 print 文には「コンマで區切らなかった場合は」云々、といふ風には書かれてゐない。ただ式を逐次的に評価し、得られたオブジェクトを標準出力に書き出します*とあるだけだ。

これはつまり……對話モードのpythonで試してみよう。

>>>'foo' 'bar'
'foobar'

隣接する文字列リテラルは結合されるやうだ。

つまりprint文にとって'Hello' ',' 'world' '!''Hello,world!'といふ一つの文字列オブジェクトとして認識されてゐた、と。

結合されるのはリテラルのみで、變數に代入された文字列は結合されない(そんなことされても困るけど)。

>>> foo = 'foo'
>>> bar = 'bar'
>>> foo bar
  File "<stdin>", line 1
    foo bar
          ^
SyntaxError: invalid syntax

ふと、irbで試してみたら……

irb(main):001:0> 'foo' 'bar'
=> "foobar"

あー、Rubyでも隣接する文字列リテラルは結合されるのね。知らなかったらしい。

空白を間に挟んだ文字列リテラルは、コンパイル時に1つの文字列リテラルと見倣されます。

Rubyリファレンスマニュアル - リテラル

ん? 空白を間に挟んだ文字列リテラルは?

irb(main):001:0> 'foo''bar'
=> "foobar"

あのー、空白が挾まってなくても良いみたいですが。

pythonもその邊は同じみたい。

>>> 'foo''bar'
'foobar'

複数の文字列リテラルは、互いに異なる引用符を使っていても (空白文字で区切って) 隣接させることができ、その意味は各々の文字列を結合したものと同じになります。

2.4.2 文字列リテラルの結合 (concatenation)

説明と實裝が異なるところまで同じだ。

型を調べる

或オブジェクトの型を知りたいときはtype()函數を使ふ。

>>> type(1)
<type 'int'>
>>> type('1')
<type 'str'>
>>> type(type)
<type 'type'>
>>> type(type(type))
<type 'type'>
>>> type(type('str'))
<type 'type'>
>>> type()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: type() takes 1 or 3 arguments

型を調べる 其の二

3.6 types -- 組み込み型の名前

関数での典型的な利用方法は、以下のように引数の型によって異なる動作をする場合です:

from types import *
def delete(mylist, item):
    if type(item) is IntType:
       del mylist[item]
    else:
       mylist.remove(item)

Python 2.2以降では、int() や str()のようなファクトリ関数は、型の名前となりましたので、typesを使用する必要はなくなりました。上記のサンプルは、以下のように記述する事が推奨されています。

def delete(mylist, item):
    if isinstance(item, int):
       del mylist[item]
    else:
       mylist.remove(item)
3.6 types -- 組み込み型の名前

isinstance()函數は、Rubyではinstance_of?メソッドみたいなものかな。

>>> isinstance('foobar', str)
True
>>> isinstance(str, str)
False
>>> isinstance(123, int)
True

strとかに代入しちゃったらどうなるんだらう。

>>> str = 'str'
>>> isinstance('foobar', str)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

んー。「さういふことはしないでね」とかだったりする?

ローカル變數

a = 'a'
print a
def foo():
    a = 'foo'
    print a
foo()
print a

これの實行結果は直感的に理解できる:

a
foo
a

でもこっちはよくわからない。

a = 'a'
print a
def foo():
    print a
    a = 'foo'
    print a
foo()
print a

實行結果:

a
Traceback (most recent call last):
  File "learningpython.py", line 7, in ?
    foo()
  File "learningpython.py", line 4, in foo
    print a
UnboundLocalError: local variable 'a' referenced before assignment

最初のコードの結果から考へてこっちは

a
a
foo
a

となるかと思ったのだけど。とりあへず4.1 名前づけと束縛 (naming and binding)を讀んでみた。

この邊かな:

ある名前の定義がどこにもない場合、 NameError例外が送出されます。名前がまだ束縛されていないローカルな変数を参照した場合、UnboundLocalError 例外が送出されます。UnboundLocalError は、 NameErrorのサブクラスです。

4.1 名前づけと束縛 (naming and binding)

ある名前束縛操作がコードブロック内のどこかにある場合、ブロック内でその名前を使うと、全て現在のブロックで束縛されている名前を指すものとみなされます。このため、ある名前が束縛される前にブロック内で使われるとエラーを引き起こす可能性があります。

この規則はやや微妙です。Python には宣言文がなく、コードブロックのどこで名前束縛操作を行ってもかまいません。あるコードブロックにおけるローカル変数は、ブロック全体から名前束縛操作が行われている部分を走査して決定します

4.1 名前づけと束縛 (naming and binding)

えーと、つまり……

def foo():
    print a
    a = 'foo'

foo()

このfoo()函數の中に登場するaは、同じ函數内のa = 'foo'によって代入される(= 名前束縛操作が同じコードブロック内にある)ので、現在のブロックで束縛されている名前を指すもの*(foo()函數内のローカル變數) と看做される。が、print aを實行する時點ではまだ代入されてゐない――代入「される」ことは(多分バイトコンパイル時に)分ってゐるので「ローカル變數」であることは決まってゐるが、まだ代入「されて」はゐない――ので、UnboundLocalErrorになる。……で良いのかな。

9. クラス9.2 Python のスコープと名前空間*:

とはいえ、言語の定義は、``コンパイル'' 時の静的な名前解決の方向に進化しているので、動的な名前解決に頼ってはいけません! (事実、ローカルな変数は既に静的に決定されています。)

9. クラス

ああ、かういふのを「靜的」て言ふのね。

[Python-ml-jp 661] Re: question to the 3-scope rule:

これは関数本体内で代入があるからです。
Python はバイトコンパイルするとき,関数本体を静的にスキャンして, もし代入があれば,その変数をローカル変数とみなします。
# そしてローカル変数専用の高速高効率なバイトコードを生成します。

[Python-ml-jp 661

『初めてのPython 第2版』の目次にもこんなのが:

  • 14.8 関数についての注意事項
    • 14.8.1 変数がローカルスコープに属するかどうかはスタティックに決定される
oreilly.co.jp -- Online Catalog: 初めてのPython 第2版

TrueとFalse

2.3.10.9 ブール値:

ブール値とは二つの定数オブジェクト False および True です。 これらは真偽値を表すために使われます (他の値も偽または真とみなされます)

2.3.10.9 ブール値

對話モードで見てみる。

>>> 1.0 == 1.000000000000
True
>>> 0 == 0.0
True
>>> 'foo' != 'bar'
True
>>> 'foo' == "foo"
True
>>> True == False
False
>>> not False
True
>>> 'Python' == 'Python'
True

==による比較の結果など、眞僞値の表現が必要なときに使はれてゐるのが分る。

TrueとFalseと0と1

2.3.10.9 ブール値:

数値処理のコンテキスト (例えば算術演算子の引数として使われた場合) では、これらはそれぞれ 0 および 1 と同様に振舞います。

2.3.10.9 ブール値

と、いふことは――

>>> True == 0
False
>>> False == 0
True
>>> True == 1
True
>>> True == 2
False
>>> False == -1
False
>>> True + 1
2
>>> True + True
2
>>> False - True
-1
>>> False * 256
0

数値処理のコンテキスト (例えば算術演算子の引数として使われた場合) では*云々だから、True + Trueも成立つ、と。

ぢゃあ……

>>> True + 'string'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unsupported operand type(s) for +: 'bool' and 'str'

やっぱり駄目?

ふと、「もしかしてTrueの正體は實は1なんぢゃないのー?」といふ疑念が湧いたりして:

>>> type(True) == type(1)
False
>>> type(False) == type(0)
False
>>> type(True); type(False)
<type 'bool'>
<type 'bool'>
>>> type(1); type(0)
<type 'int'>
<type 'int'>

型が違ふ。慥かに「振舞って」ゐるだけのやうだ。

boolはintのサブクラス

2.1 組み込み関数:

bool([x])
標準の真値テストを使って、値をブール値に変換します。x が偽なら、False を返します; そうでなければ True を返します。bool はクラスでもあり、int のサブクラスになります。bool クラスは それ以上サブクラス化できません。このクラスのインスタンスはFalse および True だけです。

2.1 組み込み関数
>>> isinstance(True, bool)
True
>>> isinstance(True, int)
True

True(及びFalse)はintのサブクラスであるboolのインスタンス。そしてisinstance()函數は引数 object が引数classinfo のインスタンスであるか、(直接または間接的な) サブクラスのインスタンスの場合に真を返2.1 組み込み関数す。

だからisinstance(True, bool)だけでなくisinstance(True, int)も眞。

TrueをFalseに

ちょっといたづらしてみた:

>>> True = False
>>> True == False
True
>>> (True == True) == True
False
>>> True + True + True
0

ユーザからの一行入力

プログラムのユーザからのちょっとした入力を扱ひたい場合は、組み込み関数raw_input()が御手輕な感じ。

name = raw_input('Your name: ')
print 'Hello,', name, ':-)'

實行結果:

Your name: Hiroshi MORIYAMA
Hello, Hiroshi MORIYAMA :-)

エンコード宣言

print 'いろはにほへと'

このプログラムを、たとへばEUC-JPでファイルに(たとへばiroha.pyといふ名前で)保存し、それをPythonで實行する。

python iroha.py

するとprint文の實行結果(いろはにほへと)の前に次のやうな警告(エラーではない)が出力される。

「非ASCII文字 '\xa4' がiroha.pyってファイルの1行目にあるけどencoding が宣言されてゐない」と言ってゐる、らしい。「詳細は http://www.python.org/peps/pep-0263.html を參照せよ」とかなんとか言ってゐる(らしい)けど、「英語だから讀めませーん」とか何とか言ふと「ほっほっほ、しやうがないなう」と言って百科おぢさんが「テレビのジョン」を呼んでくれる――わけがないのでPython リファレンスマニュアルの目次をざっと眺めて見附けた(なんて非效率な!)4 エンコード宣言 (encoding declaration)を讀んでみる。

Python スクリプト中の最初の行か、二行目にあるコメントが正規表現coding[=:]\s*([-\w.]+) にマッチする場合、コメントはエンコード宣言(encoding declaration) として処理されます

4 エンコード宣言 (encoding declaration)

no encoding declaredと言はれたのは多分これのことだよね。といふことで:

# coding: euc-jp
print 'いろはにほへと'

と書きなほして實行してみると、警告は出なくなった。めでたしめでたし。

一行目にある# coding: euc-jpて部分がエンコード宣言(二行目でも良いみたい)。これでプログラムのソースコード(ファイル)の文字エンコーディングを指定出來る。正規表現のcoding[=:]\s*([-\w.]+)にマッチすればエンコード宣言と看做されるわけだから、たとへば――

  • # <?python version=2.3.5 encoding=euc-jp?>

なんて謎形式で書いても別に良いみたいだけど、リファレンスマニュアルでは

GNU Emacs が 認識できる形式

# -*- coding: <encoding-name> -*-

または、Bram Moolenar による VIM が認識できる形式

# vim:fileencoding=<encoding-name>
4 エンコード宣言 (encoding declaration)

が推奬されてゐた。

ところで、適當に# coding: euc-jpなんて書いたら上手く行ったけれど、<encoding-name>の部分に書いて認識される名前はきっと決まってゐる筈。でも4 エンコード宣言 (encoding declaration)にはそれらしきことは書かれてゐないしそれらしいリンクも無い。http://www.python.org/peps/pep-0263.html にも名前のリストとかは無かったし……。もしかするとPython ライブラリリファレンス2 標準エンコーディングにあるリストがそれかも知れないがはっきりしない。とりあへず適當に試してみたところ、Shift_JISeuc-jputf-8iso-2022-jpなどは問題無く認識されたやうだ。Emacsが認識するeuc-japanSyntaxError: 'unknown encoding: euc-japan'といふエラーになった。大文字小文字は、どっちでも良いみたい。EuC-Jpなんて書いても一往OK。

リスト

3. 形式ばらない Python の紹介3.1.4 リスト*の邊り。

他言語で言ふところの配列のやうなものはPythonではリストと呼ばれてゐるみたい。表記はRubyやJavaScriptなどの配列に良く似てゐる。といふか同じだ。

a = [1, 2, 3]
b = [a, 'foo', 'bar']
print a
print b

實行結果:

[1, 2, 3]
[[1, 2, 3], 'foo', 'bar']

要素へのアクセスに添字が使へるあたりも同じ。

li = ['foo', 'bar']
print li[0]
print li[1]

實行結果:

foo
bar

繰返し(for文)

4. その他の制御フローツール

Python の for 文は、読者が C 言語や Pascal 言語 で使いなれているかもしれない for 文とは少し違います。(中略) Python の for 文は、任意のシーケンス型 (リストまたは文字列) にわたって反復を行います。

4. その他の制御フローツール
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in l:
    print i,

實行結果:

0 1 2 3 4 5 6 7 8 9

文字列も。

for c in 'Hello, world!':
    print c, c,

實行結果:

H H e e l l l l o o , ,     w w o o r r l l d d ! !

range()函數

4. その他の制御フローツール:

数列にわたって反復を行う必要がある場合、組み込み関数 range() が便利です。この関数は算術型の数列が入ったリストを生成します。

4. その他の制御フローツール
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

JavaScriptのfor(var i = 0; i < 10; i++) { /* ... */ }とかRubyの10.times {|i| print i }みたいなことがしたい場合は、これとfor文を使へば良いみたい。

for i in range(10):
    print i,

實行結果:

0 1 2 3 4 5 6 7 8 9

range()の第二、第三引數

range(1, 101)

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

といふリストを作る。1から101 - 1までの數字を1刻みで。

刻み方を變へたい場合は第三引數で指定する。こんな具合:

range(1, 101, 4)

實行結果:

[1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69,
73, 77, 81, 85, 89, 93, 97]

標準出力に直接書く

標準出力に書きたい場合手取り早いのはprint文だが、print文は勝手に空白を入れるので困ることがある。たとへば次のやうな場合(普通はこんなことしないだらうけど、例として):

# -*- coding: euc-jp; -*-

for i in 'いろはにほへと\n':
    print i,

この場合のfor文は文字單位ではなくバイト單位で繰返す。print文は行頭以外で空白を書足す。結果、'いろはにほへと' は1バイト毎にスペースで分割された状態に出力され、文字化けを起す。

「そのまま」出力させたい場合は標準出力に直接書けば良い。1 時間で覚える?Python 「スペースとか改行とか余計なことをするな!」という方は標準出力に直接書き込んでください*とあった。

# -*- coding: euc-jp; -*-

import sys

for i in 'いろはにほへと\n':
    sys.stdout.write(i)

print文自體に「餘計なことをするな!」と言ひたいところだけど、それは出來ないんだらうか。改行附加を抑制する方法(末尾にコンマ)はあるのに。

函數呼出しの括弧とprint文とタプル

Pythonでは、函數呼出しの括弧の省略は出來ないらしい。

>>> type 1
  File "<stdin>", line 1
    type 1
         ^
SyntaxError: invalid syntax

一方printは文なので(といふと語弊があるかも知れないが)括弧は不要。括弧を附けてもエラーにはならないが、それはどうやらタプルとやらを表現する括弧に解釋されて、函數呼び出しの括弧とは解釋されないやうだ。

>>> print 1, 2, 3
1 2 3
>>> print(1, 2, 3)
(1, 2, 3)

どうも紛らはしい。そもそもなぜPythonのprintは函數ではなく文なのだらう。何か理由があるんだらうけど。

タプル

タプルについてのリンク。

僞なる者共

Rubyではfalsenil以外の全てのオブジェクトは眞だった(FALSEも僞だけどあれは定數。詳細は知らんけど多分falseを參照してゐるのだらう、といふかリファレンスマニュアルにfalse と同じRubyリファレンスマニュアル - 組み込み定数とある)。

一方Pythonでは僞となるオブジェクトがRubyよりちょっと多い。以下Python ライブラリリファレンス2.3.1 真値テストより:

以下の値は偽であると見なされます:

  • None
  • False
  • 数値型におけるゼロ。例えば 0 、 0L 、 0.0 、 0j 。
  • 空のシーケンス型。例えば '' 、 () 、 [] 。
  • 空のマッピング型。例えば {} 。
  • __nonzero__() または __len__() メソッドが定義されているようなユーザ定義クラスのインスタンスで、それらのメソッドが整数値ゼロまたは bool 値の False を返すとき。

それ以外の値は全て真であると見なされます -- 従って、ほとんどの型のオブジェクトは常に真です。

2.3.1 真値テスト

NoneはRubyで言ふところのnilみたいなものか。

MORIYAMA Hiroshi <hiroshi@kvd.biglobe.ne.jp>