概要
何年やっとんねん!! 今回は、そんな甘ったれたことを言って Pythonista の末席を汚している緑さんの根性を叩き直そうぜ。
何がごっちゃになっているって?
いやあ、このへん↓がさ……
- Python のクラスで、インスタンス変数を定義するときは、
__init__
の中でself.
で定義するじゃん? それはいいよね。 - JavaScript でインスタンス変数を定義するときに、
constructor
の外に書いているときがあるんだよ。まあ、それも、そういう仕様なんだな、でいいよ。じゃあ、 Python でも “インスタンス変数をコンストラクタ外に書く” ことができたっけ?? わからん……。 - Python で、コンストラクタ (
__init__
) の外に変数を定義したとき、それってクラス変数になって、クラス外で好き勝手変えられるんじゃなかったっけ? 逆に、それって JavaScript で出来るんだっけ?
今回のノートに使う Python, Node.js バージョン
python -V
# --> Python 3.10.12
node -v
# --> v20.11.0
クラス外からいじれる (public) “クラス変数”
Python の場合。
class MyObject:
foo = 'ふー。'
print(MyObject.foo)
# --> ふー。
JS の場合。
class MyObject {
static foo = 'ふー。'
}
console.info(MyObject.foo)
// --> ふー。
クラス内だけからいじれる (private) “クラス変数” -
Python の場合。 Pythonista に言わせてみれば、クラス外からいじることもできるけど、まあアレ (mangling) は裏技だから! あと @classmethod
を使って書いたほうが素敵だけど、それも Pythonista のこだわりだから。
class MyObject:
__foo = 'ふー。'
def main():
print(MyObject.__foo)
# --> ふー。
MyObject.main()
print(MyObject.__foo)
# --> AttributeError: type object 'MyObject' has no attribute '__foo'
JS の場合。
class MyObject {
static #foo = 'ふー。'
static main() {
console.info(MyObject.#foo)
// --> ふー。
}
}
MyObject.main()
console.info(MyObject.#foo)
// --> SyntaxError: Private field '#foo' must be declared in an enclosing class
クラス外からいじれる (public) “インスタンス変数”
Python の場合。
class MyObject:
def __init__(self):
self.foo = 'ふー。'
my_object = MyObject()
print(my_object.foo)
# --> ふー。
JS の場合。ぼくにとって一番ややこしいのがここかな。
- コンストラクタ外で定義しても、コンストラクタ内で定義してもいい、っていうのがややこしい
- コンストラクタ外で定義したときの見た目が、 Python の public クラス変数と同じなのがややこしい
class MyObject {
foo = 'ふー。'
constructor() {
// ここで定義してもいい!!
this.foo = 'ふー。'
}
}
const myObject = new MyObject()
console.info(myObject.foo)
// --> ふー。
クラス内だけからいじれる (private) “インスタンス変数”
Python の場合。
class MyObject:
def __init__(self):
self.__foo = 'ふー。'
def main(self):
print(self.__foo)
# --> ふー。
my_object = MyObject()
my_object.main()
print(my_object.__foo)
# --> AttributeError: 'MyObject' object has no attribute '__foo'
JS の場合。
class MyObject {
#foo = 'ふー。'
constructor() {
// ここで定義してもいい!!
this.#foo = 'ふー。'
}
main() {
console.info(this.#foo)
// --> ふー。
}
}
const myObject = new MyObject()
myObject.main()
console.info(myObject.#foo)
// --> SyntaxError: Private field '#foo' must be declared in an enclosing class
ごっちゃになっているところはすっきりした?
- Python でも “インスタンス変数をコンストラクタ外に書く” ことができたっけ?: 不可能。 Python では、
self
があるものだけがインスタンス変数だ。わかりやすいね! やっぱ Python だね! - Python で、コンストラクタ (
__init__
) の外に変数を定義したとき、それってクラス変数になって、クラス外で好き勝手変えられるんじゃなかったっけ?: その通り。 - 逆に、それって JavaScript で出来るんだっけ?: 出来る。それ (クラス変数) は JavaScript では、
static
をつけて定義するんだけど、それはクラス外部から好き勝手いじれる。
JavaScript では、 “コンストラクタ外部に、クラス変数もインスタンス変数も定義できる” せいで、マーカー (この場合は static
) が増えることになり、シンプルさを損なっているね。 (もし、 Python と同じように、インスタンス変数の定義可能領域がメソッド内部に限られた場合、コンストラクタ外部に static
という表記は不要になるから。)
今回の要点
- Public なフィールドを作りたいとき、 Python でも JavaScript でも、特別なことは不要。
- Private なフィールドを作りたいとき、 Python では変数に
__
をつけて、 JavaScript では#
をつける。 - クラス変数を作りたいとき、 Python では特別なことは不要で、 JavaScript では
static
が必要。 - インスタンス変数を作りたいとき、 Python ではコンストラクタ内で
self.
で定義して、 JavaScript ではクラス直下でstatic
無しで定義 or コンストラクタ内でthis.
で定義する。
こういう整理は楽しいね。