お前は Python で無駄なクラス変数を定義している

Java や PHP などの一般的なオブジェクト指向言語経験者が Python を書いた時に書いてしまいがちな、誤ったコードを書きます。

私の会社でも、経験の浅い方のコードレビューでたまに見ますので、解説のために記事を書きました。


# 間違っているコード

```python:python
class Employee:
    name: str = ''

    def __init__(self, name: str):
        self.name = name
```

この書き方は、無駄なクラス変数を定義しており、おそらく間違いです。

:::note info
Python 経験者であれば、そこは `@dataclass` デコレータを使ったほうがいいという話になりますが、その話題ではありません。
:::


# 他の言語の場合
このクラスは、 Java でいうと

```java:java
public class Employee {
  public static String name = "";

  public String name;

  public Employee(String name) {
    this.name = name;
  }
}
```

このコードに相当します。

PHP では

```php:php
public class Employee {

    public static string $name = '';

    public string $name;

    function __construct(string $name) {
        $this->name = $name;
    }
}
```

こうなります。

どちらも、 `name` という同名の **static 変数** と **インスタンス変数** を定義してしまっています。

ちなみに、どちらもコンパイル時にエラーとなりますが、それは無視します。

Java や PHP のコードを見ると、 static 変数の `static String name` は、紛らわしいだけで意味が無い変数に見えます。

せめて static 変数でクラス自体の名前? みたいなものを定義する変数が必要なら、インスタンス変数で使う `name` と同名の変数名である `name` はやめてくれと思います。(実際、コンパイルエラーで使えません。)

それと同じ意味のコードが、冒頭の Python コードです。

# Python コードの解説

```python:python
class Employee:
    name: str = ''  # name という名前のクラス変数 (staticみたいなもの)

    def __init__(self, name: str):
        self.name = name  # name という名前のインスタンス変数
```

Python はインスタンス変数の**定義**というものがそもそもありません。
エディタによっては、 `__init__` (コンストラクタ) の中で `self` に代入したものをインスタンス変数の定義とみなします。

そのため

```java:java
class Employee {
    public String name;
    
    public Employee(String name){
        this.name = name;
    }
}
```

このコードを、Python で真似ると

```python:python
class Employee:

    def __init__(self, name: str):
        self.name = name
```

こうなります。

Python は、クラス定義内に、インスタンス変数の定義は書くことができません。

最初の例のように、 `class Employee` の下に `name: str = ''` と書くと、それはクラス自体に所属する変数となり、インスタンス変数とは別の変数なので、おそらく使われることがありません。

もし、クラス変数とインスタンス変数を同名で意図的に定義しているとしたら、ミスの発生しやすいコードとなります。


# クラス変数とインスタンス変数

それでは、実際に下記のクラス変数がどのように動くか、実験をしてみます。


```python:pythonコード
class Employee:

    name: str = 'クラス変数'

    def __init__(self, name: str):
        self.name = name

print('Employee.name:', Employee.name)
```
```:結果
Employee.name: クラス変数
```

↑ クラスをインスタンス化せずに、直接クラス変数を表示しています。


```python:コード
employee = Employee('インスタンス変数')
print('employee.name:', employee.name)
```

```:結果
employee.name: インスタンス変数
```
↑ クラスのインスタンスを作り、コンスタラクタの引数で name を設定しています。


```python:コード
employee2 = Employee('インスタンス変数2')
print('employee2.__class__.name:', employee2.__class__.name)
```
```:結果
employee2.__class__.name: クラス変数
```

↑ 新しいインスタンスを作りました。 インスタンスの `.__class__` で、そのインスタンスの**クラス**にアクセスできます。

```python:コード
print('id(employee.__class__):', id(employee.__class__), ', id(employee2.__class__):', id(employee2.__class__))
```
```：結果
id(employee.__class__): 67727568 , id(employee2.__class__): 67727568
```
↑ `id()` は、そのクラスのメモリの番地みたいなものを表示する関数です。同じIDであれば、同じオブジェクトであることを表します。最初に作ったインスタンス employee と、次に作った employee2 の `__class__` は、同じメモリを参照しています。

```python:コード
print(employee.__class__ is employee2.__class__)
```
```:結果
True
```
↑演算子 is は、同一のオブジェクトかどうかを表します。ID が同じであれば True になります。

`employee.__class__` と `employee2.__class__` は、同一のメモリを参照しており、全く同じものです。

```python:コード
print(employee.__class__ is Employee)
```
```:結果
True
```
そして、 `employee.__class__` は `Employee` と**全く同じもの**です。

```python:コード
employee2.__class__.name = '破壊したクラス変数'
print('employee.__class__.name:', employee.__class__.name)  # employee2 ではなく employee
```
```:結果
employee.__class__.name: 破壊したクラス変数
```

↑ `employee2` の `__class__` の状態を変更すると、`employee.__class__` も同様に変化します。同じものだからです。

```python:コード
print('Employee.name:', Employee.name)
```
```:結果
Employee.name: 破壊したクラス変数
```

↑ `Employee.name` も変化しています。同じものだから。


# 変数の参照優先度
もう一つ、Python のインスタンス変数とクラス変数の特徴的な動作を書きます。

```python:コード
class Fruit:
    name: str = 'クラス変数'

fruit = Fruit()
print('fruit.name:', fruit.name)
```

```:結果
fruit.name: クラス変数
```

↑クラス変数しか持たないクラスをインスタンス化し、インスタンスから変数にアクセスすると、**クラス変数が取得できます**。

```python:コード
print(fruit.name is fruit.__class__.name)
```
```:結果
True
```

```python:コード
print(id(fruit.name), id(fruit.__class__.name))
```
```:結果
140144425798800 140144425798800
```
↑ __class__ を指定してもしなくても、両方は同じメモリを指しています。

```python:コード
fruit.name = 'インスタンス変数'

print(fruit.name is fruit.__class__.name)
```
```:結果
False
```

↑ **ただし、インスタンスの変数にセット(代入)を行うと、先程のクラス変数には代入されず、新たなインスタンス変数が生成されます。**

```python:コード
print(fruit.name, fruit.__class__.name)
```
```:結果
インスタンス変数 クラス変数
```
↑ インスタンス変数ができている場合、優先して表示されます。

```python:コード
del fruit.name
print(fruit.name, fruit.__class__.name)
```
```:結果
クラス変数 クラス変数
```
↑さきほど作ったインスタンス変数を削除した場合、その前から存在していたクラス変数にアクセスできるようになります。


`インスタンス.変数` にアクセスした際は、インスタンスに変数名が存在するかを調べ、あればそれを返す。
なければ、次はクラスに探しに行って、あれば返す。という動作になります。

`print(fruit.name)` とした場合、name がインスタンス変数なのか、クラス変数なのか、コードを見ただけではわかりません。
その時のインスタンスの状態により、動的に変化します。

# `@dataclass` デコレータ

クラス変数とインスタンス変数に対し、同じ名前をつけると混乱につながりがちですが、 `dataclasses` パッケージの `@dataclass` デコレータをつけたクラスは別です。

```python:python
@dataclass
class Employee:

    name: str = ''
```

このようなクラスを定義した場合、

```python
employee = Employee(name='鈴木')
```
を行うと、自動的に employee インスタンスのインスタンス変数 name に 鈴木 が入ります。

`@dataclass` デコレータを使うと、定義したクラス変数を引数にとるコンストラクタが自動生成され、コンストラクタの中で代入処理が行われるようになります。

コードの見た目としても、クラス内にメンバー変数の一覧が定義されているように見えるため、他のオブジェクト指向言語のような見た目となります。

詳しくは、Python ドキュメントの dataclass をご覧ください。

[dataclasses --- データクラス — Python 3.11.0b5 ドキュメント](https://docs.python.org/ja/3/library/dataclasses.html)