概要

何年やっとんねん!! 今回は、そんな甘ったれたことを言って 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. で定義する。

こういう整理は楽しいね。