今回も引き続き、実際に動かすことができるように、できるだけ各サンプルコード単体で動くように心がけています。 環境などについても同様です。
特にファイル名に'utf-8'などと入っていない限りは、ファイルはShift_JISでエンコーディングしています。
作業フォルダは前回同様にC:\IronPython26フォルダの直下にフォルダを作成しました。
C:\IronPython26\proj.class |-- a01_formclass.ipy |-- a02_formclass.ipy |-- a02_formclass2.ipy |-- a02_formclass2_dir.txt |-- a02_formclass_dir.txt |-- a02_lib | |-- __init__.py | |-- mylib.py | `-- mylib2.py |-- a02_tooltipbutton.ipy ... |-- a06_form_flowlayout.ipy |-- a06_lib | |-- __init__.py | `-- mylib.py `-- a06_unittest.ipy
MSDNの.NETライブラリとO'REILLYの「Python Pocket Reference 3rd Edition」を使っています。 その他オンラインで検索したものもありますが、直接参考にしたものは随時リンクを張ります。
オブジェクトがどのようなメソッドを持っているかはdir()
メソッドで確認することができます。
マニュアルのどこを読むべきか、これであたりをつけることができる場面も多いと思います。
ここではサンプルを走らせる時には触れる必要がなかったものの中から、自分でスクリプトを作成する際に必要になった事柄を説明していきます。
私はWindowsプログラミングについてはVBを触ったぐらいであまり前提がないので、作業内容をまとめておくことで(初級者と中級者の間にあるような)ギャップを埋めることができればと思っています。
実際に必要なスクリプトを作成する時には一つのスクリプトファイルに全部の処理を記述するのではなく、適当なボリュームで分割して作成していくはずです。 Pythonらしい方法はクラスに分割する方法だと思います。
そこでクラスの作成のサンプルとそのファイルを読み込む方法を中心に、より実践的な方法についてまとめていきます。
IronPython 1.0の時代に書かれた文書ではCPythonのライブラリが不足しているため導入するように指示があります。 しかしIronPython 2.xではMSI配布パッケージにCPython 2.6のライブラリの一部が添付されるようになっています。 csv.pyなどCPythonに附属するオブジェクトコード(DLLファイル)を利用するものは正常に動かないため添付されていません。
添付されるライブラリのためにサイズが大きいですが、この文書ではMSIパッケージからIronPythonを導入したものとして説明しています。
前回一番最初に作成した空のFormを表示するスクリプトをクラスを使って作成します。
同じ動きをする2つのファイルを比べると次のようになり、今回作成したクラスファイルの方が1.5倍ほど行数が増えています。
import clr clr.AddReferenceByPartialName("System.Windows.Forms") clr.AddReferenceByPartialName("System.Drawing") import System from System.Windows.Forms import Form from System.Drawing import Size form = Form() form.Size = Size(300,200) form.Text = "Hello World!" ## main ## System.Windows.Forms.Application.Run(form)
import clr clr.AddReferenceByPartialName("System.Windows.Forms") clr.AddReferenceByPartialName("System.Drawing") import System from System.Windows.Forms import Form from System.Drawing import Size class MyForm(Form): def __init__(self): self.Size = Size(300,200) self.Text = "Hello World!" pass def run(self): System.Windows.Forms.Application.Run(self) pass ## main ## form = MyForm() form.run()
このままでは書き方が冗長になっただけで、おもしろくありません。 初心者向けにはより短い方が好まれるため、入門書によく載っているのは短い方です。
新しく作成したファイルの本体処理は2行に収まっています。 別々のファイルにするとライブラリの呼び出しが加わって3行程度が本体処理になるはずです。
まだ簡単な初期化を行なっているだけですが、ここからはアプリケーション固有の設定はクラス定義に押し込めて、イベントハンドラなど、軟性を確保したい定義は本体処理側で定義する方向を考えてみようと思います。
スクリプトの規模が大きくなってくると、別ファイルにクラスを作成する方が効率が高まります。 この場合の効率は作成する際に手分けをするという意味だけではなくて、設計やテストをファイル単位で考える事で最終的には作り手の覚えておくべきことを整理できるという意味も含みます。
入門書を終えた状態だと、同じようなスクリプトのコピーに頼ってしまいがちです。 次からはクラスを別ファイルに書き出し、ライブラリとして扱うことで、扱いが楽になるものなのか様子を眺めてみようと思います。
まずは先ほどの例をそのままメインスクリプトとライブラリファイルに分割します。
まずライブラリを入れておくためのフォルダとして"a02_lib"を作成しました。 先ほど使った"a01_formclass.ipy"ファイルを元に本体とライブラリのファイルに分けていきます。
import clr clr.AddReferenceByPartialName("System.Windows.Forms") clr.AddReferenceByPartialName("System.Drawing") import System from System.Windows.Forms import Form from System.Drawing import Size class MyForm(Form): def __init__(self): self.Size = Size(300,200) self.Text = "Hello World!" pass def run(self): System.Windows.Forms.Application.Run(self) pass ## main ## form = MyForm() form.run()
## empty
from a02_lib.mylib import * form = MyForm("Hello World!") form.run()
import clr clr.AddReferenceByPartialName("System.Windows.Forms") clr.AddReferenceByPartialName("System.Drawing") import System from System.Windows.Forms import Form from System.Drawing import Size class MyForm(Form): def __init__(self, title): self.Size = Size(300,200) self.Text = title pass def run(self): System.Windows.Forms.Application.Run(self) pass
変更前は一つのファイルだったのに、3つのファイルが作成されました。 最後のファイルは空なので、実質的に2つのファイルに分かれたことになります。 変更のポイントを順番に説明していきます。
新しく作成したライブラリファイルは"a02_lib\mylib.py"ですが、これを呼び出すためにfrom a02_lib.mylib import *
文を使っています。
これを実行した後は、a02_lib.mylib.MyForm
ではなくMyForm
のようにクラスの名前でアクセスできるようになります。
どのクラスが名前で呼べるのか、その様子はdir()コマンドで確認する事ができます。
> ..\ipy.exe >>> from a02_lib.mylib import * >>> dir() ['Form', 'MyForm', 'Size', 'System', '__builtins__', '__doc__', '__name__', 'clr']
"MyForm"の他にも、Form
やSize
などもクラスの名前だけで呼べる事がわかります。
ただし開発の規模が大きくなってくるといろいろなライブラリを使うため、MyForm
だけを使いたいのに余計なものも読み込まれ、もし他の人がそのライブラリで違う"MyFormクラス"を定義して読み込んでいた場合は混乱を引き起す事になります。
できるだけ読み込むライブラリは少ない方がよく、その制御をする方法が__all__
変数です。
この変数はimport *
で読み込まれる際にアスタリスクに何が入るのか指定する事ができます。
次のようにファイルを作り直す事ができます。
... __all__ = ['MyForm'] ...
mylib.pyファイルに、この変更を加えた後でdir()を実行してみます。
>>> from a02_lib.mylib2 import * >>> dir() ['MyForm', '__builtins__', '__doc__', '__name__']
引き続きライブラリの内部ではForm
やSize
はクラス名だけで利用できますが、ライブラリをimportして利用する側で はMyForm
クラスの1つだけがみえるようになりました。
空のファイルで作成した__init__.pyファイルの役割は、先ほどの__all__
行の管理が中心です。
ライブラリを開発していくと、フォルダの中にいろいろなファイルができていきます。 そのライブラリ本来の動きをサポートするためのユーティリティ的なクラスも多数できるでしょう。
またライブラリが提供するクラスの数が増えていった時に、form a02_lib.mylib
のようにフォルダ名とファイル名を組み合せるのはファイルの数だけ行数が増えるので非効率です。
またファイル名を覚えておくことも、難しくなるでしょう。
そこでフォルダ名だけを使ってimportできるようにする仕組みの土台を提供するのが__init__.pyファイルです。
先ほど作成したコメントだけの__init__.pyファイルを変更し、処理本体の一行目を__init__.pyに合うように変更します。
## empty
from a02_lib.mylib import * form = MyForm("Hello World!") form.run()
__all__ = ["MyForm"] from a02_lib.mylib import *
from a02_lib import * form = MyForm("Hello World!") form.run()
本体スクリプトの変更部分はfrom a02_lib import *
の部分だけです。
この文にはライブラリファイル'mylib.py'に関連する名前は一切でてきません。
そして結果としてMyForm
クラスだけに名前でアクセスできるようになります。
将来、mylib.pyファイルを複数のファイルに分割しても、__init__.pyファイルの変更だけが必要になります。
__all__行は無理に使う必要はなく、代替手段がある方法です。実際に標準ライブラリの多くで使われていません。 しかし、それは__all__行が直接使えるほど単純な場合が少ないからのようで、ほとんどは__init__.pyファイルでクラス名のロードが制御されています。 ライブラリを眺めてクラスを使おうとした時に、本体をどうやって呼び出したらよいのか分からなくなるところなので、__init__.pyファイルには慣れておいた方が良さそうです。
これまでばらばらに実装してきた機能をまとめて"mylib.py"ファイルを作成しようと思います。
これまではスクリプトの本体は部品となるFormやButtonの作成と配置だけを行なってきましたが、今回はイベントハンドラも実装しているため、本体全体で一つのクラスを作成しています。 実質的な本体処理は最後の2行で行なわれています。
本体スクリプトだけで以前作成したスクリプトと同程度の分量がありますが、これは動的に配置する"No. X"ボタンをクリックした時に図のようなボタン名入りのMessageBoxを表示するようにしているためです。
# coding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # import clr clr.AddReferenceByPartialName("System.Windows.Forms") clr.AddReferenceByPartialName("System.Drawing") import System from System.Windows.Forms import Form from System.Windows.Forms import FlowLayoutPanel from System.Windows.Forms import Button from System.Windows.Forms import MessageBox from System.Windows.Forms import DockStyle from System.Drawing import Size ## init Form object form = Form() form.Text = "Hello World!" form.Size = Size(300,200) ## init FlowLayoutPanel object flowLayout = FlowLayoutPanel() flowLayout.Size = form.Size flowLayout.Dock = DockStyle.Fill #flowLayout.AutoScroll = True ## 生成したButtonオブジェクトを記憶するリストです buttonList = [] ## setup add button addButton = Button() addButton.Text = "Add" def addButton_click(sender, arge): b = Button() b.Text = "No. " + str(len(buttonList)) flowLayout.Controls.Add(b) pass addButton.Click += addButton_click flowLayout.Controls.Add(addButton) ## setup del button delButton = Button() delButton.Text = "Delete" def delButton_click(sender, arge): if(len(buttonList) == 0): return b = buttonList.pop() flowLayout.Controls.Remove(b) pass delButton.Click += delButton_click flowLayout.Controls.Add(delButton) form.Controls.Add(flowLayout) System.Windows.Forms.Application.Run(form)
__all__ = ["MyForm", "MyButton", "MyFlowLayoutPanel"] from a07_lib.mylib import *
# coding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # import System from a07_lib import * class Main: def __init__(self): self.form = MyForm("Hello World!") self.layout = MyFlowLayoutPanel() self.callback_buttonList = [] self.putButtons() pass def putButtons(self): addButton = MyButton("Add", "add new button") addButton.Click += self.callback_addButton self.layout.Controls.Add(addButton) delButton = MyButton("Del", "delete the last button") delButton.Click += self.callback_delButton self.layout.Controls.Add(delButton) pass def callback_addButton(self, sender, arge): b = MyButton("No. " + str(len(self.callback_buttonList)), "") b.Click += self.callback_button self.callback_buttonList.append(b) self.layout.Controls.Add(b) pass def callback_delButton(self, sender, arge): if(len(self.callback_buttonList) == 0): return b = self.callback_buttonList.pop() self.layout.Controls.Remove(b) pass def callback_button(self, sender, arge): msg = "Button: " + sender.Text + " clicked" System.Windows.Forms.MessageBox.Show(msg, "Notice") pass def run(self): self.form.Controls.Add(self.layout) self.form.run() ## main ## main = Main() main.run()
# encoding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # import clr clr.AddReferenceByPartialName("System.Windows.Forms") clr.AddReferenceByPartialName("System.Drawing") import System from System.Windows.Forms import Form from System.Windows.Forms import Button from System.Windows.Forms import ToolTip from System.Windows.Forms import FlowLayoutPanel from System.Windows.Forms import DockStyle from System.Windows.Forms import ToolStripMenuItem from System.Windows.Forms import MenuStrip from System.Windows.Forms import MessageBox from System.Drawing import Size from System.Drawing import Point __all__ = ['MyForm','MyButton','MyFlowLayoutPanel'] size = Size(300,200) class MyForm(Form): def __init__(self, title): self.Size = size self.Text = title self.IsMdiContainer = True pass def setupMenu(self): self.ms = MenuStrip() self.windowNewMenu = ToolStripMenuItem("New", None) self.windowNewMenu.Click += self.windowNewMenu_onclick self.windowMenu = ToolStripMenuItem("Window") self.windowMenu.DropDownItems.Add(self.windowNewMenu) self.ms.MdiWindowListItem = self.windowMenu self.ms.Items.Add(self.windowMenu) self.ms.Dock = DockStyle.Top self.MainMenuStrip = self.ms self.Controls.Add(self.ms) pass def windowNewMenu_onclick(self, sender, arge): MessageBox.Show("Thank you for clicking on me!", "windowNewMenu_onclick") pass def run(self): self.setupMenu() System.Windows.Forms.Application.Run(self) pass class MyButton(Button): def __init__(self, name, tooltip): self.Text = name self.toolTip = MyToolTip() self.toolTip.SetToolTip(self, tooltip) pass def setLocation(self, x , y): self.Location = Point(x,y) pass class MyToolTip(ToolTip): def __init__(self): self.AutoPopDelay = 5000 self.InitialDelay = 1000 self.ReshowDelay = 500 self.ShowAlways = True pass pass class MyFlowLayoutPanel(FlowLayoutPanel): def __init__(self): self.Size = size self.Dock = DockStyle.Fill self.AutoScroll = True pass pass ## end ##
今回はスクリプト本体にイベントハンドラ用の(call_back_*)メソッドを作成しました。 メニューのようにMyFlowLayoutPanel側にメソッドを作成することもできましたが、柔軟性を確保するためにイベントハンドラは基本的にライブラリ側には置かないのが良いのだろうと思います。
Pythonはインデントに依存するため、if文やメソッド定義をくくるためにブレースなどの特別な記号は使いません。
その代りにスクリプトではpass
文を使っています。
これ自体に機能はありませんが、インデントが崩れたり、インデントレベルの違うコードをコピーしてきた時でもpass
を目印にインデントを調整する事ができるようになります。
エディタが狂ってしまい行頭の空白が全て潰れてしまった様子を想像してみてください。
pass
文は非常にコストの低い予防策になるでしょう。
全てのインデントが下がる手前に挿入しなければいけないので、少し難しいですけどね。
勝手に追加したコードのせいもありますが、以前作成したスクリプトとの行数での比較でみると2倍前後の差があります。 これで効率が高まったといえるのかどうか微妙な数字ですが、個人的には余計な機能を追加するぐらいの余裕はでてきたのかなと感じています。
ライブラリを作成する意義に迫るためにも、次回はテストについて考えていきます。
プログラミングをする上でのテストには、いろいろな考え方がありますが、フェーズ(時期)の区切りで単体テスト、統合テストという概念があります。 そして、テストの入出力(内容)に注目してホワイトボックステストやブラックボックステストという考え方があります。 後はだいたいそのバリエーションで、フェーズによって役割りが変化しますが、テストの目的は常に対象がその時期で必要とされている状態にあるか確認するもので、テストの方法自体にはそれほど違いはありません。
最終的な統合テストは必要ですが、比較的規模の小さいプログラミングでは、開発しながら行なう単体テスト+ホワイトボックステストの役割が大きいと思います。 少なくとも設計した本人は部品がどのような形で使われるのか把握していますから、テストを短い間隔で網羅的に実行できれば効率が高まると考えられます。
そんな概念を土台にするとGUI系のプログラミングでは、自動化できるテスト範囲は非常に限られています。 とはいえIronPythonでは動的に内部状態にアクセスできますから、特別なツールを使わなくても確認できる範囲は静的な言語と比べれば広いといえます。
今回はプロパティが正しければ表示は正しく行なえているはず、という立場を取り、プロパティをチェックする方向で自動化を行なってみました。 そのため実際に画面に部品を表示させる事は行なっていません。
IronPython 2.6に標準に添付されているCPythonのunittest.pyファイル(C:\IronPython26\Lib\unittest.py)を使いました。
# coding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # import unittest import a07_lib from a07_lib.mylib import * import System class MyTestCase(unittest.TestCase): def setUp(self): self.formtitle = "Hello World" self.form = MyForm(self.formtitle) self.layout = MyFlowLayoutPanel() self.tooltip = a07_lib.mylib.MyToolTip() def test_formtitle(self): self.assertEqual(self.form.Text, self.formtitle) def test_formwidth(self): self.assertEqual(self.form.Size.Width, 300) def test_layoutwidth(self): self.assertEqual(self.layout.Size.Width, 300) def test_formheight(self): self.assertEqual(self.form.Size.Height, 200) def test_layoutheight(self): self.assertEqual(self.layout.Size.Height, 200) def test_layoutdockstyle(self): self.assertEqual(self.layout.Dock, System.Windows.Forms.DockStyle.Fill) def test_layoutscroll(self): self.assertTrue(self.layout.AutoScroll) def test_tooltippopdelay(self): self.assertEqual(self.tooltip.AutoPopDelay, 5000) def test_tooltipinitdelay(self): self.assertEqual(self.tooltip.InitialDelay, 1000) def test_tooltipreshowdelay(self): self.assertEqual(self.tooltip.ReshowDelay, 500) def test_tooltipshowalways(self): self.assertTrue(self.tooltip.ShowAlways) unittest.main()
実行結果は次のようになります。
C:\IronPython26\proj.class>..\ipy a07_unittest.ipy ........... ---------------------------------------------------------------------- Ran 11 tests in 0.531s OK
このスクリプトは"a07_lib.mylib"からimportするように宣言する事で、__init__.pyの中の__all__行を無視するようにしています。 しかし"mylib.py"の中にある__all__行を参照するため、"MyToolTip"クラスにはアクセスできません。
そこで__all__行で公開していないMyToolTipクラスにアクセスするために、フルネーム(完全修飾名)でアクセスしています。
... import a07_lib ... self.tooltip = a07_lib.mylib.MyToolTip() ...
作成したスクリプトは最後のunittest.main()
によって、そのまま実行することができます。
これまで作成してきたスクリプトはそれなりの規模になってきました。 次章ではいろいろまとめてみようと思います。
ここでこれまで作成してきたライブラリは、行数でみた時にどれひとつオリジナルよりも短かくなったものはありませんでした。 そこでこれまでの作業内容を振り返ってみようと思います。
元々作成していたサンプルコードは、それぞれに共通しているコードがあまり含まれていませんでした。 ライブラリを利用して作成したサンプルではボタンには全てToolTipを付けて、フォームにはメニューが含まれています。
そこで以前作成したものにもToolTipやメニューをつけた上で比較をしてみます。
比較の対象はLines of Code(LOC)として、コメントと空行を含むファイルの全行数を比較しました。 全体で比較するとライブラリを含めても15%ほど行数の削減ができています。
無駄に機能を増やすのは若干後ろめたい気もしますが、小さいプログラムを分割するのは、あまり効果が期待できないことはこれまで見てきたとおりです。 しかしアプリの数が増え、共通化が進み、コードの重複部分が増えるに従ってコード全体が軽量になります。
問題は効率的なライブラリの作り方なんですけどね。 さて、ここからは個別のプログラム毎の比較を載せていきます。
1ファイルに全ての処理を書いたスクリプト
ライブラリを利用したスクリプト
CSVファイル
ライブラリ
ここまで作成してきたスクリプトではカンマを区切り記号としてsplitしただけで、CSVを正しく取り扱ってきませんでした。 IronPythonにはCPythonの標準ライブラリに含まれるcsv.pyは添付されません。 そこで.Netライブラリに含まれるクラスを使います。
これまで作成したファイルに次のようなCSVファイルを処理させると画面が崩れてしまいます。
name,age,address "yasu,hiro",20,AizuWakamatsu abe,25,"Yokohama Aoba-ku"
まずはコマンドライン上で動くアプリケーションからMicrosoft.VisualBasic.FileIO.TextFieldParserクラスを使用します。 このクラスはC#のサンプルがAPIリファレンスにありIronPythonからも問題なく使えそうです。
同じディレクトリに配置されたファイルから、他のファイルに書かれたクラスを呼びたい場合には、そのライブラリファイルからみたパスでimport文を使うことになります。 以下の例ではMyDataTableクラスのloadCSV()メソッドから"a09_lib\mycsv.py"で定義されたMyCSVクラスを参照するために、"a09_lib\mydata.py"に次のような宣言を利用しています。
... import mycsv from mycsv import MyCSV ...
sys.pathにあるフォルダ('.')からの相対パスではなくて、ライブラリの配置されている場所からのパスで書く必要のあるところが少し変っていると思います。
次のスクリプトと下記4つのライブラリ系ファイルを配置して動かすことができます。
# coding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # from a09_lib import * ## csv filename filedialog = MyOpenFileDialog() filename = filedialog.open() csv = MyCSV(filename) for r in csv.eachRow(): for i in r.eachItem(): print i + "\t", pass print ""
メインは今回作成した"mycsv.py"ファイルです。 その他に"mydata.py"ファイルからも"mycsv.py"で定義されたMyCSVクラスを参照しています。
次のような出力が得られます。
次はDataGridViewに、このファイルを読み込ませてみます。
以前作成したMyDataTableクラスを修正し、CSVファイルを正しく扱えるようにしました。 先ほどと同じCSVファイルを読み込ませると次のような表示になります。
CSVファイルではデータに改行文字を含める事もできますが、DataGridViewはデータの中の改行コードを無視されてしまいます。
スクリプト本体は次のようになりました。
# coding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # from a09_lib import * form = MyForm("Hello World!") ## csv filename filedialog = MyOpenFileDialog() filename = filedialog.open() ## setup datatable dt = MyDataTable() dt.loadCSV(filename) ## setup layout datagridview = MyDataGridView() datagridview.DataSource = dt form.Controls.Add(datagridview) form.run()
MyDataTableクラスの修正個所は次の通りです。
... def loadCSV(self, filename): reader = StreamReader(filename) cols = [] for c in reader.ReadLine().split(","): self.Columns.Add(c, System.Type.GetType("System.String")) cols.append(c) pass l = reader.ReadLine() while(l != None): row = self.NewRow() counter = 0 for item in l.split(","): row[cols[counter]] = item counter += 1 self.Rows.Add(row) l = reader.ReadLine() pass reader.Close() pass pass ...
... def loadCSV(self, filename): parser = MyCSV(filename) cols = [] rownum = 0 for r in parser.eachRow(): tablerow = None itemnum = 0 for item in r.eachItem(): if rownum == 0: self.Columns.Add(item, System.Type.GetType("System.String")) cols.append(item) else: if tablerow is None: tablerow = self.NewRow() tablerow[cols[itemnum]] = item pass itemnum += 1 pass rownum += 1 if tablerow is not None: self.Rows.Add(tablerow) pass pass pass ...
元々は1行目をヘッダと認識して別に処理を行なっていましたが、作成したMyCSVクラスは効率化のためにyield文を使っているため1行だけの処理を抜き出すのが難しくなっています。
特別なメソッドを準備しようかとも思いましたが、とりあえず呼び出し側でカラム、行の数を数えるカウンターを準備する事で回避しています。
今回作成したmycsv.pyライブラリファイルの内容は次の通りです。
# coding=Shift_JIS # @author: YasuhiroABE <yasu@yasundial.org> # import clr clr.AddReferenceByPartialName("Microsoft.VisualBasic") import Microsoft from Microsoft.VisualBasic.FileIO import TextFieldParser from Microsoft.VisualBasic.FileIO import FieldType import System class MyRow: def __init__(self, row): self.row = row pass def eachItem(self): for item in self.row: yield item pass pass def getItem(self): return self.row pass class MyCSV: def __init__(self, filename): self.parser = TextFieldParser(filename, System.Text.Encoding.GetEncoding("Shift_JIS")) self.parser.TextFieldType = FieldType.Delimited self.setDelimiters(',') pass def setDelimiters(self, delimiter): self.parser.SetDelimiters(delimiter) pass def eachRow(self): array = self.parser.ReadFields() while(array): row = MyRow(array) yield row array = self.parser.ReadFields() pass pass pass
行のデータにアクセスする時にはyieldするのが良いとは限らないので、getItem()メソッドを準備しています。
このクラスはCSVファイルの各列のデータは全て文字型だと仮定しています。 これはCSVファイルのエンコーディングを保持する限りは正しい仮定ですが、IronPythonで日本語を正しく扱うためには少し設定が必要です。
coding行と揃っていれば、Shift_JISでもUTF-8でもスクリプトに直接記述した文字をコントロールに表示させる事は可能です。 MyCSVクラスでは.NETの機能を使って、CSVファイルがShift_JISエンコーディングの前提で読み取っています。
単純にファイルの内容を1行づつ表示させるような処理であれば、decodeを使うこともできます。
name,age,address "yasu,裕",20,AizuWakamatsu abe,25,"Yokohama Aoba-ku"
f = open("a10_readfile.csv") for line in f: print line.decode("Shift_JIS")
IronPython 1.xの頃の解説を読むとコマンドプロンプトに出力される文字が化けてしまうのを避けるために"sys.setdefaultencoding()"を使っていたようですが、いまはそんな名前のメソッドはsysモジュールにはありません。
ファイルから読み込んだ文字列をコマンドプロンプトやGUIに渡したい場合には、ファイルのエンコーディングと同じエンコーディングをdecodeメソッドに渡せば扱えるようです。 ファイルはUTF-8でもShift-JISでも正しく指定すれば大丈夫なようです。
# encoding: UTF-8 # @author: YasuhiroABE <yasu@yasundial.org> # import System import clr clr.AddReference("System.Windows.Forms") from System.Windows.Forms import * clr.AddReference("System.Drawing") from System.Drawing import * form = Form() form.Size = Size(240,100) label = Label() label.Size = form.Size f = open("a11.utf8.txt") for line in f: print line.decode('utf8') label.Text += line.decode('utf8') form.Controls.Add(label) Application.Run(form)
確認のためだけなので横着をしてライブラリは使わずにスクリプトを組み立てました。
スクリプトを変更し、"all.sjis.txt"を読み込みline.decode('Shift_JIS')
で変換しても同じ結果が得られます。
とりあえず自分がスクリプトを始める前に調べておきたい内容はだいたいまとまったので、ここで区切りを付けようと思います。
次はデバッグ用に入出力にパイプを付けた簡易Webブラウザでも作ってみようと思います。
Created: 2010-03-13, Last modified: 2010-03-19
www.yasundial.org by Yasuhiro ABE is licensed under a Creative Commons Attribution 2.1 Japan License. Permissions beyond the scope of this license may be available at http://www.yasundial.org/info/license.html.