C# と Python
これは何の話?
普段C#を書いている自分が、Pythonコードを書くためのメモ
クラス
class Hoge
{
}
class Hoge: pass
書くものがないときは、passを書く。
new する
var h = new Hoge();
h = Hoge()
インスタンスメソッド
public class Hoge{ public void Fuga(){ } }
class Hoge: def Fuga() -> None : pass
引数なしコンストラクタ
public class Hoge{ public Hoge(){ } }
class Hoge: def __init__(self): pass
コンストラクタに self が必要なところで驚く。 var h = Hoge() とかけば init が呼ばれてHogeインスタンスができる。 なので、実際のところ、呼び出し時に self を渡すコードを書くことはない。
引数ありコンストラクタ
public class Hoge{ public Hoge(string s){ } }
class Hoge: def __init__(self, s: str): pass
selfのあとに、引数を書く。 この例では引数は受け取っているけど、使っていない。
Pythonは型を明示しなくても動作するけど、C#に慣れた身としては明示しておいた方がしっくりくる。
mypyのようなコード検査ツールでは、型指定されていればそれに応じたチェックがなされる。
この例では、変数s
は str型
なので、 Hoge(123)
は、mypyではエラーが検出される。
実行時にはチェックされない。
インスタンスフィールド ( Python では 属性 と呼ぶ )
public class Hoge{ private string s; }
class Hoge: def __init__(self): self.s: str = ""
Pythonでは、この s のことを属性という。
単に宣言だけすることはできないので、何らかの初期値を与えておく。
staticフィールド ( Python では クラス変数 と呼ぶ )
public class Hoge{ public static string s; }
from typing import ClassVar #ClassVar型構築子を使う場合に必要 class Hoge: s1: str = "" s2: ClassVar[str] h = Hoge() Hoge.s1 = "123" #OK クラス名から辿れる h.s1 = "123" #OK インスタンスからも辿れる h.s1 = 123 #NG 型があっていない Hoge.s2 = "123" #OK クラス名から辿っているのでOK h.s2 = "123" #NG インスタンスから辿っているのでNG(これがClassVar型構築子の効果) Hoge.s2 = 123 #NG 型があっていない
C#er的には、インスタンスフィールドを書いたような気分になってしまうが これで、staticフィールドになる。インスタンス変数からアクセスできてしまったりするので、C#のstatic フィールドとは ちょっと違う。 ClassVar[型] のように型を指定すれば、クラス名でのアクセスを強制できる。
複数のコンストラクタ
public class Hoge{ private string s; private int i; public Hoge(string s){ this.s = s; } public Hoge(int i){ this.int = i; } }
class Hoge: def __init__(self): self.s :str= "" self.i :int= 0 @classmethod def CreateHoge1(cls, s : str): c = cls() c.s = s return c @classmethod def CreateHoge2(cls , i : int): c = cls() c.i = i return c h0 = Hoge() h1 = Hoge.CreateHoge1("abc") h2 = Hoge.CreateHoge2(123)
端的にいうと、複数のコンストラクタを作ることはできない。 init を複数個書くと実行時にエラーになる。 selfの属性として、インスタンスフィールドを表現する都合上、確かに複数の init の記述を認めると 読み手もどう解釈すればいいのかわからないのでこうなっている、と自分は受け止めている。
複数のコンストラクタを作りたい場合は、インスタンスを作るメソッドを作ることで代用する。 これには、 @classmethod デコレータをつけたメソッドを使うことが一般的なようだ。 これに相当するものは C# にはない。
第一引数を cls
とするのは慣例なので従うこと。
次の@staticmethodでも代用できそうにも思えるけど @classmethodにしかできないこともあり、後述する。
staticメソッド
public class Hoge{ public static void Piyo(string s){ Console.System.WriteLine(s); } }
class Hoge: @staticmethod def Piyo(s: str) -> None: print(s) h1 = Hoge() Hoge.Piyo("static") h1.Piyo("instance") # インスタンスから呼ぶこともできる
classmethod
classmethod は、C.f() とも呼び出せるし、 C().f() のように呼ぶこともできる。 このようなメソッドは、C#にはない。
複数のコンストラクタの代用品を作るときに classmethod を使用した。
staticmethodでも似たようなことができるが、cls()
のように、init の呼び出しを
抽象的に書くことはできない。
clsのように書くことで、継承のときに、cls()
がサブクラスの __init__()
に読み替えられる。
staticmethod で、コンストラクタ代わりのメソッドを書くと、継承時に困る。
先ほど、classmethodでコンストラクタ代わりのメソッドを書いたが 戻り値の型指定を省略していた。継承を考慮すると、戻り値の型指定を書くことはできない。
class Hoge: def __init__(self): self.s :str= "" self.i :int= 0 @classmethod def CreateHoge1(cls, s : str) -> cls : # このように書くことはできない c = cls() c.s = s return c @classmethod def CreateHoge2(cls , i : int) -> Hoge : # これもダメだった。意外。この時点ではクラスの定義が完了していない、ということのようだ。 c = cls() c.i = i return c
using
+-- Main.py | \---Fuga | Hoge.py | Piyo.py \ __init__.py
Hogeクラスが、Hoge.py に定義されている。 Piyoクラスが、Piyo.pyに定義されている。
from Fuga import Hoge,Piyo h = Hoge.Hoge() p = Piyo.Piyo() print(h) print(p)
from Fuga.Hoge import * from Fuga.Piyo import * h = Hoge() p = Piyo() print(h) print(p)