前回、Pythonについて、構文レベルの話を書きました。
今回は、名前空間や、クラスについて述べていきます。
名前空間
名前空間とは
名前空間とは、いろいろ説明がありますが、クラスをわかりやすく整理するためのもの、と考えてよいと思います。
フォルダとファイルの関係、みたいなものです。*1
- Wikipediaの説明
IDEを使う場合、フォルダの物理構成と合わせた形で、初期の名前空間が振られます。
別のフォルダにある同名クラスは、別物として扱われます。
フォルダに同じ名前ファイルが作れないように、同じ名前空間に同じクラスは作れません。
C#の名前空間
- 公式の説明
名前空間 (C# プログラミング ガイド) | Microsoft Docs
公式の説明では、「名前空間はネストできる」とか、いろいろ書いてあります。
なお、経験上、「一つのファイル内で名前空間を分ける」ことは、ほとんどありません。
理由は、ネストが深くて見づらいから、と思っています。
C#では、ソースコードの中に、明示的に名前空間を指定します。
名前空間の中にいるクラスが、その名前空間に属することが、ソースを見たらわかります。
namespace My.Practice { public class ClassPractice{ } }
上のように書くと、My.Practice
名前空間のClassPractice
クラスを表します。
C#の難しいところは、名前空間とは別に、アセンブリという概念(Visual Studioのプロジェクト単位)があり、どう分けたらよいのかわかりづらい点にあると思います。*2
Pythonの名前空間
- チュートリアルの説明
この説明だけでは、何のことやらわかりませんでした。
結局、具体的にどうやって名前空間を識別しているのかについては、触れられていません。
というのも、前提として「モジュール」が存在するためです。
- チュートリアル(モジュール)
6. モジュール (module) — Python 3.6.4 ドキュメント
Python では定義をファイルに書いておき、スクリプトの中やインタプリタの対話インスタンス上で使う方法があります。このファイルを モジュール (module) と呼びます。
とあるように、.py
ファイルを、モジュールと呼んでいます。*3
ここは、Javaのモジュールに近いと思います。
ただ、決定的に違う点がありまして…
Pythonでは、名前空間やモジュール名を、ファイル内に宣言しません。
上記のようなソリューション構成(フォルダ構成も同じ)だとしたら、
ファイル名 | 種類 |
---|---|
PythonPractice.py | モジュール(名前空間なし) |
My | 名前空間 |
Practice.py | モジュール(My名前空間配下) |
となります。
なお、単にフォルダを作るだけではダメで、__init__.py
というファイルがあるフォルダを、名前空間と認識するそうです。*4
名前空間は、別名パッケージ…なのでしょうか?細かい部分が、あまり理解できていません。
クラスについて
名前空間については、正直謎が多いです。
クラスも、C#やJavaに慣れている人にとっては、とっつきづらいように思います。
C#のクラス
C#のクラスですが、他の要素との関りで言うと、以下のことが言えます。
- クラスは、名前空間もしくは他のクラスに属する
- メソッドやプロパティ、フィールド等は、必ずクラスに属する
前者は、名前空間の下にクラスが作られること、クラスはネストできることを言っています。
クラスのネストは、名前空間のネストに比べたら、よく使います。
後者は、グローバルな参照は無い、ということを示しています。*5
Pythonのクラス
Pythonでは、変数等は必ずしもクラスに属しません。
ただ、必ずいずれかのパッケージには属します。
両者の違い
クラスそのものの違いは置いておいて、C#とPythonで同じように書いても、意味が違うものについて書いてみます。
フィールド
public class Practice{ int _value = 10; public int Value{ get{return _value;} } public Practice(int val){ _value = val; } }
class Practice: val = 10 def __init__(self, value): val = value
という、見た目が似たクラスがあります。
def __init__(self, value):
は、コンストラクタです。
これを、
var p1 = new Practice(5); var p2 = new Practice(8); Console.WriteLine(p1.Value.ToString()); Console.WriteLine(p2.Value.ToString());
とした場合、
5 8
と出力されます。
p1 = Practice.Practice(5) p2 = Practice.Practice(8) print(p1.val) print(p2.val)
とした場合、
10 10
と出力されます。
実は、C#のフィールドと同じ書き方をすると、Pythonでは「クラス変数」として扱われます。
C#でいうと、
public class Practice{ public static int _value = 10; public Practice(int val){ _value = val; } }
という定義に近いです。*6
class Practice: val = 10 def __init__(self, value): self.val = value
のように、変数self
の値を変更します。
なお、上のように書くと、「クラス変数のval
」と、「インスタンス変数のval
」は、別の値として扱われます。
よって、
p1 = Practice.Practice(5) p2 = Practice.Practice(8) print(p1.val) print(p2.val) print(Practice.Practice.val)
と呼び出すと、
5 8 10
と出力されます。
上二つは、「インスタンス変数のval
」にアクセスし、print(Practice.Practice.val)
は、「クラス変数のval
」にアクセスしているためです。
つまり、Pythonでは、クラス変数とインスタンス変数を厳密に区別し、完全に別のものとして扱っているようです。
インスタンスに属する値は、C#のフィールドのように定義せず、self.val
のように書くだけで、定義できます。
コンストラクタの中で、初期化してあげると、分かりやすいと思われます。
関数
private int _val; public int GetValue(){ return _val; }
C#では、上記のように書くと、自身のインスタンスの_valを返します。
同じ意味を示す処理を書くと、
def GetValue(self): return self.val
となります。
先頭のself
ですが、インスタンスメソッドの場合に暗黙的に渡される、「メソッドを呼び出したインスタンス」を表します。*7
インスタンスの状態を変えるなら、このself
に対して処理しましょう。
補足
class Practice: def __init__(self, value): self.val = value
というクラスに対し、
p1 = Practice.Practice(5) print(p1.val) # 定義されていない変数を外から準備 p1.valval = 3 print(p1.valval)
とすると、どうなるでしょう?
なんと、そのまま動作します。
結果はこうなります。
5 3
C#とかJavaではありえない挙動です…
なんで、クラスで定義していない変数が追加できるんだ…と思いました。
Pythonはそういうもの、と考えましょう。*8
もっとも、こうやって変数を追加しても、クラス自体の処理には何ら影響はないです。
感想
チュートリアル程度ですが、Pythonについて勉強して、あまりにもC#と違いすぎて、最初は戸惑いました。
でも、正直、Python好きになりました。
何より、文が短くてわかりやすいです。
大規模になると、型指定不要という部分が弱点になりそうですが、3.5から「型アノテーション」が追加されたので、うまく使えばいいのでしょうきっと。
いろいろ、作ってみたい今日この頃です。
追記
まさかの、「その3」記事を書きました!