UniPy

マニュアル

製品紹介ページへ »


UniPyで出来ない事

UniPyは基本的には文法を変換するだけのトランスコンパイラである為、
本来Pythonを採用する場合の大きな要因の一つであろうPythonの豊富なライブラリ(API)を使用する事は出来ません。
ライブラリ(API)は変換先の言語に合わせてSwift(Foundation等)やC#(.NET)のものを使用する必要があります。

ビルトイン関数strオブジェクトのメソッド等、
一部に限ってだけそれに対応するSwift/C#(.NET)のメソッドに自動変換する機能を備えてはいます。

PEGに沿った文法解析はおこなっていない為、文法解析ミスで上手く変換出来ない場合があります。
特に、式や文中に余計な空白文字があると、正しくパース出来なくなります。
基本的にはPEP8に準拠した記述する事で、UniPyでパース出来ないパターンを回避する事が出来ます。

また、一行に複数の式を詰め込まず、一度変数に代入する等して処理を複数行に分け、
一行の文をシンプルにすると、正常にパース出来る様になる場合もありますのでお試しください。

UniPyで正しく変換する為の記述仕様

Pythonは動的型付け言語ですが、UniPyでコードをSwift/C#に変換する為には、
静的型付け言語であるSwfit/C#と同じく、変数の型を意識してコードを記述する必要があります。
具体的には、型ヒントの指定が必須です。

次の様に記述します。

文字列型の変数hogeを定義する例:

これは次の様に変換されます。


int型の引数を受け取り文字列を返す関数(メソッド)を定義する例:

これは次の様に変換されます。

初期値のない変数宣言の書き方(Dummy.initについて)
UniPyでは初期値のない変数の定義はそのままでは上手く変換出来ません。
hoge: str  # これは上手く変換出来ない
空文字等を初期値として指定すれば済む話ですが、
そうではなくどうしても初期値の無い変数宣言に変換する必要がある場合は、
仮の代入値としてDummy.initを使用します。
unipy_stubs/__init__.py内に定義されています。Any型のクラス変数です。
hoge: str = Dummy.init  # ダミー初期値として記述する

これは次の様に変換されます。

型推論させる場合の変数宣言の書き方
型ヒントの指定にVar型を使用します。
unipy_stubs/__init__.py内にAnyのTypeAliasとして定義されています。

これは次の様に変換されます。

クラスHogeに空のコンストラクタとデストラクタを定義するだけの例:

これは次の様に変換されます。

普通に__init__内にselfを付けて変数として定義します。

Swift/C#に変換時に__init__外に移動されます。

これは次の様に変換されます。

静的メンバはクラス変数として定義します。

静的メソッドは関数定義時に@staticmethodデコレータを付与します。

これは次の様に変換されます。

PEP8の命名規則に従って、_(アンダースコア)から始まる名前で定義します。

これは次の様に変換されます。

property関数を使用します。
getterのみsetterのみの指定にも対応しますが、deleterには対応していません。
__(アンダースコア二つ)から始まる関数名を指定するとprivateキーワードが付きます。

または@propertyデコレータを使用する方法がありますが、
この方法はSwiftのgetterへの変換にのみ対応しています。

これは次の様に変換されます。

Python3.12以降の関数名の後に[T]を直接記述する方法には対応していません。

Python3.11以前のTypeVarを使う方法に対応しています。

これは次の様に変換されます。

Swiftに変換する場合と、C#に変換する場合で、記述が異なります。

typingモジュールのcast関数を使用します。

クラスBaseClassとそのサブクラスSubClassを定義し、
そのオブジェクトをダウンキャストアップキャストして変数に代入する例:

アップキャストは暗黙的に行われる為、本来はcast関数を使って明示する必要はありません。

Swiftに変換する場合と、C#に変換する場合で、キャスト自体の書き方は同じですが、他に必要な記述が異なります。

@attributeデコレータ、またはattribute関数を属性を付与したい定義の前の行に記述します。
デコレータ(関数)はunipy_stubs/__init__.py内に定義されているUniPy特有の関数です。

Swiftに変換する場合と、C#に変換する場合で、属性(アトリビュート)の書き方は同じです。
以下の例はそれぞれ具体的な属性(アトリビュート)使用例記載の為、SwiftとC#で別な内容になっています。

スライスの変換にも対応しています。
ステップ [start:stop:step] の指定には対応していません。

int型のリストを定義して、中身の一部をスライスしてprint出力する例:

これは次の様に変換されます。

intがキー、文字列が値の、空の辞書(a_dic)定義して、項目を追加し、一つをprint出力する例、と、
文字列がキー、intが値の、初期値のある辞書(b_dic)を定義して、
キーと値をそれぞれリストにして、print出力する例:

これは次の様に変換されます。

これは次の様に変換されます。

PythonとSwift/C#でループ変数のスコープが異なる点は注意が必要です。

リストと辞書の変数を定義し、それを直接回す例と、range関数を使って要素数分回す例:

これは次の様に変換されます。

以下の様に変換されます。

  • elif else if に。
  • and && に。
  • or || に。
  • not ! に。
  • in は containsメソッドの呼び出し に。

==,!=,<=,>=,<,>等の比較演算子の記法は言語間で違いがない為、変換処理はありません。

これは次の様に変換されます。

ネストされている等複雑な文は正しく変換出来ません。なるべくシンプルな文にしてください。

0~4までループし、偶数と奇数でそれぞれ別な文字列をprintする例:

for i in range(5):
    print("odd" if (i % 2) != 0 else "even")

これは次の様に変換されます。

これは次の様に変換されます。

int型とstr型の二つの要素のあるタプルを定義してそれにアクセスする例:

これは次の様に変換されます。

タプルの要素へのアクセス(インデックス指定)の制限
  • 0~9まで対応しています。それ以上のインデックスの変換は対応していません。
  • 静的値である必要があります。動的値を指定された場合の変換には対応していません。
  • SwiftやC#ではPythonの様にfor文でタプルの要素を回す事はそのままでは出来ません、
    現状UniPyでよしなに変換する事も出来ません。

enumモジュールのEnumクラス、またはIntEnumクラスの子クラスとして定義します。

from enum import Enum, IntEnum, auto


class Season(IntEnum):
    Spring = 1
    Summer = 2
    Autumn = 3
    Winter = 4


class ColorRGB(Enum):
    Red = auto()
    Green = auto()
    Blue = auto()

これは次の様に変換されます。

クラスHogeに、関数を引数で受け取るaction_worldを定義し、その引数をlambda式で記述して呼び出す例:

これは次の様に変換されます。

ネストされている等複雑な文は正しく変換出来ません。なるべくシンプルな文にしてください。

lst1から"o"を含む要素だけを抜き出し大文字に変換したlst2をリスト内包表記で作成する例:

これは次の様に変換されます。

これは次の様に変換されます。

引数を参照で受け取る関数を定義するには、_ref_またはref_で始まる名前で引数を定義します。

参照で引数を渡すには、ref関数を使用します。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。

クラスHogeに、二つの引数を参照で受け取る関数inout_worldを定義し、その関数を呼び出す例:
(ref_bbの方の引数は'名前付き引数(キーワード引数)'として呼び出しを記述)

これは次の様に変換されます。

クラスHogeに、可変長引数のある関数params_worldを定義し、その関数を呼び出す例:

これは次の様に変換されます。

Pythonのコメントとして次のように定義し、
"""native

"""
中にSwift/C#のコードを記述します。必ずnativeの文言から始めて改行して内容を書く必要があります。

native内に書いた内容はUniPyで何も変換処理されずそのまま変換後のSwift/C#ソースに書き出しされます。

UniPyが対応していない記法をする場合や、上手く変換出来ない場合に使用してください。

クラスBaseClassに、関数call_from_nativeを定義し、その関数をUniPy変換を通さず呼び出す例:

これは次の様に変換されます。

Pythonのコメントとして次のように定義し、
# no_unipy start

# no_unipy end
中に変換されたくないPythonのコードを記述します。

二つのコメント文の内に書いた内容はUniPyで何も変換処理されず変換後のSwift/C#ソースに書き出しもされません。

IDE上でのエラーを回避する為のダミー定義を用意する場合等に使用してください。

関数over_worldを三つ定義し(但し一つはUniPyで変換しないMypy用のダミー定義)、それを呼び出す例:

これは次の様に変換されます。

オーバーロードの書き方について

Swift/C#ではオーバーロードの定義に特別なキーワードは必要ありません。
しかしPython上では、Mypyでチェック行なっている場合、次の様な'定義重複のエラー'となります。

このエラーはUniPyの変換には影響ありませんが、このエラーを回避するには、
上記のPythonの例の様に、typingモジュールの@overloadデコレータを使用して記述してください。

unipy_stubs/内にスタブファイルが用意されているモジュールについては、
そのモジュールをimportする事で該当するSwift/C#のモジュールがimport/usingされます。

スタブファイルが用意されていないモジュールについては、
"""native~"""の書き方を使用してください。

C#への変換の場合、コメント文としてusing文を記述する事で逆にコメントが除去されて変換される機能も使えます。

@overrideデコレータを使用します。
IDEの実行環境がPython3.12以降の場合、typingモジュールに含まれています。
IDEの実行環境がPython3.11以前の場合の為に、unipy_stubs/__init__.py内に定義されています。
(※IDEの実行環境のPythonバージョンはUniPyの変換においては関係ありません。
あくまでIDEのエディタ上の参照関係を解決する為の指定の話です。)

C#への変換の場合、
上書きされるメソッドに@virtualデコレータの指定も必要です、virtualキーワードに変換されます。
unipy_stubs/__init__.py内に定義されています。
Swiftへの変換の場合は必要ありませんが、指定しても除去される為問題はありません。

クラスBaseClassに定義した関数ride_worldを、そのサブクラスSubClassで上書きし、
クラスHogeからそれらを呼び出して確認する例:
SwiftとC#でPythonのコード例を共通化する為に、クラスインスタンス化時にnew関数を噛ませています、
Swiftへの変換の場合は必要ありませんが、指定しても除去される為問題はありません。
詳しくは "C#への変換にのみ必要な記述仕様 - クラスのインスタンス化" を参照してください。

これは次の様に変換されます。

_tcから始まる名前で内部関数として定義します。
呼び出し部分でSwiftではトレイリングクロージャ、C#では匿名クロージャに変換されます。

これは次の様に変換されます。

asyncキーワードを使って非同期関数を定義し、
awaitキーワードを使って非同期関数を呼び出します。

非同期関数を並列呼び出しするには、asyncioモジュールのcreate_task関数を使用します。

また、同じくasyncioモジュールのrun関数が、
SwiftではTask、C#ではTask.Runを使った非同期関数の呼び出しに変換されます。

クラスHogeに非同期関数async_loopとasync_worldの二つを定義し、
クラスHogeのinit内でasync_worldを呼び出し、
async_worldからasync_loopを二つ並列実行しその二つの結果を待って返し、printする例:

これは次の様に変換されます。

# #ifのようにコメントとして記述すると、最初の#が除去されて変換されます。
#if #else #endif に対応しています。(または、"""native~"""の使用を検討してください)

# #if DEBUG
print("Debug version")
# #else
print("Release version")
# #endif

これは次の様に変換されます。

例外のキャッチは標準のPythonと同じように記述します。

以下にない組み込み関数の変換には対応していません。

以下にある組み込み関数でもPythonと同等の機能を提供する変換を行えるとは限りません。

以下にないstrオブジェクトのメソッドの変換には対応していません。

以下にあるstrオブジェクトのメソッドでもPythonと同等の機能を提供する変換を行えるとは限りません。

Swiftへの変換にのみ関係する記述仕様

typingモジュールのFinalを使用します。

これは次の様に変換されます。

let hoge: String = "Hello";

オプショナルとして変数を定義するには、
typingモジュールのOptionalを使用します。

オプショナルの変数をアンラップするには、
unwrap関数を使用します。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。

これは次の様に変換されます。

var hoge: Optional<String> = nil;
print(hoge!);

データクラス(構造体(struct))の定義には、
dataclassesモジュールの@dataclassデコレータを使用します。

from dataclasses import dataclass
from typing import ClassVar


@dataclass
class HogeData():
    aa: str = ""
    Bb: ClassVar[int] = 0


hd: HogeData = HogeData(aa="AA")

これは次の様に変換されます。

struct HogeData
{
    var aa: String = "";
    static var Bb: Int = 0;
}
var hd: HogeData = HogeData(aa: "AA");

@mutatingデコレータを使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。

これは次の様に変換されます。

struct HogeData
{
    var count: Int = 0;
    init()
    {
    }
    mutating func add_count() -> Void
    {
        count += 1;
    }
}

sharp_selector関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。

これは次の様に変換されます。

class Hoge
{
    init()
    {
        Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Hoge.sel_method), userInfo: nil, repeats: false);
    }
    @objc
    func sel_method() -> Void
    {
        print("Hello #selector World");
    }
}

some型でラップして定義します。
unipy_stubs/swift_api/swift.py内でAny[T]のTypeAliasとして定義されているUniPy特有の型です。

これは次の様に変換されます。

struct HogeView : View
{
    var body: some View
    {
        return EmptyView();
    }
}

二通りの書き方が出来ます。

一つは、
viewbuilder型を戻り値として関数を定義します。
unipy_stubs/swift_api/swift.py内でAnyのTypeAliasとして定義されているUniPy特有の型です。

もう一つは、
@attributeデコレータにViewBuilderクラスを指定し、戻り値にsome[View]を指定する方法です。

これは次の様に変換されます。

struct HogeView : View
{
    var body: some View
    {
        return VStack() {
            self.my_content1();
            self.my_content2();
        }
    }
    @ViewBuilder func my_content1() -> some View
    {
        Text("Hello World 1");
    }
    @ViewBuilder
    func my_content2() -> some View
    {
        Text("Hello World 2");
    }
}

SwiftUIListと書くとListに変換されます。
unipy_stubs/swift_api/swift_uiスタブでもListがSwiftUIListとして定義されています。
typingモジュールのListと名前が重複してしまう為の処置です。

これは次の様に変換されます。

struct HogeListView : View
{
    var body: some View
    {
        return List() {
            ForEach(0..<5) { i in
                Text(i.description);
            }
        }
    }
}

keypath関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。

引数の違いで三通りに変換されます。

KeyPathオブジェクトを定義する場合、
第1引数に文字列としてタイプネームとパスを指定します。

keyPathで値を読み出す場合、
第1引数に対象のオブジェクト、第2引数にKeyPathオブジェクトを指定します。

keyPathで値を代入する場合、
第1引数に対象のオブジェクト、第2引数にKeyPathオブジェクト、第3引数に値を指定します。

これは次の様に変換されます。

var hd: HogeData = HogeData();
var kp: KeyPath<HogeData, Int> = \HogeData.count;
var c: Int = hd[keyPath: kp];
print("Read by keypath: \(c)");
var kp2: WritableKeyPath<HogeData, Int> = \HogeData.count;
hd[keyPath: kp2] = 99;
print("Written by keypath: \(hd.count)");

self_関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。

また、型ヒントにtype[X]を指定すると、X.Typeに変換されます。

これは次の様に変換されます。

var t: HogeData.Type = HogeData.self;
print(t);

バインディングして変数を渡すには、binding関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。
binding関数の引数に指定した変数が$が付いた名前に変換されます。

@Binding属性が指定された変数に値を代入するには、unbinding関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。
代入先の変数が_(アンダーバー)が付いた名前に変換されます、
他のUniPy用関数に比べて特殊な変換のされ方なので注意してください。

これは次の様に変換されます。

struct Binding_SheetView : View
{
    @Binding
    var text: String;
    init(text: Binding<String>)
    {
        self._text = text;
    }
    var body: some View
    {
        return Button("Change Text") {
            self.text = "Hello Binding!";
        }
    }
}
struct Binding_ContentView : View
{
    @State
    var text: String = "Hello World!";
    @State
    var is_show_sheet: Bool = false;
    var body: some View
    {
        return VStack() {
            Text(self.text);
            Spacer();
            Button("Show Sheet") {
                self.is_show_sheet = true;
            }
        }.sheet(isPresented: $is_show_sheet) {
            Binding_SheetView(text: self.$text);
        }
    }
}

型推論で暗黙的に型を決定できるコンテキストで.(ドット)から始まる省略表記についてです。
例えば、

Image(systemName="bold").imageScale(.large)
の(.large)部分のような表記です。
Pythonでは対応していない為、IDE(Linter)で
'invalid syntax Mypy(syntax)'のようなエラー表示が出るかと思います。
IDEでエラーを出さないようにするには、
Image(systemName="bold").imageScale(Image.Scale.large)
このように暗黙メンバ式を用いず省略しない書き方をする必要があります。

但し、UniPyでの変換においては、IDEでエラーが出ていても別に問題はありません。
暗黙メンバ式を使った表記でも、そのままSwiftのコードに書き出されます。
IDEのエラーを気にしなければそのままでも構いません。

C#への変換にのみ関係する記述仕様

C#ではPythonと異なりクラスのインスタンス化の際に、new演算子が必要です。
new演算子が必要な箇所でも変換の際に自動付与は行われない為、
new関数を使って明示する必要があります。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。

第一引数に通常Pythonでインスタンス化する際と同じように記述します。

オプションの第二引数以降を記述すると、オブジェクト初期化子に変換されます。

これは次の様に変換されます。

public class PointStruct
{
    public int X;
    public int Y;
    public PointStruct(string msg)
    {
        Console.WriteLine(string.Format("init msg={0}", msg));
    }
}
public class Hoge
{
    public Hoge()
    {
        var xy1 = new PointStruct("Hello");
        Console.WriteLine(string.Format("xy1 X={0}, Y={1}", xy1.X, xy1.Y));
        var xy2 = new PointStruct("World") { X=1, Y=2 };
        Console.WriteLine(string.Format("xy2 X={0}, Y={1}", xy2.X, xy2.Y));
    }
}

# namespace から始まるコメントとして記述します。
以降ファイル全体のネストが一段深くなりファイル末尾まで{}で囲われます。
ネストの変換には対応していません。(ネストが必要な場合、"""native~"""の使用を検討してください)

# namespace HogeSpace

class Hoge():
    pass

これは次の様に変換されます。

namespace HogeSpace
{
    public class Hoge
    {

    }
}

または、C#10以降の"ファイル スコープ名前空間宣言"を使用したい場合は、
最後に;を付けて記述してください。

# namespace HogeSpace;

その他

Pythonのassert文の行は無視され、変換後のソースには出力されません。
それでもPython上でassert文を記述しておくのはIDE(Linter)上で型ヒント有効化する場合等有用です。

  • with
  • match(switch)

上記一覧になくても、このページの解説やサンプルソースにないSwift/C#の文法や式への変換は対応していません。
(但しPythonとSwift/C#で全く同じ文法(式)なのであればそれは動作します)

UniPyが変換対応していない文法や式を使う必要がある場合は、"""native~"""の書き方を使うか、
またはその部分をSwift/C#のソースファイルに切り出して、直接Swift/C#のコードとして記述し、
UniPyで記述するコードからは単純に関数を呼び出すだけにする等にして、運用で対応してください。