🐡

PythonLunaticTechnic 1 インスタンス前のクラスをコピーする。

1 min read

what's this

タイトル通りインスタンスにする前のクラスをコピーする方法です。
例えば以下のようなコードがあったとします。

class A:
    miracle = "Do you know da wae? count: "

class_list = []
for i in range(3):
    B = A
    B.miracle = B.miracle + str(i + 1)
    class_list.append(B)

このコードを書いた人はクラスAのコピーを三つ作りたいと思っており、そのコピーそれぞれのmiracleに何個目のコピーかの数を追加しようとしました。  
ですがなぜか全部同じ数になっているではありませんか!
(しかも123と巨大でクラスAまで同じになっている。)

for C in class_list:
    print(C.miracle)
# 全部`Do you know da wae? count: 123`が出力された。

解決方法

組み込み関数であるtypeを使って動的にクラスAを継承するクラスを作ることでこれは実現した!

type

以下の二つのXのようにtypeは三つの引数でクラスを作ることができるらしいです。

>>> class X:
...     a = 1
...
>>> X = type('X', (), dict(a=1))

(Pythonドキュメント参照)

修正後コード

class A:
    miracle = "Do you know da wae? count: "

class_list = []
for i in range(3):
    B = type("NewA", (A,), {})
    B.miracle = B.miracle + str(i + 1)
    class_list.append(B)

for C in class_list:
    print(C.miracle)
    # 一回目 : Do you know da wae? count: 1
    # 二回目 : Do you know da wae? count: 2
    # 三回目 : Do you know da wae? count: 3

解説

type("NewA", (A,), {})

NewAという名前でクラスAを継承してる何も属性やメソッドを持たないクラスを作っています。
(A,)はクラスAのみがあるタプルで継承するものにタプルを使う理由は、クラスは複数のクラスを継承することができるからです。
{}の部分はクラスにつける属性やメソッドを指定するためのもので今回は必要ありません。
(一様ここからクラスAのmiracleをオーバーライドすることもできる。(と思う。(試してない。)))

最後

これでとある人は意味不明なコードを動かすことに成功しました!
それと私は説明が苦手なのでもし説明を間違えている箇所があれば伝えてほしいです。

本当に最後の最後

コピーしたクラスで__init__を使う際はsuper().__init__()を置かなければなりません。
これはあくまでコピーではなくコピーする対象のクラスを継承したクラスを作っているだけだからです。
ここ、ご注意。


次のPythonLunaticTechnic

Discussion

ログインするとコメントできます