UniPyは基本的には文法を変換するだけのトランスコンパイラである為、
本来Pythonを採用する場合の大きな要因の一つであろうPythonの豊富なライブラリ(API)を使用する事は出来ません。
ライブラリ(API)は変換先の言語に合わせてSwift(Foundation等)やC#(.NET)のものを使用する必要があります。
ビルトイン関数やstrオブジェクトのメソッド等、
一部に限ってだけそれに対応するSwift/C#(.NET)のメソッドに自動変換する機能を備えてはいます。
PEGに沿った文法解析はおこなっていない為、文法解析ミスで上手く変換出来ない場合があります。
特に、式や文中に余計な空白文字があると、正しくパース出来なくなります。
基本的にはPEP8に準拠した記述する事で、UniPyでパース出来ないパターンを回避する事が出来ます。
また、一行に複数の式を詰め込まず、一度変数に代入する等して処理を複数行に分け、
一行の文をシンプルにすると、正常にパース出来る様になる場合もありますのでお試しください。
Pythonは動的型付け言語ですが、UniPyでコードをSwift/C#に変換する為には、
静的型付け言語であるSwfit/C#と同じく、変数の型を意識してコードを記述する必要があります。
具体的には、型ヒントの指定が必須です。
文字列型の変数hogeを定義する例:
hoge: str = "Hello"
hoge = "Hello" # type: str
これは次の様に変換されます。
var hoge: String = "Hello";
string hoge = "Hello";
int型の引数を受け取り文字列を返す関数(メソッド)を定義する例:
def number_to_string(self, _value: int, message: str) -> str:
return str(_value) + message
def number_to_string(self, _value, message):
# type: (int, str) -> str
return str(_value) + message
これは次の様に変換されます。
func number_to_string(_ _value: Int, message: String) -> String
{
return _value.description + message;
}
public string number_to_string(int _value, string message)
{
return _value.ToString() + message;
}
hoge: str # これは上手く変換出来ない
空文字等を初期値として指定すれば済む話ですが、hoge: str = Dummy.init # ダミー初期値として記述する
これは次の様に変換されます。
var hoge: String;
string hoge;
x: Var = ""
x = "" # type: Var
これは次の様に変換されます。
var x = "";
var x = "";
クラスHogeに空のコンストラクタとデストラクタを定義するだけの例:
class Hoge(object):
def __init__(self) -> None:
pass
def __del__(self) -> None:
pass
class Hoge(object):
def __init__(self):
# type: () -> None
pass
def __del__(self):
# type: () -> None
pass
これは次の様に変換されます。
class Hoge
{
init()
{
}
deinit
{
}
}
public class Hoge
{
public Hoge()
{
}
~Hoge()
{
}
}
普通に__init__内にselfを付けて変数として定義します。
Swift/C#に変換時に__init__外に移動されます。
class Hoge():
def __init__(self) -> None:
self.aa: str = "AA"
class Hoge():
def __init__(self):
# type: () -> None
self.aa = "AA" # type: str
これは次の様に変換されます。
class Hoge
{
var aa: String;
init()
{
self.aa = "AA";
}
}
public class Hoge
{
public string aa;
public Hoge()
{
this.aa = "AA";
}
}
静的メンバはクラス変数として定義します。
静的メソッドは関数定義時に@staticmethodデコレータを付与します。
class Hoge(object):
Hello: str = "Static Variable"
@staticmethod
def World() -> str:
return "Static Function"
class Hoge(object):
Hello = "Static Variable" # type: str
@staticmethod
def World():
# type: () -> str
return "Static Function"
これは次の様に変換されます。
class Hoge
{
static var Hello: String = "Static Variable";
static func World() -> String
{
return "Static Function";
}
}
public static class Hoge
{
public static string Hello = "Static Variable";
public static string World()
{
return "Static Function";
}
}
PEP8の命名規則に従って、_(アンダースコア)から始まる名前で定義します。
class Hoge(object):
def __init__(self) -> None:
self._aa: str = "AA"
def _bb(self) -> None:
pass
class Hoge(object):
def __init__(self):
# type: () -> None
self._aa: str = "AA"
def _bb(self):
# type: () -> None
pass
これは次の様に変換されます。
class Hoge
{
private var _aa: String;
init()
{
self._aa = "AA";
}
private func _bb() -> Void
{
}
}
public class Hoge
{
private string _aa;
public Hoge()
{
this._aa = "AA";
}
private void _bb()
{
}
}
property関数を使用します。
getterのみsetterのみの指定にも対応しますが、deleterには対応していません。
__(アンダースコア二つ)から始まる関数名を指定するとprivateキーワードが付きます。
または@propertyデコレータを使用する方法がありますが、
この方法はSwiftのgetterへの変換にのみ対応しています。
class Hoge(object):
def __init__(self) -> None:
self._aa: str = ""
self._bb: int = 1
def __set_aa(self, value: str) -> None:
self._aa = value
def get_aa(self) -> str:
return self._aa
Aa = property(get_aa, __set_aa)
@property
def Bb(self) -> int:
return self._bb
class Hoge(object):
def __init__(self):
# type: () -> None
self._aa = "" # type: str
self._bb = 1 # type: int
def __set_aa(self, value):
# type: (str) -> None
self._aa = value
def get_aa(self):
# type: () -> str
return self._aa
Aa = property(get_aa, __set_aa)
@property
def Bb(self):
# type: () -> int
return self._bb
これは次の様に変換されます。
class Hoge
{
private var _aa: String;
private var _bb: Int;
init()
{
self._aa = "";
self._bb = 1;
}
public private(set) var Aa: String
{
get
{
return _aa;
}
set(value)
{
_aa = value;
}
}
var Bb: Int
{
return _bb;
}
}
public class Hoge
{
private string _aa;
private int _bb;
public Hoge()
{
this._aa = "";
this._bb = 1;
}
public string Aa
{
get
{
return _aa;
}
private set
{
_aa = value;
}
}
public int Bb()
{
return _bb;
}
}
Python3.12以降の関数名の後に[T]を直接記述する方法には対応していません。
Python3.11以前のTypeVarを使う方法に対応しています。
from typing import TypeVar, Any
class Hoge(object):
T = TypeVar('T')
def g1_hoge(self, _a: T) -> None:
pass
V = TypeVar('V', bound=Any)
def g2_hoge(self, _a: T, _b: V) -> None:
pass
from typing import TypeVar, Any
class Hoge(object):
T = TypeVar('T')
def g1_hoge(self, _a):
# type: (T) -> None
pass
V = TypeVar('V', bound=Any)
def g2_hoge(self, _a, _b):
# type: (T, V) -> None
pass
これは次の様に変換されます。
class Hoge
{
func g1_hoge<T>(_ _a: T) -> Void
{
}
func g2_hoge<T, V: Any>(_ _a: T, _ _b: V) -> Void
{
}
}
public class Hoge
{
public void g1_hoge<T>(T _a)
{
}
public void g2_hoge<T, V>(T _a, V _b)
{
}
}
Swiftに変換する場合と、C#に変換する場合で、記述が異なります。
Swiftの場合、Swiftで型推論が行われる為、特別な記述は必要ありません、
通常のメソッド呼び出しと同じ様に記述します。
コードは省略
C#の場合、generic関数を使用します。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
第一引数に"型"、第二引数に"メソッド呼び出し文"を指定します。
先程のジェネリックメソッドの定義の例で定義したHogeクラスに
call_gという名前のメソッドを追加で定義するとして、
そこから同一クラス内にあるg1_hogeとg2_hogeを呼び出す例です。
from unipy_stubs import *
def call_g(self) -> None:
generic(int, self.g1_hoge(1))
generic([int, str], self.g2_hoge(1, "a"))
これは次の様に変換されます。
public void call_g()
{
g1_hoge<int>(1);
g2_hoge<int, string>(1, "a");
}
上記の例だと、Mypyでチェックを行なっている場合、Mypyが次の様なエラーを出すかと思います。
このエラーはUniPyの変換には影響ありませんが、このエラーを回避するには、
generic(int, self.g1_hoge(1)) # type: ignore
の様にMypyのignoreを付けるか、または、
generic(int, lambda: self.g1_hoge(1))
の様にlambdaを付与してください。この場合lambdaは無視され変換結果は同一になります。
typingモジュールのcast関数を使用します。
クラスBaseClassとそのサブクラスSubClassを定義し、
そのオブジェクトをダウンキャストアップキャストして変数に代入する例:
アップキャストは暗黙的に行われる為、本来はcast関数を使って明示する必要はありません。
Swiftに変換する場合と、C#に変換する場合で、キャスト自体の書き方は同じですが、他に必要な記述が異なります。
Swiftの場合、cast関数がasに変換されます。
さらにunwrap関数で囲う事で、as!に変換されます。
unwrap関数はunipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
Optionalの展開でも使用します。
また、cast関数の第一引数の型に、Optional型を指定するとas?に変換されます。
from typing import Optional, cast
from unipy_stubs import *
class BaseClass():
pass
class SubClass(BaseClass):
pass
bc: BaseClass = SubClass()
sc: SubClass = unwrap(cast(SubClass, bc))
"""Forced Downcasting"""
bc = cast(BaseClass, sc)
"""Upcasting"""
opt_sc: Optional[SubClass] = cast(Optional[SubClass], bc)
"""Optional Downcasting"""
これは次の様に変換されます。
class BaseClass
{
}
class SubClass : BaseClass
{
}
var bc: BaseClass = SubClass();
var sc: SubClass = (bc as! SubClass);
bc = (sc as BaseClass);
var opt_sc: Optional<SubClass> = (bc as? SubClass);
C#の場合、cast関数が()(括弧)を使ったキャストに変換されます。
また、cast関数の第一引数の型に、Optional型を指定するとasを使ったキャストに変換されます。
from typing import Optional, cast
from unipy_stubs import *
class BaseClass():
pass
class SubClass(BaseClass):
pass
bc: BaseClass = Dummy.init
bc = new(SubClass())
sc: SubClass = Dummy.init
sc = cast(SubClass, bc)
"""Downcasting"""
bc = cast(BaseClass, sc)
"""Upcasting"""
opt_sc: Optional[SubClass] = cast(Optional[SubClass], bc)
"""Optional Downcasting"""
これは次の様に変換されます。
public class BaseClass
{
}
public class SubClass : BaseClass
{
}
BaseClass bc;
bc = new SubClass();
SubClass sc;
sc = ((SubClass)bc);
bc = ((BaseClass)sc);
SubClass opt_sc = (bc as SubClass);
@attributeデコレータ、またはattribute関数を属性を付与したい定義の前の行に記述します。
デコレータ(関数)はunipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
Swiftに変換する場合と、C#に変換する場合で、属性(アトリビュート)の書き方は同じです。
以下の例はそれぞれ具体的な属性(アトリビュート)使用例記載の為、SwiftとC#で別な内容になっています。
from unipy_stubs import *
from unipy_stubs.swift_api.swift_ui import *
@attribute('available(macOS 10.12, *)')
class AttrClass(ObservableObject):
def __init__(self) -> None:
attribute(Published)
self.all_text: list[str] = []
@attribute('objc')
def native_method(self) -> None:
pass
これは次の様に変換されます。
@available(macOS 10.12, *)
class AttrClass : ObservableObject
{
@Published
var all_text: [String];
init()
{
self.all_text = [];
}
@objc
func native_method() -> Void
{
}
}
from unipy_stubs import *
from unipy_stubs.unity_api.unity_engine import *
@attribute(AddComponentMenu("Transform/Follow Transform"))
class FollowTransform(MonoBehaviour):
def __init__(self) -> None:
attribute(SerializeField)
self._has_health_potion: bool = Dummy.init
attribute(ContextMenuItemAttribute("Reset", "ResetBiography"))
attribute(MultilineAttribute(8))
self.player_biography: str = Dummy.init
これは次の様に変換されます。
[AddComponentMenu("Transform/Follow Transform")]
public class FollowTransform : MonoBehaviour
{
[SerializeField]
private bool _has_health_potion;
[ContextMenuItem("Reset", "ResetBiography")]
[Multiline(8)]
public string player_biography;
public FollowTransform()
{
}
}
スライスの変換にも対応しています。
ステップ [start:stop:step] の指定には対応していません。
int型のリストを定義して、中身の一部をスライスしてprint出力する例:
lst: list[int] = [1, 2, 3, 4, 5, 6]
print(lst[:2]) # [1, 2]
print(lst[2:4]) # [3, 4]
print(lst[-2:]) # [5, 6]
lst = [1, 2, 3, 4, 5, 6] # type: list[int]
print(lst[:2]) # [1, 2]
print(lst[2:4]) # [3, 4]
print(lst[-2:]) # [5, 6]
これは次の様に変換されます。
var lst: [Int] = [1, 2, 3, 4, 5, 6];
print(lst.slice(0, 2));
print(lst.slice(2, 4));
print(lst.slice(-2));
lst.shuffle()
と記述した場合、Python上では、var lst = new List<int> { 1, 2, 3, 4, 5, 6 };
Console.WriteLine(lst.Slice(0, 2));
Console.WriteLine(lst.Slice(2, 4));
Console.WriteLine(lst.Slice(-2));
lst.LastIndexOf(1)
と記述した場合、Python上では、list型ではなくarray型として定義します。
ary: array[int] = [1, 2, 3, 4, 5, 6]
これは次の様に変換されます。
var ary = new int[] { 1, 2, 3, 4, 5, 6 };
intがキー、文字列が値の、空の辞書(a_dic)定義して、項目を追加し、一つをprint出力する例、と、
文字列がキー、intが値の、初期値のある辞書(b_dic)を定義して、
キーと値をそれぞれリストにして、print出力する例:
a_dic: dict[int, str] = {}
a_dic[0] = "A"
a_dic[1] = "B"
print(a_dic[0])
b_dic: dict[str, int] = {"A": 0, "B": 1}
b_keys: list[str] = list(b_dic.keys())
b_values: list[int] = list(b_dic.values())
print(b_keys)
print(b_values)
a_dic = {} # type: dict[int, str]
a_dic[0] = "A"
a_dic[1] = "B"
print(a_dic[0])
b_dic = {"A": 0, "B": 1} # type: dict[str, int]
b_keys = list(b_dic.keys()) # type: list[str]
b_values = list(b_dic.values()) # type: list[int]
print(b_keys)
print(b_values)
これは次の様に変換されます。
var a_dic: Dictionary<Int, String> = [:];
a_dic[0] = "A";
a_dic[1] = "B";
print(a_dic[0]);
var b_dic: Dictionary<String, Int> = ["A": 0, "B": 1];
var b_keys: [String] = list(b_dic.keys);
var b_values: [Int] = list(b_dic.values);
print(b_keys);
print(b_values);
var a_dic = new Dictionary<int, string>();
a_dic[0] = "A";
a_dic[1] = "B";
Console.WriteLine(a_dic[0]);
var b_dic = new Dictionary<string, int> { {"A", 0}, {"B", 1} };
var b_keys = new List<string>();b_keys.AddRange(b_dic.Keys.Cast<string>());
var b_values = new List<int>();b_values.AddRange(b_dic.Values.Cast<int>());
Console.WriteLine(b_keys);
Console.WriteLine(b_values);
lst: list[str] = ["1", "2", "3", "4", "5", "6"]
del lst[5:] # delete 6
print(lst)
del lst[:1] # delete 1
print(lst)
del lst[1:-1] # delete 3, 4
print(lst)
del lst[0] # delete 2
print(lst)
del lst[:] # delete all
print(lst)
dic: dict[str, str] = {"a": "A", "b": "B"}
del dic["a"]
print(dic)
lst = ["1", "2", "3", "4", "5", "6"] # type: list[str]
del lst[5:] # delete 6
print(lst)
del lst[:1] # delete 1
print(lst)
del lst[1:-1] # delete 3, 4
print(lst)
del lst[0] # delete 2
print(lst)
del lst[:] # delete all
print(lst)
dic = {"a": "A", "b": "B"} # type: dict[str, str]
del dic["a"]
print(dic)
これは次の様に変換されます。
var lst: [Int] = [1, 2, 3, 4, 5, 6];
lst.removeSubrange(5..<(len(lst)));
print(lst);
lst.removeSubrange(0, 1);
print(lst);
lst.removeSubrange(1, -1);
print(lst);
lst.remove(at: 0);
print(lst);
lst.removeAll();
print(lst);
var dic: Dictionary<String, String> = ["a": "A", "b": "B"];
dic.removeValue(forKey: "a");
print(dic);
var lst = new List<string> { "1", "2", "3", "4", "5", "6" };
lst.RemoveRange(5, lst.Count - 5);
Console.WriteLine(lst);
lst.RemoveRange(0, (1 < 0) ? (lst.Count + 1 - 0) : (1 - 0));
Console.WriteLine(lst);
lst.RemoveRange(1, (-1 < 0) ? (lst.Count + -1 - 1) : (-1 - 1));
Console.WriteLine(lst);
lst.RemoveAt(0);
Console.WriteLine(lst);
lst.Clear();
Console.WriteLine(lst);
var dic = new Dictionary<string, string> { {"a", "A"}, {"b", "B"} };
dic.Remove("a");
Console.WriteLine(dic);
PythonとSwift/C#でループ変数のスコープが異なる点は注意が必要です。
リストと辞書の変数を定義し、それを直接回す例と、range関数を使って要素数分回す例:
lst_for: list[str] = ["a", "b"]
for n in lst_for:
print(str(n))
dic_for: dict[str, str] = {"a": "A", "b": "B"}
for m in dic_for:
print("{0}: {1}".format(m, dic_for[m]))
for key, val in dic_for.items():
print("{0}: {1}".format(key, val))
for i in range(len(lst_for)):
print(str(i))
for i in range(len(dic_for)):
print(str(i))
for i in range(2):
print(str(i))
lst_for = ["a", "b"] # type: list[str]
for n in lst_for:
print(str(n))
dic_for = {"a": "A", "b": "B"} # type: dict[str, str]
for m in dic_for:
print("{0}: {1}".format(m, dic_for[m]))
for key, val in dic_for.items():
print("{0}: {1}".format(key, val))
for i in range(len(lst_for)):
print(str(i))
for i in range(len(dic_for)):
print(str(i))
for i in range(2):
print(str(i))
これは次の様に変換されます。
var lst_for: [String] = ["a", "b"];
for n in lst_for
{
print(n.description);
}
var dic_for: Dictionary<String, String> = ["a": "A", "b": "B"];
for m in dic_for.keys
{
print("\(m): \(dic_for[m])");
}
for (key, val) in dic_for
{
print("\(key): \(val)");
}
for i in 0 ..< len(lst_for)
{
print(i.description);
}
for i in 0 ..< len(dic_for)
{
print(i.description);
}
for i in 0 ..< 2
{
print(i.description);
}
var lst_for = new List<string> { "a", "b" };
foreach (string n in lst_for)
{
Console.WriteLine(n.ToString());
}
var dic_for = new Dictionary<string, string> { {"a", "A"}, {"b", "B"} };
foreach (var m in dic_for.Keys.Cast<string>())
{
Console.WriteLine(string.Format("{0}: {1}", m, dic_for[m]));
}
foreach (var (key, val) in dic_for)
{
Console.WriteLine(string.Format("{0}: {1}", key, val));
}
for (var i = 0; i < lst_for.Count; i++)
{
Console.WriteLine(i.ToString());
}
for (var i = 0; i < dic_for.Keys.Count; i++)
{
Console.WriteLine(i.ToString());
}
for (var i = 0; i < 2; i++)
{
Console.WriteLine(i.ToString());
}
ary_keys: array[str] = list(dic_for.keys())
for i in range(len(ary_keys)):
print(ary_keys[i])
これは次の様に変換されます。
string[] ary_keys = dic_for.Keys.Cast<string>().ToArray();
for (var i = 0; i < ary_keys.Length; i++)
{
Console.WriteLine(ary_keys[i]);
}
System.Collections.SpecializedのOrderedDictionaryのループ変数は、DictionaryEntry型になりますが、
UniPyではよりPythonicに簡単に扱える様にする為、特別な変換に対応しています。
次の例の様に、for文の次の行にcast関数でキーの型を指定、そのまた次の行に値の型を指定します。
from typing import cast
from unipy_stubs.dotnet_api.system_collections_specialized import OrderedDictionary
od_for: OrderedDictionary = {"a": "A", "b": "B"}
for key, val in od_for.items():
key = cast(str, key)
val = cast(str, val)
print("{0}: {1}".format(key, val))
これは次の様に変換されます。
var od_for = new OrderedDictionary { {"a", "A"}, {"b", "B"} };
foreach (DictionaryEntry kv3 in od_for) {
string key = (string)kv3.Key;
string val = (string)kv3.Value;
Console.WriteLine(string.Format("{0}: {1}", key, val));
}
以下の様に変換されます。
==,!=,<=,>=,<,>等の比較演算子の記法は言語間で違いがない為、変換処理はありません。
s: str = str(datetime.now())
if "2024" in s and ("9/" in s or "-09-" in s):
if len(s) >= 25:
print("1-1")
else:
print("1-2")
elif "2025" not in s:
print("2")
s = str(datetime.now()) # type: str
if "2024" in s and ("9/" in s or "-09-" in s):
if len(s) >= 25:
print("1-1")
else:
print("1-2")
elif "2025" not in s:
print("2")
これは次の様に変換されます。
var s: String = datetime.now().description;
if (s.contains("2024") && (s.contains("9/") || s.contains("-09-")))
{
if (len(s) >= 25)
{
print("1-1");
}
else
{
print("1-2");
}
}
else if (!s.contains("2025"))
{
print("2");
}
string s = DateTime.Now.ToString();
if (s.Contains("2024") && (s.Contains("9/") || s.Contains("-09-")))
{
if (s.Length >= 25)
{
Console.WriteLine("1-1");
}
else
{
Console.WriteLine("1-2");
}
}
else if (!s.Contains("2025"))
{
Console.WriteLine("2");
}
ネストされている等複雑な文は正しく変換出来ません。なるべくシンプルな文にしてください。
0~4までループし、偶数と奇数でそれぞれ別な文字列をprintする例:
for i in range(5):
print("odd" if (i % 2) != 0 else "even")
これは次の様に変換されます。
for i in 0 ..< 5
{
print((i % 2) != 0 ? "odd" : "even");
}
for (var i = 0; i < 5; i++)
{
Console.WriteLine((i % 2) != 0 ? "odd" : "even");
}
n: int = 0
while n < 5:
print(str(n))
n += 1
n = 0 # type: int
while n < 5:
print(str(n))
n += 1
これは次の様に変換されます。
var n: Int = 0;
while (n < 5)
{
print(n.description);
n += 1;
}
var n = 0;
while (n < 5)
{
Console.WriteLine(n.ToString());
n += 1;
}
int型とstr型の二つの要素のあるタプルを定義してそれにアクセスする例:
t: tuple[int, str] = (1, "a")
print(str(t[0]))
print(t[1])
t = (1, "a") # type: tuple[int, str]
print(str(t[0]))
print(t[1])
これは次の様に変換されます。
var t: (Int, String) = (1, "a");
print(t.0.description);
print(t.1);
(int, string) t = (1, "a");
Console.WriteLine(t.Item1.ToString());
Console.WriteLine(t.Item2);
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()
これは次の様に変換されます。
enum Season : Int
{
case Spring = 1;
case Summer = 2;
case Autumn = 3;
case Winter = 4;
}
enum ColorRGB
{
case Red
case Green
case Blue
}
public enum Season : int
{
Spring = 1,
Summer = 2,
Autumn = 3,
Winter = 4,
}
public enum ColorRGB
{
Red,
Green,
Blue,
}
クラスHogeに、関数を引数で受け取るaction_worldを定義し、その引数をlambda式で記述して呼び出す例:
from typing import Callable
class Hoge(object):
def __init__(self) -> None:
self.action_world(lambda x: print("Hello lambda {0}".format(x)))
def action_world(self, _f: Callable[[str], None]) -> None:
_f("World!")
from typing import Callable
class Hoge(object):
def __init__(self):
# type: () -> None
self.action_world(lambda x: print("Hello lambda {0}".format(x)))
def action_world(self, _f):
# type: (Callable[[str], None]) -> None
_f("World!")
これは次の様に変換されます。
class Hoge
{
init()
{
action_world({(x) in print("Hello lambda \(x)")});
}
func action_world(_ _f: (String) -> Void) -> Void
{
_f("World!");
}
}
public class Hoge
{
public Hoge()
{
action_world((x) => Console.WriteLine(string.Format("Hello lambda {0}", x)));
}
public void action_world(Action<string> _f)
{
_f("World!");
}
}
ネストされている等複雑な文は正しく変換出来ません。なるべくシンプルな文にしてください。
lst1から"o"を含む要素だけを抜き出し大文字に変換したlst2をリスト内包表記で作成する例:
lst1: list[str] = ["one", "two", "three", "for"]
lst2: list[str] = [x.upper() for x in lst1 if "o" in x]
print(lst2) # ["ONE", "TWO", "FOR"]
lst1 = ["one", "two", "three", "for"] # type: list[str]
lst2 = [x.upper() for x in lst1 if "o" in x] # type: list[str]
print(lst2) # ["ONE", "TWO", "FOR"]
これは次の様に変換されます。
var lst1: [String] = ["one", "two", "three", "for"];
var lst2: [String] = lst1.comprehension(({(x) in x.upper()}), ({(x) in x.contains("o")}));
print(lst2);
var lst1 = new List<string> { "one", "two", "three", "for" };
List<string> lst2 = lst1.comprehension(((x) => x.ToUpper()), ((x) => x.Contains("o")));
Console.WriteLine(lst2);
class Hoge():
def __init__(self) -> None:
self.default_method()
self.default_method(name="Taro", num=2)
def default_method(self, name: str = "Jone", num: int = 1) -> None:
print("{0}: {1}".format(name, str(num)))
class Hoge():
def __init__(self):
# type: () -> None
self.default_method()
self.default_method(name="Taro", num=2)
def default_method(self, name="Jone", num=1) -> None:
# type: (str, int) -> None
print("{0}: {1}".format(name, str(num)))
これは次の様に変換されます。
class Hoge
{
init()
{
default_method();
default_method(name: "Taro", num: 2);
}
func default_method(name: String = "Jone", num: Int = 1) -> Void
{
print("\(name): \(num.description)");
}
}
public class Hoge
{
public Hoge()
{
default_method();
default_method(name: "Taro", num: 2);
}
public void default_method(string name = "Jone", int num = 1)
{
Console.WriteLine(string.Format("{0}: {1}", name, num.ToString()));
}
}
引数を参照で受け取る関数を定義するには、_ref_またはref_で始まる名前で引数を定義します。
参照で引数を渡すには、ref関数を使用します。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
クラスHogeに、二つの引数を参照で受け取る関数inout_worldを定義し、その関数を呼び出す例:
(ref_bbの方の引数は'名前付き引数(キーワード引数)'として呼び出しを記述)
class Hoge():
def __init__(self) -> None:
aa: str = "Hello"
bb: str = "Hello"
self.inout_world(ref(aa), ref_bb=ref(bb))
print(aa)
print(bb)
def inout_world(self, _ref_aa: str, ref_bb: str) -> None:
_ref_aa += " World A"
ref_bb += " World B"
class Hoge():
def __init__(self):
# type: () -> None
aa = "Hello" # type: str
bb = "Hello" # type: str
self.inout_world(ref(aa), ref_bb=ref(bb))
print(aa)
print(bb)
def inout_world(self, _ref_aa, ref_bb):
# type: (str, str) -> None
_ref_aa += " World A"
ref_bb += " World B"
これは次の様に変換されます。
class Hoge
{
init()
{
var aa: String = "Hello";
var bb: String = "Hello";
inout_world(&aa, ref_bb: &bb);
print(aa);
print(bb);
}
func inout_world(_ _ref_aa: inout String, ref_bb: inout String) -> Void
{
_ref_aa += " World A";
ref_bb += " World B";
}
}
public class Hoge
{
public Hoge()
{
var aa = "Hello";
var bb = "Hello";
inout_world(ref aa, ref_bb: ref bb);
Console.WriteLine(aa);
Console.WriteLine(bb);
}
public void inout_world(ref string _ref_aa, ref string ref_bb)
{
_ref_aa += " World A";
ref_bb += " World B";
}
}
クラスHogeに、可変長引数のある関数params_worldを定義し、その関数を呼び出す例:
class Hoge():
def __init__(self) -> None:
self.params_world("a", "b", "c")
def params_world(self, *_abc: str) -> None:
for x in _abc:
print(x)
class Hoge():
def __init__(self):
# type: () -> None
self.params_world("a", "b", "c")
def params_world(self, *_abc):
# type: (str) -> None
for x in _abc:
print(x)
これは次の様に変換されます。
class Hoge
{
init()
{
params_world("a", "b", "c");
}
func params_world(_ _abc: String...) -> Void
{
for x in _abc
{
print(x);
}
}
}
public class Hoge
{
public Hoge()
{
params_world("a", "b", "c");
}
public void params_world(params string[] _abc)
{
foreach (var x in _abc)
{
Console.WriteLine(x);
}
}
}
Pythonのコメントとして次のように定義し、
"""native
〜
"""
中にSwift/C#のコードを記述します。必ずnativeの文言から始めて改行して内容を書く必要があります。
native内に書いた内容はUniPyで何も変換処理されずそのまま変換後のSwift/C#ソースに書き出しされます。
UniPyが対応していない記法をする場合や、上手く変換出来ない場合に使用してください。
クラスBaseClassに、関数call_from_nativeを定義し、その関数をUniPy変換を通さず呼び出す例:
class BaseClass():
def __init__(self) -> None:
"""native
// Write swift/cs code here.
call_from_native();
"""
"""native_swift
// Write swift code here.
"""
"""native_cs
// Write cs code here.
"""
pass
def call_from_native(self) -> None:
print("Hello World")
class BaseClass():
def __init__(self):
# type: () -> None
"""native
// Write swift/cs code here.
call_from_native();
"""
"""native_swift
// Write swift code here.
"""
"""native_cs
// Write cs code here.
"""
pass
def call_from_native(self):
# type: () -> None
print("Hello World")
これは次の様に変換されます。
class BaseClass
{
init()
{
// Write swift/cs code here.
call_from_native();
// Write swift code here.
}
func call_from_native() -> Void
{
print("Hello World");
}
}
public class BaseClass
{
public BaseClass()
{
// Write swift/cs code here.
call_from_native();
// Write cs code here.
}
public void call_from_native()
{
Console.WriteLine("Hello World");
}
}
Pythonのコメントとして次のように定義し、
# no_unipy start
〜
# no_unipy end
中に変換されたくないPythonのコードを記述します。
二つのコメント文の内に書いた内容はUniPyで何も変換処理されず変換後のSwift/C#ソースに書き出しもされません。
IDE上でのエラーを回避する為のダミー定義を用意する場合等に使用してください。
関数over_worldを三つ定義し(但し一つはUniPyで変換しないMypy用のダミー定義)、それを呼び出す例:
from typing import Any, overload
class Hoge():
def __init__(self) -> None:
print(self.over_world(99))
print(self.over_world("Bye-bye"))
@overload
def over_world(self, _i: int) -> str:
return "Hello Over World" + str(_i)
@overload
def over_world(self, _s: str) -> str:
return _s + " Over World"
# no_unipy start
def over_world(self, *args: Any, **kwargs: Any) -> str:
pass
# no_unipy end
from typing import Any, overload
class Hoge():
def __init__(self):
# type: () -> None
print(self.over_world(99))
print(self.over_world("Bye-bye"))
@overload
def over_world(self, _i):
# type: (int) -> str
return "Hello Over World" + str(_i)
@overload
def over_world(self, _s):
# type: (str) -> str
return _s + " Over World"
# no_unipy start
def over_world(self, *args, **kwargs):
# type: (Any, Any) -> str
pass
# no_unipy end
これは次の様に変換されます。
class Hoge
{
init()
{
print(over_world(99));
print(over_world("Bye-bye"));
}
func over_world(_ _i: Int) -> String
{
return "Hello Over World" + _i.description;
}
func over_world(_ _s: String) -> String
{
return _s + " Over World";
}
}
public class Hoge
{
public Hoge()
{
Console.WriteLine(over_world(99));
Console.WriteLine(over_world("Bye-bye"));
}
public string over_world(int _i)
{
return "Hello Over World" + _i.ToString();
}
public string over_world(string _s)
{
return _s + " Over World";
}
}
Swift/C#ではオーバーロードの定義に特別なキーワードは必要ありません。
しかしPython上では、Mypyでチェック行なっている場合、次の様な'定義重複のエラー'となります。
unipy_stubs/内にスタブファイルが用意されているモジュールについては、
そのモジュールをimportする事で該当するSwift/C#のモジュールがimport/usingされます。
スタブファイルが用意されていないモジュールについては、
"""native~"""の書き方を使用してください。
C#への変換の場合、コメント文としてusing文を記述する事で逆にコメントが除去されて変換される機能も使えます。
from unipy_stubs.swift_api.swift_ui import *
"""native_swift
import Foundation
"""
これは次の様に変換されます。
import SwiftUI
import Foundation
from unipy_stubs.unity_api.unity_engine import *
"""native_cs
using UnityEditor;
"""
# using OpenCvSharp;
これは次の様に変換されます。
using UnityEngine;
using UnityEditor;
using OpenCvSharp;
@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#への変換にのみ必要な記述仕様 - クラスのインスタンス化" を参照してください。
from unipy_stubs import *
class BaseClass():
@virtual
def ride_world(self) -> None:
print("I'm BaseClass")
class SubClass(BaseClass):
@override
def ride_world(self) -> None:
print("I'm SubClass")
class Hoge():
def __init__(self) -> None:
ride1: BaseClass = new(BaseClass())
ride1.ride_world()
ride2: SubClass = new(SubClass())
ride2.ride_world()
from unipy_stubs import *
class BaseClass():
@virtual
def ride_world(self):
# type: () -> None
print("I'm BaseClass")
class SubClass(BaseClass):
@override
def ride_world(self):
# type: () -> None
print("I'm SubClass")
class Hoge():
def __init__(self):
# type: () -> None
ride1: BaseClass = new(BaseClass())
ride1.ride_world()
ride2: SubClass = new(SubClass())
ride2.ride_world()
これは次の様に変換されます。
class BaseClass
{
func ride_world() -> Void
{
print("I'm BaseClass");
}
}
class SubClass : BaseClass
{
override func ride_world() -> Void
{
print("I'm SubClass");
}
}
class Hoge
{
init()
{
var ride1: BaseClass = BaseClass();
ride1.ride_world();
var ride2: SubClass = SubClass();
ride2.ride_world();
}
}
public class BaseClass
{
public virtual void ride_world()
{
Console.WriteLine("I'm BaseClass");
}
}
public class SubClass : BaseClass
{
public override void ride_world()
{
Console.WriteLine("I'm SubClass");
}
}
public class Hoge
{
public Hoge()
{
BaseClass ride1 = new BaseClass();
ride1.ride_world();
SubClass ride2 = new SubClass();
ride2.ride_world();
}
}
_tcから始まる名前で内部関数として定義します。
呼び出し部分でSwiftではトレイリングクロージャ、C#では匿名クロージャに変換されます。
class Hoge():
def __init__(self) -> None:
def _tc0() -> None:
print("Unname0")
self.unname_func_world(_tc0)
def _tc1() -> None:
print("Unname1")
def _tc2() -> None:
print("Unname2")
self.unname_func_world2(_tc1, action2=_tc2)
def _tc3(_s: str) -> None:
print("Unname3 {0}".format(_s))
def _tc4(_s: str) -> None:
print("Unname4 {0}".format(_s))
self.unname_func_world3(action1=_tc3, action2=_tc4)
def unname_func_world(self, action: Callable[[], None]) -> None:
action()
def unname_func_world2(self, action1: Callable[[], None], action2: Callable[[], None]) -> None:
action1()
action2()
def unname_func_world3(self, action1: Callable[[str], None], action2: Callable[[str], None]) -> None:
action1("Hello")
action2("World")
class Hoge():
def __init__(self):
# type: () -> None
def _tc0():
# type: () -> None
print("Unname0")
self.unname_func_world(_tc0)
def _tc1():
# type: () -> None
print("Unname1")
def _tc2():
# type: () -> None
print("Unname2")
self.unname_func_world2(_tc1, action2=_tc2)
def _tc3(_s):
# type: (str) -> None
print("Unname3 {0}".format(_s))
def _tc4(_s):
# type: (str) -> None
print("Unname4 {0}".format(_s))
self.unname_func_world3(action1=_tc3, action2=_tc4)
def unname_func_world(self, action):
# type: (Callable[[], None]) -> None
action()
def unname_func_world2(self, action1, action2):
# type: (Callable[[], None], Callable[[], None]) -> None
action1()
action2()
def unname_func_world3(self, action1, action2):
# type: (Callable[[str], None], Callable[[str], None]) -> None
action1("Hello")
action2("World")
これは次の様に変換されます。
class Hoge
{
init()
{
unname_func_world() {
print("Unname0");
}
unname_func_world2() {
print("Unname1");
} action2: {
print("Unname2");
}
unname_func_world3() { _s in
print("Unname3 \(_s)");
} action2: { _s in
print("Unname4 \(_s)");
}
}
func unname_func_world(action: () -> Void) -> Void
{
action();
}
func unname_func_world2(action1: () -> Void, action2: () -> Void) -> Void
{
action1();
action2();
}
func unname_func_world3(action1: (String) -> Void, action2: (String) -> Void) -> Void
{
action1("Hello");
action2("World");
}
}
public class Hoge
{
public Hoge()
{
unname_func_world(() => {
Console.WriteLine("Unname0");
});
unname_func_world2(() => {
Console.WriteLine("Unname1");
}, action2: () => {
Console.WriteLine("Unname2");
});
unname_func_world3(action1: (string _s) => {
Console.WriteLine(string.Format("Unname3 {0}", _s));
}, action2: (string _s) => {
Console.WriteLine(string.Format("Unname4 {0}", _s));
});
}
public void unname_func_world(Action action)
{
action();
}
public void unname_func_world2(Action action1, Action action2)
{
action1();
action2();
}
public void unname_func_world3(Action<string> action1, Action<string> action2)
{
action1("Hello");
action2("World");
}
}
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する例:
from typing import Awaitable
from asyncio import create_task, run, sleep
class Hoge():
def __init__(self) -> None:
print("before async_world()")
async def _tc_task() -> None:
print(await self.async_world())
run(_tc_task())
print("after async_world() - printed before 'Done async_loop'")
async def async_loop(self, _loop: int) -> int:
ret: int = 0
for i in range(_loop):
ret += 1
print("Lopping in async_loop({0}): {1}".format(_loop, i))
try:
await sleep(1.0)
except Exception:
pass
print("Done async_loop({0})".format(_loop))
return ret
async def async_world(self) -> int:
a: Awaitable[int] = create_task(self.async_loop(5))
b = create_task(self.async_loop(3))
c: list[int] = [await a, await b]
return c[0] + c[1]
from typing import Awaitable
from asyncio import create_task, run, sleep
class Hoge():
def __init__(self):
# type: () -> None
print("before async_world()")
async def _tc_task() -> None:
print(await self.async_world())
run(_tc_task())
print("after async_world() - printed before 'Done async_loop'")
async def async_loop(self, _loop):
# type: (int) -> int
ret = 0 # type: int
for i in range(_loop):
ret += 1
print("Lopping in async_loop({0}): {1}".format(_loop, i))
try:
await sleep(1.0)
except Exception:
pass
print("Done async_loop({0})".format(_loop))
return ret
async def async_world(self):
# type: () -> int
a = create_task(self.async_loop(5)) # type: Awaitable[int]
b = create_task(self.async_loop(3))
c = [await a, await b] # type: list[int]
return c[0] + c[1]
これは次の様に変換されます。
class Hoge
{
init()
{
print("before async_world()");
Task() {
print(await self.async_world());
}
print("after async_world() - printed before 'Done async_loop'");
}
func async_loop(_ _loop: Int) async -> Int
{
var ret: Int = 0;
for i in 0 ..< _loop
{
ret += 1;
print("Lopping in async_loop(\(_loop)): \(i)");
do
{
try await Task.sleep(nanoseconds: UInt64(1.0 * 1_000_000_000));
}
catch
{
}
}
print("Done async_loop(\(_loop))");
return ret;
}
func async_world() async -> Int
{
async let a: Int = async_loop(5);
async let b = async_loop(3);
var c: [Int] = [await a, await b];
return c[0] + c[1];
}
}
public class Hoge
{
public Hoge()
{
Console.WriteLine("before async_world()");
Task.Run(async () => {
Console.WriteLine(await this.async_world());
});
Console.WriteLine("after async_world() - printed before 'Done async_loop'");
}
public async Task<int> async_loop(int _loop)
{
var ret = 0;
for (var i = 0; i < _loop; i++)
{
ret += 1;
Console.WriteLine(string.Format("Lopping in async_loop({0}): {1}", _loop, i));
try
{
await Task.Delay(Convert.ToInt32(1.0 * 1000));
}
catch
{
}
}
Console.WriteLine(string.Format("Done async_loop({0})", _loop));
return ret;
}
public async Task<int> async_world()
{
Task<int> a = Task.Run(() => async_loop(5));
var b = Task.Run(() => async_loop(3));
var c = new List<int> { await a, await b };
return c[0] + c[1];
}
}
# #ifのようにコメントとして記述すると、最初の#が除去されて変換されます。
#if #else #endif に対応しています。(または、"""native~"""の使用を検討してください)
# #if DEBUG
print("Debug version")
# #else
print("Release version")
# #endif
これは次の様に変換されます。
#if DEBUG
print("Debug version");
#else
print("Release version");
#endif
#if DEBUG
Console.WriteLine("Debug version");
#else
Console.WriteLine("Release version");
#endif
例外のキャッチは標準のPythonと同じように記述します。
Swiftへの変換の場合、ワンライナーで処理する以下のキーワードへの変換にも対応しています。
エラーをオプショナル値に変換して処理するtry?に変換する場合、try9関数を使用します。
エラーの伝播を無効して処理するtry!に変換する場合、try1関数を使用します。
どちらもunipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
from unipy_stubs import *
a = "" # type: str
try:
a = open("a.txt", "r").read()
except Exception as error:
print(error)
b = try9(open("b.txt", "r")) # type: Var
c = try1(open("c.txt", "r")).read() # type: str
これは次の様に変換されます。
var a: String = "";
do
{
a = try open("a.txt", "r").read();
}
catch
{
print(error);
}
var b = try? open("b.txt", "r");
var c: String = try! open("c.txt", "r").read();
exceptがcatchに変換されます。
from unipy_stubs.unity_api.unity_engine import *
class Hoge(MonoBehaviour):
def __init__(self) -> None:
s = None # type: str
try:
_ = len(s) # type: int
except NullReferenceException as e:
print(e)
finally:
s = ""
Debug.Log(s)
これは次の様に変換されます。
public class Hoge : MonoBehaviour
{
public Hoge()
{
string s = null;
try
{
var _ = s.Length;
}
catch (NullReferenceException e)
{
Console.WriteLine(e);
}
finally
{
s = "";
}
Debug.Log(s);
}
}
以下にない組み込み関数の変換には対応していません。
以下にある組み込み関数でもPythonと同等の機能を提供する変換を行えるとは限りません。
abs()
unipy.swiftに互換関数を用意する事で対応しています。
bytes()
unipy.swiftで[byte]のtypealiasとして指定しています。
dict()
[:]に変換されます。コンストラクタを使った初期化には対応していません。
初期値を指定したい場合は、{}を使った記述を使用してください。
float()
unipy.swiftでFloatのtypealiasとして指定しています。
また、float('inf')がFloat.infinityに変換されます。
int()
unipy.swiftでIntのtypealiasとして指定しています。
isinstance()
isキーワードに変換されます。
len()
unipy.swiftに互換関数を用意する事で対応しています。
list()
unipy.swiftでArrayのtypealiasとして指定しています。
max()
unipy.swiftに互換関数を用意する事で対応しています。
min()
unipy.swiftに互換関数を用意する事で対応しています。
open()
unipy.swiftに互換関数を用意する事で対応しています。機能は限定的です。
print()
変換は行われませんがSwiftにも同等の関数がある為利用出来ます。
property()
プロパティの定義に変換されます。詳細は"プロパティの書き方"の項目を参照してください。
range()
Half-Open(..<)オペレータに変換されます。
Closed(...)オペレータが必要な場合はclosedrange関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。
reversed()
unipy.swiftに互換関数を用意する事で対応しています。
sorted()
unipy.swiftに互換関数を用意する事で対応しています。オプションには対応していません。
staticmethod()
staticの定義に変換されます。詳細は"静的メンバ/静的メソッドの書き方"の項目を参照してください。
str()
.descriptionに変換されます。
unipy.swiftでStringのtypealiasとして指定しています。
super()
super(C, self)がsuperに変換されます。(Cにはそのクラス名を指定)
引数を省略した場合の変換には対応していません。
abs()
Math.Abs()に変換されます。
bytearray()
byte[]に変換されます。
dict()
new Dictionary<TKey, TValue>()に変換されます。コンストラクタを使った初期化には対応していません。
初期値を指定したい場合は、{}を使った記述を使用してください。
float()
引数が数字のみならfを付与、違うならfloat.Parse()に変換されます。
また、float('inf')がfloat.MaxValueに変換されます。
int()
int.Parse()に変換されます。代入先の型がshort/longと認識出来た場合は、short.Parse()/long.Parse()に変換。
isinstance()
isキーワードに変換されます。
len()
引数に指定された変数の型に応じて.Countまたは.Lengthに変換されます。
list()
引数が指定されていない場合、引数が固定文字列の場合、
引数が配列の場合、引数がリストの場合、で変換が異なります。
lst1 = list() # type: list[str]
lst2 = list("Hello World") # type: list[char]
ary1 = [1, 2, 3] # type: array[int]
lst3 = list(ary1) # type: list[int]
ary2 = list(lst3) # type: array[int]
これは次の様に変換されます。
var lst1 = new List<string>();
var lst2 = new List<char>();lst2.AddRange("Hello World");
var ary1 = new int[] { 1, 2, 3 };
var lst3 = new List<int>();lst3.AddRange(ary1.Cast<int>());
int[] ary2 = lst3.Cast<int>().ToArray();
print()
Console.WriteLine()に変換されます。
property()
プロパティの定義に変換されます。詳細は"プロパティの書き方"の項目を参照してください。
range()
for文以外で使用した場合、Enumerable.Range()に変換されます。
reversed()
Enumerable.Reverse(X).ToList()に変換されます。
sorted()
X.OrderBy(i => i).ToList()に変換されます。
staticmethod()
staticの定義に変換されます。詳細は"静的メンバ/静的メソッドの書き方"の項目を参照してください。
str()
.ToString()に変換されます。
super()
super(C, self)がbaseに変換されます。(Cにはそのクラス名を指定)
引数を省略した場合の変換には対応していません。
以下にないstrオブジェクトのメソッドの変換には対応していません。
以下にあるstrオブジェクトのメソッドでもPythonと同等の機能を提供する変換を行えるとは限りません。
.count()
unipy.swiftに互換関数を用意する事で対応しています。
.lstrip(), .rstrip(), .strip()
unipy.swiftに互換関数を用意する事で対応しています。
.startswith(), .endswith()
unipy.swiftに互換関数を用意する事で対応しています。
.replace()
unipy.swiftに互換関数を用意する事で対応しています。
.format()
\()を使った記述に変換されます。
.split()
unipy.swiftに互換関数を用意する事で対応しています。
.ljust(), .rjust()
unipy.swiftに互換関数を用意する事で対応しています。
.join()
unipy.swiftに互換関数を用意する事で対応しています。
.rfind(), .find()
unipy.swiftに互換関数を用意する事で対応しています。
.isupper(), .isdigit()
unipy.swiftに互換関数を用意する事で対応しています。
.encode()
unipy.swiftに互換関数を用意する事で対応しています。
.lstrip(), .rstrip(), .strip()
.TrimStart(), .TrimEnd(), .Trim()に変換されます。
.upper(), .lower()
.ToUpper(), .ToLower()に変換されます。
.startswith(), .endswith()
.StartsWith(), .EndsWith()に変換されます。
.replace()
.Replace()に変換されます。
.format()
string.Format()に変換されます。
.split()
.Split()に変換されます。セパレータ文字は''(シングルクォート)で指定する必要があります。
.ljust(), .rjust()
.PadLeft(), .PadRight()に変換されます。埋め文字は''(シングルクォート)で指定する必要があります。
.join()
string.Join()に変換されます。
typingモジュールのFinalを使用します。
from typing import Final
hoge: Final[str] = "Hello"
from typing import Final
hoge = "Hello" # type: Final[str]
これは次の様に変換されます。
let hoge: String = "Hello";
オプショナルとして変数を定義するには、
typingモジュールのOptionalを使用します。
オプショナルの変数をアンラップするには、
unwrap関数を使用します。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
from typing import Final
hoge: Optional[str] = None
print(unwrap(hoge)) # This will result in a run-time error.
from typing import Final
hoge = None # type: Optional[str]
print(unwrap(hoge)) # This will result in a run-time error.
これは次の様に変換されます。
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特有の関数です。
from unipy_stubs.swift_api.swift import *
@dataclass
class HogeData():
def __init__(self) -> None:
self.count: int = 0
@mutating
def add_count(self) -> None:
self.count += 1
from unipy_stubs.swift_api.swift import *
@dataclass
class HogeData():
def __init__(self):
# type: () -> None
self.count = 0 # type: int
@mutating
def add_count(self):
# type: () -> None
self.count += 1
これは次の様に変換されます。
struct HogeData
{
var count: Int = 0;
init()
{
}
mutating func add_count() -> Void
{
count += 1;
}
}
sharp_selector関数を使用します。
unipy_stubs/swift_api/swift.py内に定義されているUniPy特有の関数です。
"""native
import Foundation
"""
class Hoge():
def __init__(self) -> None:
Timer.scheduledTimer(timeInterval=1,
target=self,
selector=sharp_selector(Hoge.sel_method),
userInfo=None,
repeats=False)
@attribute('objc')
def sel_method(self) -> None:
print("Hello #selector World")
"""native
import Foundation
"""
class Hoge():
def __init__(self):
# type: () -> None
Timer.scheduledTimer(timeInterval=1,
target=self,
selector=sharp_selector(Hoge.sel_method),
userInfo=None,
repeats=False)
@attribute('objc')
def sel_method(self):
# type: () -> None
print("Hello #selector World")
これは次の様に変換されます。
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特有の型です。
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self) -> some[View]:
return EmptyView()
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self):
# type: () -> some[View]
return EmptyView()
これは次の様に変換されます。
struct HogeView : View
{
var body: some View
{
return EmptyView();
}
}
二通りの書き方が出来ます。
一つは、
viewbuilder型を戻り値として関数を定義します。
unipy_stubs/swift_api/swift.py内でAnyのTypeAliasとして定義されているUniPy特有の型です。
もう一つは、
@attributeデコレータにViewBuilderクラスを指定し、戻り値にsome[View]を指定する方法です。
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self) -> some[View]:
def _tc_view() -> viewbuilder:
self.my_content1()
self.my_content2()
return VStack(content=_tc_view)
def my_content1(self) -> viewbuilder:
Text("Hello World 1")
@attribute(ViewBuilder)
def my_content2(self) -> some[View]:
Text("Hello World 2")
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self):
# type: () -> some[View]
def _tc_view():
# type: () -> viewbuilder
self.my_content1()
self.my_content2()
return VStack(content=_tc_view)
def my_content1(self):
# type: () -> viewbuilder
Text("Hello World 1")
@attribute(ViewBuilder)
def my_content2(self):
# type: () -> some[View]
Text("Hello World 2")
これは次の様に変換されます。
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と名前が重複してしまう為の処置です。
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeListView(View):
@property
def body(self) -> some[View]:
def _tc_list() -> viewbuilder:
def _tc_foreach(i: int) -> viewbuilder:
Text(str(i))
ForEach(range(5), content=_tc_foreach)
return SwiftUIList(content=_tc_list)
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeListView(View):
@property
def body(self):
# type: () -> some[View]
def _tc_list():
# type: () -> viewbuilder
def _tc_foreach(i):
# type: (int) -> viewbuilder
Text(str(i))
ForEach(range(5), content=_tc_foreach)
return SwiftUIList(content=_tc_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引数に値を指定します。
hd: HogeData = HogeData()
kp: KeyPath[HogeData, Int] = keypath("HogeData.count")
c: int = keypath(hd, kp)
print("Read by keypath: {}".format(c))
kp2: WritableKeyPath[HogeData, Int] = keypath("HogeData.count")
keypath(hd, kp2, 99)
print("Written by keypath: {}".format(hd.count))
hd = HogeData() # type: HogeData
kp = keypath("HogeData.count") # type: KeyPath[HogeData, Int]
c = keypath(hd, kp) # type: int
print("Read by keypath: {}".format(c))
kp2 = keypath("HogeData.count") # type: WritableKeyPath[HogeData, Int]
keypath(hd, kp2, 99)
print("Written by keypath: {}".format(hd.count))
これは次の様に変換されます。
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に変換されます。
t: type[HogeData] = self_(HogeData)
print(t)
t = self_(HogeData) # type: type[HogeData]
print(t)
これは次の様に変換されます。
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用関数に比べて特殊な変換のされ方なので注意してください。
from dataclasses import dataclass
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class Binding_SheetView(View):
def __init__(self, text: Binding[str]):
attribute(Binding)
self.text: str = unbinding(text)
@property
def body(self) -> some[View]:
def _tc_action() -> None:
self.text = "Hello Binding!"
return Button("Change Text", action=_tc_action)
@dataclass
class Binding_ContentView(View):
attribute(State)
text: str = "Hello World!"
attribute(State)
is_show_sheet: bool = False
@property
def body(self) -> some[View]:
def _tc_vstack() -> viewbuilder:
Text(self.text)
Spacer()
def _tc_action() -> None:
self.is_show_sheet = True
Button("Show Sheet", action=_tc_action)
def _tc_sheet() -> viewbuilder:
Binding_SheetView(text=binding(self.text))
return VStack(content=_tc_vstack)\
.sheet(isPresented=binding(self.is_show_sheet), content=_tc_sheet)
from dataclasses import dataclass
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class Binding_SheetView(View):
def __init__(self, text):
# type: (Binding[str]) -> None
attribute(Binding)
self.text = unbinding(text) # type: str
@property
def body(self):
# type: () -> some[View]
def _tc_action():
# type: () -> None
self.text = "Hello Binding!"
return Button("Change Text", action=_tc_action)
@dataclass
class Binding_ContentView(View):
attribute(State)
text = "Hello World!" # type: str
attribute(State)
is_show_sheet = False # type: bool
@property
def body(self):
# type: () -> some[View]
def _tc_vstack():
# type: () -> viewbuilder
Text(self.text)
Spacer()
def _tc_action():
# type: () -> None
self.is_show_sheet = True
Button("Show Sheet", action=_tc_action)
def _tc_sheet():
# type: () -> viewbuilder
Binding_SheetView(text=binding(self.text))
return VStack(content=_tc_vstack)\
.sheet(isPresented=binding(self.is_show_sheet), content=_tc_sheet)
これは次の様に変換されます。
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)部分のような表記です。Image(systemName="bold").imageScale(Image.Scale.large)
このように暗黙メンバ式を用いず省略しない書き方をする必要があります。C#ではPythonと異なりクラスのインスタンス化の際に、new演算子が必要です。
new演算子が必要な箇所でも変換の際に自動付与は行われない為、
new関数を使って明示する必要があります。
unipy_stubs/__init__.py内に定義されているUniPy特有の関数です。
第一引数に通常Pythonでインスタンス化する際と同じように記述します。
オプションの第二引数以降を記述すると、オブジェクト初期化子に変換されます。
from unipy_stubs import *
class PointStruct():
def __init__(self, msg: str) -> None:
print("init msg={0}".format(msg))
self.X: int = 0
self.Y: int = 0
class Hoge():
def __init__(self) -> None:
xy1: PointStruct = new(PointStruct("Hello"))
print("xy1 X={0}, Y={1}".format(xy1.X, xy1.Y))
xy2: PointStruct = new(PointStruct("World"), X=1, Y=2)
print("xy2 X={0}, Y={1}".format(xy2.X, xy2.Y))
from unipy_stubs import *
class PointStruct():
def __init__(self, msg) -> None:
# type: (str) -> None
print("init msg={0}".format(msg))
self.X = 0 # type: int
self.Y = 0 # type: int
class Hoge():
def __init__(self):
# type: () -> None
xy1 = new(PointStruct("Hello")) # type: PointStruct
print("xy1 X={0}, Y={1}".format(xy1.X, xy1.Y))
xy2 = new(PointStruct("World"), X=1, Y=2) # type: PointStruct
print("xy2 X={0}, Y={1}".format(xy2.X, xy2.Y))
これは次の様に変換されます。
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)上で型ヒント有効化する場合等有用です。
上記一覧になくても、このページの解説やサンプルソースにないSwift/C#の文法や式への変換は対応していません。
(但しPythonとSwift/C#で全く同じ文法(式)なのであればそれは動作します)
UniPyが変換対応していない文法や式を使う必要がある場合は、"""native~"""の書き方を使うか、
またはその部分をSwift/C#のソースファイルに切り出して、直接Swift/C#のコードとして記述し、
UniPyで記述するコードからは単純に関数を呼び出すだけにする等にして、運用で対応してください。