LoginSignup
6
7

IronPython との付き合いかた

Last updated at Posted at 2024-02-19

IronPython 3.4 の正式リリース(2022年12月)から1年以上が過ぎました。
マイナーな存在ですが、Python と .NET のいいとこ取りができるので、個人利用の小規模なスクリプトを書くのに適しています。

.NET 8 もリリースされた機会に、備忘録を兼ねて IronPython との付き合い方をまとめておきます。

(2024.3.6 追記) 続編を投稿しました。

確認環境

  • Windows 11 Pro(Ver. 23H2)
  • .NET Runtime 8.0.2 (64-bit)
  • IronPython 3.4.1 (3.4.1.1000)

IronPython のインストール

Windows 以外にも .NET や Mono(www.mono-project.com) 環境があれば、Linux, Mac を問わず動作します。(なお、Linux へのインストールはすでに投稿しています。)

IronPython の手動インストール

まずは、こちらから IronPython.3.4.1.zip をダウンロードします。

zip ファイルを適当な場所に展開し、「net6.0」と「lib」の2つのディレクトリを任意の場所にコピーして完了です。zip は削除して構いません。

配置例
c:\Users\User\IronPython
└── 📁 3.4
     ├── 📁 net6.0
     └── 📁 lib

msi ファイル(IronPython-3.4.1.msi)は ipy.exe をインストールします。
こちらは.NET Framework がターゲットです。

インストールの詳細はこちらでご確認を。

起動

dotnet コマンドに net6.0\ipy.dll を渡せば起動します。
.NET 8 で動かす場合は --roll-forward LatestMajor オプションが必要です。

dotnet --roll-forward LatestMajor "C:\Users\User\IronPython\3.4\net6.0\ipy.dll"

(参考)環境による起動メッセージの違い
Windows + .NET 8(dotnet ipy.dll で起動) <こちらが本記事の環境>

Linux + .NET 8(dotnet ipy.dll で起動)

Windows + .NET Framework 4.8(ipy.exe で起動)

import Systemは必須

.NET API を利用する場合は、最低でも import System が必要です。

整数を代入した変数 i に対して import System 前と後とで dir(i) を比較してみます。

IronPython
i = 123
# import System 前
[attr for attr in dir(i) if attr[:2] != '__']
# ['bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator',
#  'real', 'to_bytes']

# import System 後
import System
[attr for attr in dir(i) if attr[:2] != '__']
# ['Abs', 'Add', 'Clamp', 'Compare', 'CompareTo', 'CopySign', 'CreateChecked',
#  'CreateSaturating', 'CreateTruncating', 'DivRem', 'Divide', 'Equals',
#  'GetBitLength', 'GetByteCount', 'GetHashCode', 'GetShortestBitLength', 'GetType',
#  'GetTypeCode', 'GreatestCommonDivisor', 'IsEven', 'IsEvenInteger', 'IsNegative',
#  'IsOddInteger', 'IsOne', 'IsPositive', 'IsPow2', 'IsPowerOfTwo', 'IsZero',
#  'LeadingZeroCount', 'Log', 'Log10', 'Log2', 'Max', 'MaxMagnitude', 'MaxValue',
#  'MemberwiseClone', 'Min', 'MinMagnitude', 'MinValue', 'MinusOne', 'ModPow',
#  'Multiply', 'Negate', 'One', 'Parse', 'PopCount', 'Pow', 'ReferenceEquals',
#  'Remainder', 'RotateLeft', 'RotateRight', 'Sign', 'Subtract', 'ToBigInteger',
#  'ToBoolean', 'ToByte', 'ToByteArray', 'ToChar', 'ToDateTime', 'ToDecimal',
#  'ToDouble', 'ToInt16', 'ToInt32', 'ToInt64', 'ToSByte', 'ToSingle', 'ToString',
#  'ToType', 'ToUInt16', 'ToUInt32', 'ToUInt64', 'TrailingZeroCount',
#  'TryConvertFromChecked', 'TryConvertFromSaturating', 'TryConvertFromTruncating',
#  'TryConvertToChecked', 'TryConvertToSaturating', 'TryConvertToTruncating',
#  'TryFormat', 'TryParse', 'TryWriteBigEndian', 'TryWriteBytes',
#  'TryWriteLittleEndian', 'WriteBigEndian', 'WriteLittleEndian', 'Zero', 'bit_length',
#  'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

データ型について

Python の基本的な組み込み型は、.NET のクラスで実装されています。
intは要注意です。後で触れます。

Python .NET
object System.Object
int System.Int32
System.Numerics.BigInteger
float System.Double
str System.String
complex System.Numerics.Complex
bool System.Boolean

IronPython では、Python と .NET のクラスや属性を混用できてしまうので要注意です。

Python の str 型と String(System.String) クラスを混用してみます。

IronPython
# 例1
from System import String
str.Join(', ', map(chr, range(65, 70)))
# 'IronPython.Runtime.Map'

str.JoinJ は大文字)は .NET の String.Join メソッドとして解釈されます。
第2引数は “map オブジェクトの文字列表現” (ToString)になるため、想定外(?)の結果を返します。

IronPython
# 例1の補足 (map オブジェクトの文字列表現)
map(chr, range(65, 70)).ToString()
# 'IronPython.Runtime.Map'

String.joinj は小文字)は Python の str.join と解釈されます。

IronPython
# 例2
String.join(', ', map(chr, range(65, 70)))
# 'A, B, C, D, E'

おまけですが、String.Join メソッドでもmapをアンパックすれば要素を結合します。

IronPython
String.Join(', ', *map(chr, range(65, 70)))
# 'A, B, C, D, E'

Int32Double なども、intfloatと解釈され、Python 的に振る舞います。

IronPython
from System import Double, Int32

Int32(1.0)   # int(1.0) と解釈
# 1
Int32('1')   # int('1')
# 1
Double(1)    # float(1)
# 1.0
Double('1')  # float('1')
# 1.0

なお、下の例のように競合する記述は Python の解釈が優先されます。

IronPython
ca = 'XYZ'.ToCharArray()            # System.Array[Char] を作って...
print(ca)
# Array[Char](('X', 'Y', 'Z'))

System.String(ca)                   # System.String コンストラクタで 'XYZ' を期待しても...
# "Array[Char](('X', 'Y', 'Z'))"      str 関数として機能する

IronPython では 型を[ ]で囲みます。< >ではありません。

文字列を作る代替手段はいくらでもあるので大した問題ではありません。

IronPython
''.join(ca)               # 'XYZ'
System.String.Concat(ca)  # 'XYZ'

intは要注意

下の例のように整数データは int, Int32, System.Numerics.BigInteger(以下BigInteger)それぞれの属性を持ちます。

IronPython
j = 1234          # 整数型データ j に対して
j.bit_length()    # Python のメソッド (int.bit_length) も
# 11
j.ToString('e3')  # .NET のメソッド (Int32.ToString) も
# '1.234e+003'
j.IsEven          # .NET のプロパティ (BigInteger.IsEven) も使える
# True

Python3 の仕様に合わせるため、intInt32BigInteger2つを使った特殊な実装です。

特に .NET API で整数を扱う場合は要注意です。
たとえば int は .NET 目線で見ると「暗黙の型変換」が発生している場合があります。

IronPython
big = 0x7fffffff
print(f'Value : {big}   Type : {type(big)} <{big.GetType()}>')
# Value : 2147483647   Type : <class 'int'> <System.Int32>

big += 1
print(f'Value : {big}   Type : {type(big)} <{big.GetType()}>')
# Value : 2147483648   Type : <class 'int'> <System.Numerics.BigInteger>
# Int32 の最大値を超えると、内部では BigInteger に変換されている

big -= 1
print(f'Value : {big}   Type : {type(big)} <{big.GetType()}>')
# Value : 2147483647   Type : <class 'int'> <System.Numerics.BigInteger>
# Int32 には自動的には戻らない

詳細はこちらに記載されています。

コレクションについて

Python のリスト、タプル と .NET の System.Collections.Generic.ListSystem.Array などの主なコレクションは簡単に相互変換できます。

Python のコレクションを .NET のコレクションに変換

IronPython
# Python のコレクションを .NET のコレクションに変換する
from System import Array
from System.Collections.Generic import List

py_list = [1, 2, 3]
py_tuple = ('A', 'B', 'C')

List[int](py_list)
# List[int]([1, 2, 3])
List[str](py_tuple)
# List[str](['A', 'B', 'C'])

Array[int](py_list)
# Array[int]((1, 2, 3))
Array[str](py_tuple)
# Array[str](('A', 'B', 'C'))

渡した要素の型が指定と異なる場合、通常はエラーになりますが、Array は受け入れます。

IronPython
# List コンストラクタは指定以外の型を拒否する
List[int]([1, 2.5, 3])     # エラー(float 要素が入っている)
# TypeError: Error in IEnumeratorOfTWrapper.Current. Could not cast: from System.Double to System.Numerics.BigInteger
List[float]([1, 2.5, 3])   # エラー(int 要素が入っている)
# TypeError: Error in IEnumeratorOfTWrapper.Current. Could not cast: from System.Int32 to System.Double

# 型が混在する場合は object にします
List[object]([1, 2.5, 3])
# List[object]([1, 2.5, 3])

# Array コンストラクタは型変換して受け入れる
Array[int]([1, 2.5, 3])    # 小数部は切り捨てられる
# Array[int]((1, 2, 3))
Array[float]([1, 2.5, 3])
# Array[float]((1.0, 2.5, 3.0))

Array クラスのコンストラクタは言語(IronPython)側での実装なので型の緩さがあります。
また、Array 要素への代入でも同様に型変換されます。

.NET のコレクションを Python のコレクションに変換

listtuple に渡します。

IronPython
NET_List = List[str]()                                   # str型で空の List を作成
NET_List.Add('A'); NET_List.Add('B'); NET_List.Add('C')  # 3つの要素を追加
list(NET_List)
# ['A', 'B', 'C']
tuple(NET_List)
# ('A', 'B', 'C')

NET_Array = Array.CreateInstance(int, 3)                 # int型で要素数3の Array を作成
NET_Array[0], NET_Array[1], NET_Array[2] = 1, 2, 3       # 要素を代入
list(NET_Array)
# [1, 2, 3]
tuple(NET_Array)
# (1, 2, 3)

int問題」と回避策

通常は、Int32BigInteger の壁を IronPython が処理するので、ユーザーは意識する必要がありません。

例えば List[int] の実体として List[BigInteger] が作られます。
そこに Int32 インスタンスを渡しても受け入れます。

IronPython
NET_List = List[int]([1, 2])
print(f'Value : {NET_List}   Type : {type(NET_List)} <{NET_List.GetType()}>')
# Value : List[int]([1, 2])   Type : <class 'List[int]'> <System.Collections.Generic.List`1[System.Numerics.BigInteger]>
# List[BigInteger] が作られていることがわかる

j1, j2 = 12, 123          # j1, j2 は Int32 のインスタンス
print(f'Value : {j1}   Type : {type(j1)} <{j1.GetType()}>')
# Value : 12   Type : <class 'int'> <System.Int32>
print(f'Value : {j2}   Type : {type(j2)} <{j2.GetType()}>')
# Value : 123   Type : <class 'int'> <System.Int32>

NET_List[1] = j1          # List[BigInteger] に対して
NET_List.Add(j2)          # Int32 を問題なく代入・追加できる

NET_List
# List[int]([1, 12, 123])

ただし、受け入れない場合もあります。

IronPython
NET_Array = Array[int]([1, 2, 3])
print(f'Value : {NET_Array}   Type : {type(NET_Array)} <{NET_Array.GetType()}>')
# Value : Array[int]((1, 2, 3))   Type : <class 'Array[int]'> <System.Numerics.BigInteger[]>

NET_Array.SetValue(123, 2)                    # Int32 は受け付けない
# TypeError: Object cannot be stored in an array of this type.

NET_Array.SetValue((123).ToBigInteger(), 2)   # 回避策

NET_Array
# Array[int]((1, 2, 123))

IronPython は回避策として ToBigInteger メソッドを実装していますが、.NET API を使う場合は Int32 など、可能な限り .NET の整数型を明示する方が無難でしょう。

IronPython
from System import Int32
NET_Array = Array[Int32]([1, 2, 3])
NET_Array.SetValue(123, 2)                    # 問題ない

NET_Array
# Array[Int32]((1, 2, 123))

コレクションの操作について

IronPython は ListArray にもスライスやアンパックが使えます。
Arrayに対しては負のインデックスも許可しています。
いろいろ試しましたが、おおむね対応できています。

IronPython
NET_List = List[str](['A', 'B', 'C'])
NET_Array = Array[Int32]([1, 2, 3])

NET_List[:2]                           # スライス
# List[str](['A', 'B'])
NET_List[::-1]                         # スライスによるリバース
# List[str](['C', 'B', 'A'])
print(*NET_List, sep=' - ')            # アンパック
# A - B - C

[*map(lambda s: s.lower(), NET_List)]  # map
# ['a', 'b', 'c']
[*zip(NET_List, NET_Array)]            # zip
# [('A', 1), ('B', 2), ('C', 3)]

NET_Array[-1]                          # Array は負のインデックスも許可
# 3
NET_Array[-1:-3:-1]                    # スライス
# Array[int]((3, 2))
[i * 2 for i in NET_Array]             # 内包表記
# [2, 4, 6]
NET_Array + NET_Array                  # シーケンス演算(結合)
# Array[int]((1, 2, 3, 1, 2, 3))
NET_Array * 3                          # シーケンス演算(繰り返し)
# Array[int]((1, 2, 3, 1, 2, 3, 1, 2, 3))

それぞれのコレクションで全ての操作が等しくサポートされているわけではありません。

IronPython
# Array ではできても List ではできないこと
NET_List[-1]         # 負のインデックスはエラー
# ValueError: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')

NET_List + NET_List  # シーケンス演算も不可
# TypeError: unsupported operand type(s) for +: 'List[str]' and 'List[str]'
NET_List * 3
# TypeError: unsupported operand type(s) for *: 'List[str]' and 'int'

未解決の問題も残っています。

IronPython
[*map(int, List[str](['1', '3', '5', '7', '9']))]   # 問題なし
# [1, 3, 5, 7, 9]
[*map(int, Array[str](['1', '3', '5', '7', '9']))]  # 問題発生...
# TypeError: int() takes at most 3 arguments (6 given)

この事象は報告済みですが、問題の根は深いようです。

ImportError について

一部のクラスで import できないことがありますが、これは IronPython ではよくある事象です。
clr モジュールをインポートして、アセンブリへの参照設定 clr.AddReference(アセンブリ) で解決します。
アセンブリは .NET API ブラウザで確認できます。
LinkedList の場合は System.Collections.dll が参照先ですが ".dll" は外します。

LinkedList のエラーは参照設定で解決】

.NETCore と .NETFramework とでエラーになるクラスが異なる場合もあります。
また、参照するアセンブリも一部で異なります。

【.NETFramework では hashSet がエラーになる】

LINQ の利用

以下3行で LINQ の拡張メソッドが使えます。

IronPython
import System, clr
clr.AddReference('System.Core')
clr.ImportExtensions(System.Linq)

拡張メソッドはリストやタプルに対しても機能します。

Min メソッド と Max メソッド

普通に使えるのですが...

IronPython
[2, 5, 1, 4].Min()    # list
# 1
(9, -5, 0, 4).Max()   # tuple
# 9

int は要注意」の具体例

Int32BigInteger の混在によるトラブル例です。

IronPython
# 全要素が BigInteger なら問題ないが
[4000000000, 5000000000, 6000000000].Max()     
# 6000000000 

# Int32 と BigInteger が混在するとエラー
[1000000000, 2000000000, 3000000000].Max()
# ValueError: The parameter must be a BigInteger. (Parameter 'obj')
[3000000000, 2000000000, 1000000000].Max()
# ValueError: Object must be of type Int32.

int 型の明示で回避できます。

IronPython
# 解決策
[1000000000, 2000000000, 3000000000].Max[int]()
# 3000000000
[3000000000, 2000000000, 1000000000].Max[int]()
# 3000000000

文字列では min, max 関数と挙動が異なる

文字列については min, max関数と異なる結果を返します。
LINQ の MinMax メソッドは全角文字が混在しても辞書順です。

IronPython
# 'A' だけ全角文字
# min 関数 や max 関数はコードポイント順
min(['', 'B', 'c'])
# 'B'
max(['', 'B', 'c'])
# 'A'

# Min() や Max() は辞書順
['', 'B', 'c'].Min()
# 'A'
['', 'B', 'c'].Max()
# 'c'

変換関数の使用

変換関数を使う場合は System.Func を介します。
lambda 式も使えます。

IronPython
from System import Func             
(-9, 5, 2, -4).Max(Func[int, int](abs))
# 9
(-9, 5, 2, -4).Max[int](Func[int, float](lambda i: 1 / i))    # Max[int] の明示が必要
# 0.5

.NET6 で追加された MinByMaxBy メソッドは System.Func 不要です。

IronPython
(-9, 5, 2, -4).MaxBy(lambda i: abs(i))
# -9
(-9, 5, 2, -4).MaxBy(lambda i: 1 / i)
# 2

Sum メソッド と Average メソッド

次の2点に注意します。

  • AsEnumerable[T]()Cast[T]() を通す
  • 整数は int ではなく System.Int32 など、.NETネイティブな整数型にする
IronPython
# 失敗例
(-9, 5, 2, -4).Sum()
# TypeError: Sum() takes exactly 1 argument (0 given)
(-9, 5, 2, -4).AsEnumerable[int]().Sum()
# TypeError: Sum() takes exactly 1 argument (0 given)

# 成功例
from System import Int32
(-9, 5, 2, -4).AsEnumerable[Int32]().Sum()  # -6 AsEnumerable または
(-9, 5, 2, -4).Cast[Int32]().Sum()          # -6 Cast を使う

変換関数の使用

ここでも System.Func を介します。
Func を使うときは AsEnumerableCast は省略できます。

2乗和と2乗平均の例です。

IronPython
(-9, 5, 2, -4).Sum(Func[int, Int32](lambda i: i * i))
# 126
(-9, 5, 2, -4).Average(Func[int, Int32](lambda i: i * i))
# 31.5

その他応用例を示しておきます。

IronPython
# int と float の混在
[7.6, 2.5, -4, -1.1, 6].Average(Func[object, float](float))
# 2.2000000000000002

# 誤差を嫌う場合は Decimal で
from System import Decimal
[7.6, 2.5, -4, -1.1, 6].Average(Func[object, Decimal](Decimal))
# 2.2

# OfType[T] の使用
[7.6, 2.5, -4, -1.1, 6].OfType[float]().Average()  # float だけの平均
# 3.0
[7.6, 2.5, -4, -1.1, 6].OfType[Int32]().Average()  # Int32 だけの平均
# 1.0

# None が存在する場合は System.Nullable を使う
from System import Int32, Nullable
[1, None, 2, None, 3].AsEnumerable[Nullable[Int32]]().Average()
# 2.0

その他あれこれ

IEnumerable もアンパックできる

LINQ では多くのメソッドが IEnumerable を返しますが、こちらもアンパックできます。

IronPython
# dict を SelectMany に渡してリストに展開
cities = {'新潟県': ('新潟市', '長岡市', '上越市'), '宮城県': ('仙台市', '石巻市')}
[*cities.SelectMany(lambda pref: cities[pref], lambda pref, city: f'{pref} {city}')]
# ['宮城県 仙台市', '宮城県 石巻市', '新潟県 新潟市', '新潟県 長岡市', '新潟県 上越市']

メソッドチェーン

見やすくできます。

IronPython
# 上の例を print で出力
(cities
 .SelectMany(lambda pref: cities[pref],
             lambda pref, city: f'{pref} {city}')
 .ToList()
 .ForEach(print))
# 宮城県 仙台市
# 宮城県 石巻市
# 新潟県 新潟市
# 新潟県 長岡市
# 新潟県 上越市

GroupBy OrderBy の使用例

LINQ の GroupByitertools.groupby と異なり事前のソートは不要で、元データの順番を維持します。

IronPython
# 40個のランダムな整数を用意(最小0 最大99)
from random import randint
data = [randint(0, 99) for _ in range(40)]
# 値を10刻みでグループ分け
groups = data.GroupBy(lambda i: i // 10).OrderBy(lambda group: group.Key)

# データと結果を表示
from pprint import pprint
pprint(data, width=40, compact=True)
# [2, 76, 37, 8, 22, 66, 43, 18, 19, 4,
#  39, 54, 42, 61, 45, 78, 98, 44, 12, 46,
#  45, 0, 28, 40, 99, 56, 62, 12, 47, 25,
#  87, 46, 76, 0, 61, 7, 2, 53, 17, 72]

for group in groups:
    print(f'{group.Key * 10:2} -{group.Key * 10 + 9:3} : {[*group]}')
#  0 -  9 : [2, 8, 4, 0, 0, 7, 2]
# 10 - 19 : [18, 19, 12, 12, 17]
# 20 - 29 : [22, 28, 25]
# 30 - 39 : [37, 39]
# 40 - 49 : [43, 42, 45, 44, 46, 45, 40, 47, 46]
# 50 - 59 : [54, 56, 53]
# 60 - 69 : [66, 61, 62, 61]
# 70 - 79 : [76, 78, 76, 72]
# 80 - 89 : [87]
# 90 - 99 : [98, 99]

map, zip との組み合わせ

IronPython
# わかりにくいコレよりも...
[*zip(*[iter(range(15))] * 5)]
# [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14)]

# Chunk がわかりやすい
[*map(tuple, range(15).Chunk(5))]
# [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14)]

# zip(*) を使って転置行列を作成
[*zip(*range(15).Chunk(5))]
# [(0, 5, 10), (1, 6, 11), (2, 7, 12), (3, 8, 13), (4, 9, 14)]

参照渡しについて

.NET API で参照渡しを必要とする場合があります。

たとえば、Int32.TryParse メソッドの引数は「変換したい文字列」と「変換結果を受け取る変数の参照」です。

ただし、IronPython は TryParse などに「暗黙的に結果を受け取る」手段を用意しているので、通常は参照渡しを回避できます。

IronPython
Int32.TryParse('123')   # TryParse は「変換の成否」と「変換結果」をタプルで返す
# (True, 123)
Int32.TryParse('12-3')  # 変換できない場合
# (False, 0)

余談ですが、F# でも同様に実装されています。

参考までに、参照渡しの手順も示します。

IronPython
i_ref = clr.Reference[Int32]()  # 変換結果を受け取る変数(の参照)を作成
Int32.TryParse('123', i_ref)    # TryParse は成否を bool で返す
# True
i_ref.Value                     # Value プロパティで変換結果を得る
# 123

Int32.TryParse('12-3', i_ref)   # 変換できない文字列を渡す
# False
i_ref.Value                     # 変換に失敗すると 0
# 0

ファイル入出力について

単純なファイル出力であれば、with open('file', 'w') as f: よりも WriteAllLines が簡潔で使いやすいでしょう。リストのまま渡すこともできます。

IronPython
from System.IO.File import ReadAllLines, WriteAllLines

# リストをファイルに書き込む
dec_hex = ['10進\t2進'] + [f'{i:2}\t{i.ToString("B4")}' for i in range(16)]
WriteAllLines('Dec-Hex.txt', dec_hex)

【結果】

ファイルからの入力例として、ReadAllLines を使ってファイルの先頭5行を表示します。

IronPython
# Array.ForEach には Action で渡す
from System import Array, Action

# ファイルの先頭5行を表示
Array.ForEach(ReadAllLines('Dec-Hex.txt')[:5], Action[str](print))
# 10進    2進
#  0      0000
#  1      0001
#  2      0010
#  3      0011

日付の処理について

記事の趣旨から逸脱して .NET API の紹介ぽくなりますが、備忘録としての観点で書いておきます。

和暦の処理に絞ったメモです。こちらの記事を参考にさせていただきました。

和暦から西暦へ

こちらは非常に簡単です。

IronPython
# 和暦を西暦に変換する
from System import DateOnly
DateOnly.Parse('R6.1.1').ToLongDateString()          # 元号の略号(R)もO.K.
# '2024年1月1日'
DateOnly.Parse('令和6年1月1日').ToLongDateString()    # 漢字表記もO.K.
# '2024年1月1日'

DateOnly.Parse('S100.1.1').ToLongDateString()        # 来年は昭和100年
# '2025年1月1日'

西暦から和暦へ

カルチャを設定して

IronPython
from System.Globalization import CultureInfo, JapaneseCalendar
ci = CultureInfo('ja-JP')
ci.DateTimeFormat.Calendar = JapaneseCalendar()

カルチャ付き ToString で和暦を取得できます。

IronPython
DateOnly(2024, 1, 1).ToString('gy年M月d日', ci)
# '令和6年1月1日'

元年表記にも対応します。

IronPython
d = DateOnly(2019, 4, 30)
d.ToString('gy年M月d日', ci)               # 改元前日
# '平成31年4月30日'
d.AddDays(1).ToString('gy年M月d日', ci)    # 改元初日
# '令和元年5月1日'

補足

和暦を Parse する場合、カルチャを指定すると「元年」も受け付けます。

IronPython
DateOnly.Parse('令和1年5月1日').ToLongDateString()
# '2019年5月1日'

# カルチャを指定しない場合「元年」はエラー
DateOnly.Parse('令和元年5月1日').ToLongDateString()
# SystemError: String '令和元年5月1日' was not recognized as a valid DateOnly.

# カルチャを指定するとO.K.
DateOnly.Parse('令和元年5月1日', ci).ToLongDateString()
# '2019年5月1日'

まとめ

  • スクリプトの先頭に最低限書いておくもの
IronPython
import System, clr
from System import Int32, String
  • コレクションを使うために入れておきたいもの
IronPython
from System import Array, Action, Func
from System.Collections.Generic import List
clr.AddReference('System.Core')
clr.ImportExtensions(System.Linq)
  • ファイル処理をする場合入れておきたいもの
IronPython
from System.IO.File import ReadAllLines, WriteAllLines
  • 可能な限り intInt32 にする

参考

本家 Python との違いもありますので、こちらもご確認ください。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7