Subscribed unsubscribe Subscribe Subscribe

Python でクラスキャスト

cast 関数

わざわざ関数を定義するまでも無いかもしれませんが。

def cast(ParentalClass, child_object):
  child_object.__class__ = ParentalClass

○ 動作確認と使用例

__class__ への代入についてマニュアルの文言を見つけられなかったので、実際に触って期待した動作をするか動作確認しました。

>>> class Person():
...   id = 1
...   age = 30
...   name = 'Yaruo'
... 
>>> 
>>> class Student(Person):
...   id = 100
...   grade = 'junior'
... 
>>> 
>>> p = Person()
>>>
>>> id(p)
4415976168
>>> p.id = 2
>>> p.age = 31
>>>
>>>
>>> # キャスト
>>> cast(Student, p)
>>> 
>>> # 1) キャストしても
>>> # id は変化しない
>>> id(p)
4415976168
>>>
>>> # 2) キャストすると
>>> # 属性が追加されている。
>>> p.grade
'junior'
>>>
>>> # 3) キャストしても
>>> # もともとあった属性は変更されない。
>>> p.id
2
>>> p.age
31

○ __class__ への代入条件

よくわかりませんが、一応、条件があるようです。

__class__ への代入は、両方のクラスが同じ __slots__ を持っているときのみ動作します。

3.3.2.3.1. __slots__ を利用する際の注意

copy_parental_value 関数

__class__ への代入の動作に気づくまでは、次のような関数を定義して使っていました。__class__ への代入に比べると 1) の条件を満たしません。新たに別のオブジェクトが生成されるので id は同じではありません。

# cast っぽいメソッド
def copy_parental_value(child_obj, parental_obj):
  for attr_name in parental_obj.__dict__:
    setattr(child_obj, 
            attr_name,
            getattr(parental_obj, attr_name))
  return child_obj


# 動作例
# 親クラスと子クラスを作成
class ParentalClass():
  parental_attr = ""

class ChildClass(ParentalClass):
  def __init__(self, child_attr):
    self.child_attr = child_attr

parental_object = ParentalClass()
parental_object.parental_attr = "hello"

# 継承
child_object = copy_parental_value(
    ChildClass("Yaruo"), parental_object)
print(child_object.parental_attr)
print(child_object.child_attr)

調べたこと

組み込み関数としてクラスキャストをサポートするものはなさそう。

検討したこと

__init__ で定義してあげると、もっと自然な方法になると思うのですが、例えば、つぎのような具合に。

child_object = ChildClass(parent_object)

python - How to convert (inherit) parent to child class? - Stack Overflow
 

ただし、それは、つぎの2つの理由から却下しました(。-`ω-)ンー
1) 本来の目的で __init___ が使えなくなる。(例えば、Django の models.Model クラスを継承したクラスには適用できません。)
2) クラスごとに __init__ を定義しないといけない。
python - Writing a __init__ function to be used in django model - Stack Overflow

でも組み込みの str, int クラス(型)は、そのような実装をしています。Python多態性はどうやって実装するんやろか。__init__ で場合分けさせてるのでしょうか... ちなみに Python においてクラス class と型 type は同義です。

>>> # 整数 -> 文字列
>>> str(1)
'1'
>>> # 文字列 -> 整数
>>> int('1')
1
>>> # 浮動小数 -> 整数
>>> int(0.1)
0



Remove all ads