タプル、リスト、デクト

Pythonにはデータを作成する際に便利な構造が沢山?あります。

>>> t = ()
>>> l = []
>>> d = {}
>>> dir(t) 
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__
ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__g
t__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul_
_', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__',
'__setattr__', '__str__']
>>> dir(l)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delsli
ce__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__gets
lice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '
__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__r
educe_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__
', '__setslice__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'p
op', 'remove', 'reverse', 'sort']
>>> dir(d)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__'
, '__eq__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '
__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__re
duce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__str__', '
clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys',
'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

よく利用するのは、リスト(list)とデクト(dict)だと思う。タプル(tuple)はリストからリストを操作する機能を取り除いた構造と考えて良いと思う。タプルってどこで利用すればいいのかな?タプルでできる事は全てリストでできると思うのだが、さらに操作性を考えるとリストの方が利用しやすいと思うのだが。タプルの使いどころがわからない。また、タプルを使う事によって得られるメリットとかあるの?なんだろうね。

デクトは良く言われるのはハッシュとか連想配列とかと同じもの。文字列をキーにして値を取得できるデータ型だね。

>>> l = ["a", "b", "c", "d"]
>>> for i in l:
...   print(i)
...
a
b
c
d
>>> print("\n".join(l))
a
b
c
d

なるほど

>>> d = {
...   "one"  : "Ichi",
...   "two"  : "Ni",
...   "three": "San"
... }
>>> for k, v in d.items():
...   print("%s=%s" % (k, v))
...
three=San
two=Ni
one=Ichi

順番は保存されていないね。そうそう、「range()」ってのは便利だ。

>>> for i in rrange(5):
...   print i
...
0
1
2
3
4
>>> li = ["a", "b", "c", "d", "e"]
>>> for i in range(len(li)):
...   print(li[i])
...
a
b
c
d
e

便利だ。

あと、こんな事もできる。これにはちょっとビックリした。

>>> data = {
...   "Tom": (96, 88, 92, "All-Round"),
...   "Sam": (13, 96, 31, "Defensive"),
...   "Fin": (89, 19, 92, "Offensive")
... }
>>> for name, (offense, defense, mental, comm) in data.items():
...   print("%s: %d-%d-%d %s" % (name, offense, defense, mental, comm))
...
Fin: 89-19-92 Offensive
Sam: 13-96-31 Defensive
Tom: 96-88-92 All-Round
>>>

めっちゃ便利だ。

ファイルに書き込む

Pythonでファイルに書き込むには、

>>> f = open("logfile.log", "w")
>>> f
<open file 'sample001.py', mode 'w' at 0x00AF4410>
>>> f.write("This is a pen!")
>>> f.close()
>>> print file("logfile.log").read()
This is a pen!

と言う事だ。OPEN関数の第二引数にモードを明記する。「r:read」「w:write」「a:append」との事。ファイルが存在しない場合「w」すると新規にファイルを作成してくれた。

>>> f = open("logfile.log", "a")
>>> f.write("This is it!")
>>> f.close()
>>> print file("logfile.log").read()
This is a pen!This is it!

思っているのと違った。改行も入れてくれるのかなと思ったが違った。改行は自分で入れろって事ですね。了解しました。ちなみに「r+」のようなおなじみの書き方もできるようです。

あと、「file()」って便利ですね。いちいちファイルをオープンさせないで中身を見ることができるんですから・・・たぶん内部でオープンしてるんでしょうけど。それとファイルをクローズしていないのに「file()」で読み込もうとすると、読み込めませんね。空行が返ってくるから何が起きたのか理解するまで時間を要した。

Pythonって知らない事だらけで、物凄く面白い。まだまだ興味が尽きませんね。

Pythonの変数宣言

ちょっと下記のコードを動かしてみた。

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

def fileopen(filename):
  try:
    f = open(filename, "rb", 0)
    try:
      f.seek(-128, 2)
      data = f.read(128)
    finally:
      f.close()
  except IOError:
    print("IOError")

if __name__ == "__main__":
  import sys
# filename = sys.stdin.read(1)
  filename = sys.argv[1]
  fileopen(filename)

コメントの所はちょっとしたメモね。上記のコードで驚いた。処理は引数でファイル名を渡してファイルの末尾から128byte読み込むってだけの処理なんだが、驚いたのは例外処理の事。空ファイル(0byte)を引数に渡すとどうなるか?OPENは行われるので例外は発生しない。でSEEKの段階で例外が発生するんだが、その例外の捕捉はSEEKの外にあるEXCEPTで捉えているんだが・・・これって当たり前なの?Javaもそうだっけ?違った気がするんだが、考え方はわからんこともない。内で発生した例外も外で捕捉できるようにしているわけだよね。ちょっと驚いた。

で、更に驚いたのは、コードを下記のように変えてみた。まあ「data」を表示してみたいと考えて何も考えず「print(data)」を一番外の「try〜except」の直後に追加したんだよね。

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

def fileopen(filename):
  try:
    f = open(filename, "rb", 0)
    try:
      f.seek(-128, 2)
      data = f.read(128)
    finally:
      f.close()
  except IOError:
    print("IOError")
  print(data)

if __name__ == "__main__":
  import sys
# filename = sys.stdin.read(1)
  filename = sys.argv[1]
  fileopen(filename)

正常に動くんですよ。エラーも発生せず。もちろん引数で渡すファイルは128byte以上ですけどね。これって良く見ると不思議じゃないですか?「data」って変数は2つ目のtryの中で宣言されていますよね。この「data」は、この「try〜finally」のブロックのみで有効だと思いませんか?であるならば、fileopen関数の最後の行にある「print(data)」でエラーになるはず、だと思うのだが・・・そのようではないですね。こういった場合、他の言語では「print(data)」と同じ層に変数を宣言しておくものだと思ったのだが・・・そうですよね。どのようなメモリマップ上に変数を格納しているのか理解できていないが不思議だ。

「try〜except」の中だからですか?そこはネストしているが本当はトップレベルの層と同じとか?そんなわけ無いよね〜でも気になるので、

>>> def test():
...     if 1:
...             data = 9999
...     print(data)
...
>>> test()
9999

IF文のブロックで囲い、その中で変数を初期化。正常に動作する。もちろん初期化を行わなければエラーとなるんだが・・・不思議だ。

不安になったのでPerlで確認。

#!/usr/bin/perl

use strict;
use warnings;

if (1) {
  my $data = 9999;
}

print $data;
exit;

実行してみる。

$ ./sample001.pl
Global symbol "$data" requires explicit package name at sample001.pl line 10.
Execution of sample001.pl aborted due to compilation errors.

これが普通?だよね。これが自然というか今までやってきたやつだよ。すっきりする。動作させるには下記のように修正。

#!/usr/bin/perl

use strict;
use warnings;

my $data = 0;
if (1) {
  $data = 9999;
}

print $data;
exit;

Pythonは不思議だ。関数の内部変数は全て同じメモリ空間に保存されるって事なんだろうな〜それが同列ってわけか!ん〜これはメリット、デメリットあると思うな〜慣れるまでは絶対にデメリットの方が大きい気がする。大きなコードになると変数がどこでどのように宣言され初期値を与えられているのかわからなくなりそうだし、思いがけずに変数の値を上書きしそうだよね。不思議だ。

ファイルオープン

Pythonでのファイルオープン

>>> f = open("/file/path/sample.txt", "r")

どの言語も似たようなものですね。ただ、オープンに失敗した場合も考えると下記のほうが良いのかしら

>>> try:
...   f = open("/file/path/sample.txt", "r")
... except IOError:
...   print("IOError")
...

「except」で例外を捕捉すべし。あと「finally」句も存在する。Javaと同じだね。で何故だか「else」句もあるんだが、よくわからんな〜なんで例外処理に「else」句があるのか、どういった動作をするのか想像もつかない。

>>> try:
...   f = open("/file/path/sample.txt", "r")
... except IOError:
...   print("IOError")
... else:
...   print("Else statement")
...

当初、この「else」句が、「finally」句に当たるのかと思っていたがちがった。

Pythonオンラインマニュアル、try文の箇所には「オプションの else 節は、実行の制御が try 節の末尾に到達した場合に実行されます。7.1else 節内で起きた例外は、else 節に先行する except 節で処理されることはありません。」との事。

つまり「try」の処理が正常に動作後、「else」の処理に移るってことですね。また「else」内で発生した例外は、「else」よりも先にある「except」では処理されないって事ですね。わかりました。

>>> f
<open file 'sample001.py', mode 'r' at 0x00AF4140>
>>> type(f)
<type 'file'>
>>> dir(f)
['__class__', '__delattr__', '__doc__', '__enter__', '__exit__', '__getattribute
__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__
', '__repr__', '__setattr__', '__str__', 'close', 'closed', 'encoding', 'fileno'
, 'flush', 'isatty', 'mode', 'name', 'newlines', 'next', 'read', 'readinto', 're
adline', 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writeli
nes', 'xreadlines']

いつもながら面白い。

>>> f.tell()
0L
>>> print(f.read())
 --- ファイルの中身が表示される ---
>>> f.tell()
572L
>>> f.seek(0, 0)
>>> f.tell()
0L
>>> print(f.read())
 --- ファイルの中身が表示される ---
>>> f.seek(0)
>>> f.tell()
0L

ちょっと遊んでみた。「tell()」でファイルポインタの現在の位置がわかり、「seek()」でファイルポインタを移動でき、「read()」でファイルを一括読み込みするわけだ。他の言語でseekの処理ってもっとややこしかった気がするけど・・・と言うか、特にseek処理って必要なかった気がするな〜なんか簡単にでき驚いている。

「seek()」の第一引数は移動するバイト数、第二引数には、それぞれ「0:ファイルの先頭から(デフォルト値)」「1:ファイルの現在位置から」「2:ファイルの末端から」を取ります。なんだかんだで面白い。

>>> f.name
'sample001.py'
>>> f.mode
'r'

なるほど〜でファイルってオープンしたらクローズしたくなるのでクローズする。

>>> f.closed
False
>>> f.close()
>>> f.closed
True
>>> f.seek(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file
>>> f
<closed file 'sample001.py', mode 'r' at 0x00AF4140>

ふと思ったんだがクローズする場合、下記のようにしたほうがいいのかな?

>>> if not f.closed:
...   f.close()
...   f = None
...

とかってやるべきかどうか・・・よくc言語さわっているとやると思うんだが、そういや〜PerlPHPではやらんね。「close()」だけだよね。あまり深く考えず、「f.close()」だけでいいよね。

・・・最近、Pythonが面白くて仕方がないよ。なんでこんなに面白いんだろう。不思議だ。

Pythonの変数隠蔽?

Pythonでクラスを作成した後、そのクラスに属する変数ってクラス変数?インスタンス変数?まあ〜なんだな。Javaとかいろんな言語やってると呼び方なんかどうでも良くなる。と言うよりもごちゃごちゃになって明確に分けるのを放棄してしまうよね。

>>> class TestFunc():
...   name1   = "name1"
...   _name2  = "name2"
...   __name3 = "name3"
...   def __init__(self):
...     pass
...
>>> t = TestFunc()
>>> t.name1
'name1'
>>> t._name2
'name2'
>>> t.__name3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Test instance has no attribute '__name3'

変数の先頭に「__」をつけると一応外から見えなくなるみたい。ただ、やり方を知っていればアクセス可能。

>>> t._Test__name3
'name3'

なるほど!Pythonって面白いよ。

Dive Into Python」にこんなんあった。

>>> class counter:
...   count = 0
...   def __init__(self):
...     self.__class__.count += 1
...
>>> counter
<class __main__.counter at 0x00AEDF30>
>>> counter.count
0
>>> c = counter()
>>> c.count
1
>>> d = counter()
>>> d.count
2
>>> c.count
2
>>> counter.count
2

動作は簡単で、単純にオブジェクトを生成した回数をカウントしているだけですよね〜

ではではでは

>>> class TestCounter:
...   count = 0
...   def __init__(self):
...     self.count += 1
...
>>> TestCounter
<class __main__.TestCounter at 0x00AEDF90>
>>> TestCounter.count
0
>>> c = TestCounter()
>>> c.count
1
>>> d = TestCounter()
>>> d.count
1
>>> c.count
1
>>> TestCounter.count
0

あれ?動作が違うね。なんで、同じかなと思ったんだが。

ん〜最初の「count」変数はクラスに属しており、コンストラクタ「__init__」ないでも「self.__class__.count」としクラスに属している「count」変数をカウントしているんだよね。で2つめのはコンストラクタ「__init__」では「self.count」としているが、これって、あくまで・・・どうなの?別の変数と言う考え方でいいの?名前は同じだけど属している場所が異なっており干渉しあわない。

ややこいな〜「self.count」とし「self」でアクセスしているのでクラスに属していてもよさそうなんだが・・・違うのか!もう少し調査必要かな。

Pythonの引用符

Pythonって知れば知るほど面白いよね。

Pythonでのコメントは行の先頭に「#」をつけると、その行がコメントになります。それは良いとして、Pythonでは単一引用符「'」と二重引用符「"」では、何か動作に違いがあるの?

わかりやすい例ではprint()での、それぞれの動作。

>>> print("%s" % "This is it!")
This is it!
>>> print('%s' % "This is it!")
This is it!

Perlでは、単一引用符「'」で囲まれていれば、その中に変数が存在しても変数と見なさず展開しない。しかし二重引用符「"」で囲まれていれば展開するって感じだと思うんだよ。それなのにPythonでは動作が同じ様に見えるのだが、同じと考えて問題ないのかな〜

>>> str = "This is a pen!"
>>> "Who are you str"
'Who are you str'
>>> 'Who are you str'
'Who are you str'

よくよく考えると、単一引用符、二重引用符で囲まれた中にある文字列が変数かどうかの区別がつかないよね。Perlだけだっけ、二重引用符の中に変数があれば展開するのって?忘れたな〜PHPRubyはどうだっけか!

Perlは「@」や「$」等々で変数ってことがわかるからね〜ん〜不思議に思えて仕方がない。

モジュールのリロード

Pythonインタプリタでいろいろ遊んでいるんだが・・・

自分でモジュールを編集しながら試しているとモジュールをリロードしたい場合が多々あるわけだ、そんな時、インタプリタを終了して再度起動、その後、インポートしなおしていたんだがめんどくさい。なんとかならんものかと思っていたら「reload()
」っていう組み込み関数があるみたいだが使い方が良くわかんね。

「infomation」ってモジュールにある「info」って関数をリロードしたいのよ。

>>> from infomation import info
>>> reload(info)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: reload() argument must be module
>>> reload(infomation.info)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'infomation' is not defined
>>> reload('c:\practice\mymodule\infomation')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: reload() argument must be module
>>> reload('c:\practice\mymodule\infomation.py')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: reload() argument must be module

なんでよ?いろいろ試したが正解にたどり着けない。ほんとに何でだ?

ん〜もしかしてモジュールじゃないとか!

>>> type(info)
<type 'function'>

これは間違いじゃない。

>>> import types
>>> dir(types)
['BooleanType', 'BufferType', 'BuiltinFunctionType', 'BuiltinMethodType', 'Class
Type', 'CodeType', 'ComplexType', 'DictProxyType', 'DictType', 'DictionaryType',
 'EllipsisType', 'FileType', 'FloatType', 'FrameType', 'FunctionType', 'Generato
rType', 'GetSetDescriptorType', 'InstanceType', 'IntType', 'LambdaType', 'ListTy
pe', 'LongType', 'MemberDescriptorType', 'MethodType', 'ModuleType', 'NoneType',
 'NotImplementedType', 'ObjectType', 'SliceType', 'StringType', 'StringTypes', '
TracebackType', 'TupleType', 'TypeType', 'UnboundMethodType', 'UnicodeType', 'XR
angeType', '__builtins__', '__doc__', '__file__', '__name__']
>>> types.ModuleType
<type 'module'>

ん〜ダメだ。何が何だかわかんないや〜厳密に言うと関数「'function'」とモジュール「'module'」は違うってこと?reload()ではモジュールがリロードできるってこと?じゃあモジュールってどうやって定義するの?「def」とは異なるの?ああ、ダメだわかんないや。今日は寝よう。

で起きてみて再度挑戦してみた。

>>> import infomation 
>>> reload(infomation)
<module 'infomation' from 'infomation.pyc'>
>>>

リロードできたみたい。ん〜「from infomation import info」は関数だからモジュールをリロードするには、「import infomation」としないと「reload()」は利用できないってこと?なんかめんどいな!

>>> import types
>>> types.FunctionType
<type 'function'>
>>> FunctionType
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'FunctionType' is not defined
>>> from types import FunctionType
>>> FunctionType
<type 'function'>

結構厳密にインポートされるんですね。メモリ等考えた場合、コード中で利用しない関数はインポートしない方がいいのかな?必要最小限の関数をインポートしましょうってことでいいのかな。知らない事だらけで面白いですねPythonは!