🧐

Pythonでインスタンスオブジェクトをサブクラスのものに変換する

2022/10/15に公開約1,900字

概要

言葉でうまく説明できそうに無いのでコードをどうぞ

main.py
class ClassA:
    def __init__(self):
        self.a = 1

    def method_a(self):
        print(self.a)


class ClassB(ClassA):
    def __init__(self):
        super().__init__()
        self.b = 2

    def method_b(self):
        print(self.b)


instance_a = ClassA()

... # instance_aをClassBのインスタンスに変換

instance_a.method_a()
instance_a.method_b()

このコードが正しく動作するようにしたいという訳だ。

解決策

stack overflowより
https://stackoverflow.com/questions/597199/converting-an-object-into-a-subclass-in-python

__class__を上書きして、instanceの属するクラスを変更すればいいらしい。
しかし、上書きしただけでは__init__が呼び出されないので、手動で初期化してやる必要がある。

というわけで、コードを少し書き換えてみる。

main.py
class ClassA:
    def __init__(self):
        self.a = 1

    def method_a(self):
        print(self.a)


class ClassB(ClassA):
    def __init__(self):
        super().__init__()
-        self.b = 2
+        self._init_b()

+    def _init_b(self):
+        self.b = 2

    def method_b(self):
        print(self.b)


instance_a = ClassA()

- ... # instance_aをClassBのインスタンスに変換
+ instance_a.__class__ = ClassB
+ instance_a._init_b()

instance_a.method_a()
instance_a.method_b()

これで無事、正常に動くコードになった。

上記のコードでmypyを走らせるとどうなるだろうか?

正解はこちら

main.py:24: error: "ClassA" has no attribute "_init_b"; maybe "__init__"?
main.py:27: error: "ClassA" has no attribute "method_b"; maybe "method_a"?

mypyはクラスが変わったことを検知してくれない。
なので、"わからせる"必要がある。

main.py
class ClassA:
    def __init__(self):
        self.a = 1

    def method_a(self):
        print(self.a)


class ClassB(ClassA):
    def __init__(self):
        super().__init__()
        self._init_b()

    def _init_b(self):
        self.b = 2

    def method_b(self):
        print(self.b)


instance_a = ClassA()

instance_a.__class__ = ClassB
+ assert isinstance(instance_a, ClassB)
instance_a._init_b()

instance_a.method_a()
instance_a.method_b()

これで再びmypyを走らせると

Success: no issues found in 1 source file

無事黙らせることが出来た。

Discussion

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