やっぱ真っ先に思いつく需要はこれだろてことなのだが。
入り口だけには立っときたいなと思って少し格闘し始めて、すぐに「ブチ当たる」。
まず Microsoft.Office.Interop.Excel に限らず、の基礎的な話から。
.NET アッセンブリのうち、「皆が喜んで使えるもの」は、「GAC (Global Assembly Cache)」に登録されている。ワタシはちょっと誤解していたんだけれど、.NET 前夜の「COM」では、同じように「登録」という行為が必要な点は変わらないんだけれど、COM 流儀と .NET 流儀は全然シカケが違うんだね。
COM は、1. まずは自身のアイデンティティを GUID で主張する、2. COM 語でおはなしするための窓口のネェちゃんを IDL で定義する、3. これをレジストリに登録する、てのがまぁ「雑な」説明なんだけれど、「COM ライブラリを開発する」という行為をする場合、1. と 2. が「開発工程」の作業、3. がインストール時の作業(開発時も自身が使うために同じ行為が必要)ね。
上で「皆が喜んで使えるもの」なんてヘンチクリンな言い回しをしたのは、.NET が、「COM では「不可欠」として強制された一連のパブリッシュ手続き」が、.NET では一段階緩くなっていて、「ご自分にしか使わんもんならラフにやってよろしい」つーことらしいのね。つまり自家発電用途のものを作って使うぶんには、これでワタシが不安に感じたことは「杞憂」、アレでいいらしい。対して「皆が喜んで使える清く正しいライブラリ」を作って公開したいと願った場合に初めて「COM 流儀にあったような厳格な規約に従わねばならぬ」と。けどそれは COM のそれとはかなり違って、「厳格な命名(とバージョニング)規則に従わなければならない/public keytoken を主張しなければならない」という点が「そーね厳格ね」なのだが、まぁ登録は言っちゃえば「決められた場所に配置するだけ」。置き場所の規則も厳格だけれど、レジストリに魔法が潜むなんてことはなくて、ほんと「置くだけ」(実際は手で置くんじゃなく gacutil を使うみたい)。
でその「厳格な規約に従ってくれたありがたーいライブラリたち(だけが GAC に登録出来る)」が、まぁ開発者目線では「GAC のフォルダにほいほい置いてあるだけ」にみえる。場所は古いものが c:/Windows/assembly
に、新しいものが c:/Windows/Microsoft.NET/assembly
(.NET 4.0 以降) にいる。なのでここにいる連中は基本、pythonnet から Assembly.LoadWithPartialName
や clr.AddReference
でロード出来る。
てわけで、GAC を漁れば Microsoft.Office.Interop.Excel
とか Microsoft.Office.Tools.Excel
がすぐに見つかる。無論これまで何をインストールしてきたかとか、プリインストールの状態とかにもよるけどとにかく「自分ちで使えるもの」はすぐにわかる。(COM は大変だったのよ、COM/OLE ビューワなる専用のビューワで探索するしかなかったのだが、まぁこれが「膨大」でなぁ。)
さぁ始められるぞ…:
1 import clr
2 from System.Reflection import Assembly
3 #clr.AddReference("Microsoft.Office.Interop.Excel") # NG.
4 Assembly.LoadWithPartialName("Microsoft.Office.Interop.Excel")
5 import Microsoft.Office.Interop.Excel as Excel
6
7 xlApp = Excel.Application()
ここまでは良かったの。で、例えば Read Excel File in C# を真似していきゃすぐだろ、て思うわけだ。けどこれがダメなのね。
どうやら「Excel を扱う .NET ライブラリ」は「本物の .NET と本物の COM」のミックスで実現されているらしく、使いたい Workbook
などが全て .NET で包まれてない模様。これを .NET for Python (pythonnet) からストレートに扱えない:
1 Traceback (most recent call last):
2 File "xxx.py", line 26, in <module>
3 xlApp.Workbooks.Open("new-book.xlsx")
4 AttributeError: '__ComObject' object has no attribute 'Open'
これは 公式の issue として挙がっている。面白いというか救いなのが、issue を挙げた本人(Owner)が 「解決策」を挙げていることだったりもする。.NET for Python に取り込まれてくれたらありがたいが、「そんなもんです」と受け容れてしまうのもないではないか…。この解決策はまだ試みてないけど、これでイケるようなら、これまでは「Excel をフルに扱えるまともなもの」は
- pyExcelerator
- xlrd, xlwt, xlutils
- PyWin32 の COM/OLE 連携
しかほとんどないに等しかったけれど、選択肢が一つ増えてハッピーだね、てね。特に最後の PyWin32 とは完全に競合し、PyWin32 を「捨ててもいい」となりそうなのが個人的には嬉しかったりはする。
なお、「.NET for Python で本物の COM を使う」方法は公式ドキュメントに書かれている。けど今回のこの問題とは違うような気はする。違わないのかもしれないけれど、ただ、今回のヤツって、「Application オブジェクト」を取るとこまではほかの .NET 使いと全然変わらんのよね、だから「違う」んだと思う。
21:10 追記:
イケた:
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import
3 from __future__ import unicode_literals
4 from __future__ import print_function
5
6 import os
7
8 import clr
9 from System.Reflection import Assembly
10 #clr.AddReference("System.Reflection")
11 from System.Reflection import BindingFlags
12
13 #
14 Assembly.LoadWithPartialName("Microsoft.Office.Interop.Excel")
15 import Microsoft.Office.Interop.Excel as Excel
16
17 from System import Array
18 import System
19
20 #
21 class comobj(object):
22 """
23 TNX:
24
25 https://gist.github.com/denfromufa/ec559b5af41060c5ac318f7f59d8b415#file-excel_interop_vsto-ipynb
26
27 """
28 def __init__(self, obj):
29 #AttributeError: _comobj__ComObject???
30 #if not isinstance(obj, clr.System.__ComObject):
31 # raise TypeError('Type not System.__ComObject for obj {}'.format(type(obj)))
32 self.obj = obj
33 self.typ = obj.GetType()
34
35 def __getattr__(self, name):
36 def newm(*argsv):
37 if not argsv:
38 argsv=(None,)
39 return self.typ.InvokeMember(
40 name,
41 BindingFlags.InvokeMethod | BindingFlags.GetProperty,
42 None, self.obj, *argsv)
43 return newm
44
45
46 #
47 xlApp = Excel.ApplicationClass()
48 xlApp.Visible = True
49
50 books = comobj(xlApp.Workbooks)
51 arrobj = Array[System.Object]
52 arr = arrobj.CreateInstance(System.Object, 1)
53 arr[0] = os.path.abspath("book.xlsx") # existing book
54 books.Open(arr)
55 books.Close()
まぁ「.NET や COM 経由で Excel を扱う」こと自体が苦痛な作業ではあるわけで、「おぉすげーぜ」と思うためにはそもそも「Python らしいことをしたいから」というモチベーションが不可欠だよ。例えば「計算は NumPy 使ったるぜをら」とかさ。そうでないなら Python から扱えても「うれしぅない」と思うのが普通。そしてやってみた例からわかるように、「C# より遥かにダルい」のは明白、「comobj 問題」がなくても。