Python: 什么是有效地改变实例的子类的最佳方式(保留原始实例的变量)?

huangapple go评论70阅读模式
英文:

Python: What is the best way to effectively change the subclass of an instance (keeping the original instances' variables)?

问题

I am not a very great or experienced Python coder, and I'm wondering if there's an easy way to do what I need to do. But I haven't been able to find anything on my own because I'm not sure how to phrase the question properly.

The situation I have is more or less as follows:

class Insect:
  def __init__(self, arg1):
    self.arg1 = arg1

  def insect_method(self):
    print('do insect stuff')

class Caterpillar(Insect):
  def __init__(self, arg1):
    super().__init__(arg1)

  def caterpillar_method(self):
    print('do caterpillar stuff')

class Butterfly(Insect):
  def __init__(self, arg1, arg2):
    super().__init__(arg1)
    self.arg2 = arg2

  def butterfly_method(self):
    print('do butterfly stuff')

I'm not actually simulating caterpillars and butterflies, but it's a similar process so I thought it might help make things clearer.

I have a super class 'Insect' which is never created by itself but exists so subclasses can inherit its methods. Most of the time, I am just spawning the subclasses directly and that's it. But I have a special case where the Caterpillar subclass should become an instance of the Butterfly subclass.

I have searched, and been convinced that changing __class__ is probably not what I want to do in the long run. However, I am also not sure if there's an elegant way to do this otherwise?

The only technique I can think of at the moment is to add in a bunch of if statements like this:

class Insect:
  def __init__(self, arg1):
    self.arg1 = arg1

  def insect_method(self):
    print('do insect stuff')

class Caterpillar(Insect):
  def __init__(self, arg1):
    super().__init__(arg1)

  def caterpillar_method(self):
    print('do caterpillar stuff')

class Butterfly(Insect):
  def __init__(self, arg1, arg2, caterpillar=None):
    if not caterpillar:
        super().__init__(arg1)
    else:
        self.arg1 = caterpillar.arg1
    self.arg2 = arg2

  def butterfly_method(self):
    print('do butterfly stuff')

This would work if the actual code was as simple as this, but it's not, so I'd end up having to make a kind of double of the Insect.__init__ method in the Butterfly.__init__ method and then would have to make sure to constantly update both methods whenever changing anything in the Insect.__init__ method.

Is there an elegant Pythonic method to have this kind of functionality without needing to create a very convoluted __init__ method for the Butterfly class?

Many thanks in advance!!!

英文:

I am not a very great or experienced Python coder, and I'm wondering if there's an easy way to do what I need to do. But I haven't been able to find anything on my own because I'm not sure how to phrase the question properly.

The situation I have is more or less as follows:

class Insect:
  def __init__(self, arg1):
    self.arg1 = arg1

  def insect_method(self):
    print('do insect stuff')

class Caterpillar(Insect):
  def __init__(self, arg1):
    super().__init__(arg1)

  def caterpillar_method(self):
    print('do caterpillar stuff')

class Butterfly(Insect):
  def __init__(self, arg1, arg2):
    super().__init__(arg1)
    self.arg2 = arg2

  def butterfly_method(self):
    print('do butterfly stuff')

I'm not actually simulating caterpillars and butterflies, but it's a similar process so I thought it might help make things clearer.

I have a super class 'Insect' which is never created by itself but exists so subclasses can inherit its methods. Most of the time, I am just spawning the subclasses directly and that's it. But I have a special case where the Caterpillar subclass should become an instance of the Butterfly subclass.

I have searched, and been convinced that changing __class__ is probably not what I want to do in the long run. However, I am also not sure if there's an elegant way to do this otherwise?

The only technique I can think of at the moment is to add in a bunch of if statements like this:

class Insect:
  def __init__(self, arg1):
    self.arg1 = arg1

  def insect_method(self):
    print('do insect stuff')

class Caterpillar(Insect):
  def __init__(self, arg1):
    super().__init__(arg1)

  def caterpillar_method(self):
    print('do caterpillar stuff')

class Butterfly(Insect):
  def __init__(self, arg1, arg2, caterpillar=None):
    if not caterpillar:
        super().__init__(arg1)
    else:
        self.arg1 = caterpillar.arg1
    self.arg2 = arg2

  def butterfly_method(self):
    print('do butterfly stuff')

This would work if the actual code was as simple as this, but it's not, so I'd end up having to make a kind of double of the Insect.__init__ method in the Butterfly.__init__ method and then would have to make sure to constantly update both methods whenever changing anything in the Insect.__init__ method.

Is there an elegant Pythonic method to have this kind of functionality without needing to create a very convoluted __init__ method for the Butterfly class?

Many thanks in advance!!!

答案1

得分: 2

Instead of putting this logic in your __init__, I'd maybe keep the __init__ simple and put the logic to create a Butterfly from a Caterpillar in a classmethod:

class Butterfly(Insect):
    def __init__(self, arg1, arg2):
        super().__init__(arg1)
        self.arg2 = arg2

    @classmethod
    def from_caterpillar(cls, caterpillar, arg2):
        return cls(caterpillar.arg1, arg2)

Then you might have code that does something like:

bob = Caterpillar("Bob")
# metamorphose
bob = Butterfly.from_caterpillar(bob, "Small")

Note that similar to your original code, this doesn't mutate the original Caterpillar object to make it a Butterfly, it creates a new Butterfly instance (and reassigns bob to it).

英文:

Instead of putting this logic in your __init__, I'd maybe keep the __init__ simple and put the logic to create a Butterfly from a Caterpillar in a classmethod:

class Butterfly(Insect):
    def __init__(self, arg1, arg2):
        super().__init__(arg1)
        self.arg2 = arg2

    @classmethod
    def from_caterpillar(cls, caterpillar, arg2):
        return cls(caterpillar.arg1, arg2)

Then you might have code that does something like:

bob = Caterpillar("Bob")
# metamorphose
bob = Butterfly.from_caterpillar(bob, "Small")

Note that similar to your original code, this doesn't mutate the original Caterpillar object to make it a Butterfly, it creates a new Butterfly instance (and reassigns bob to it).

答案2

得分: 2

以下是翻译好的部分:

有两个独立的概念:蝴蝶的物种和蝴蝶的成年阶段。在具有线性类型的纯函数语言中,您可以有一个函数,它接受一个毛虫,生成一只蝴蝶,确保您无法再次使用该毛虫实例。

在Python中,我可以定义以下类:

from abc import ABC, abstractmethod

class Stage(ABC):
    @abstractmethod
    def do_something(self):
        ...

class Caterpillar(Stage):
    def do_something(self):
        print("进行毛虫的操作")

class AdultButterfly(Stage):
    def do_something(self):
        print("进行蝴蝶的操作")

class Butterfly(Insect):
    def __init__(self):
        self.stage = Caterpillar()

    def do_something(self):
        return self.stage.do_something()

    def growup(self):
        self.stage = AdultButterfly()

这允许Butterfly的实例从毛虫过渡到(成年)蝴蝶,而不会:

  1. 从空气中创建成年蝴蝶,或
  2. 从单个毛虫创建多个成年蝴蝶。
英文:

There are two separate notions here: butterfly the species, and butterfly the adult stage of that specie's lifestyle. In a pure functional language with linear types, you might have a function that takes a caterpillar, produces a butterfly, and ensures that you cannot use that caterpillar instance ever again.

In Python, I might define the following classes:

from abc import ABC, abstractmethod


class Stage(ABC):
    @abstractmethod
    def do_something(self):
        ...


class Caterpillar(Stage):
    def do_something(self):
        print("do caterpillar stuff")
    

class AdultButterfly(Stage):
    def do_something(self):
        print("do butterfly stuff")
    

class Butterfly(Insect):
    def __init__(self):
        self.stage = Caterpillar()

    def do_something(self):
        return self.stage.do_something()

    def growup(self):
        self.stage = AdultButterfly() 

This allows an instance of Butterfly to transition from caterpillar to (adult) butterfly without

  1. Creating an adult butterfly from thin air, or
  2. Creating multiple adult butterflies from a single caterpillar

答案3

得分: 1

以下是翻译好的内容:

有几种方法可以做到这一点。可能最可读和“干净”(至少对我来说)的方法是使用@classmethod:要么在Butterfly中提出的from_caterpillar类方法,由另一个答案中的@Samwise提出,要么在Caterpillar中提出的become_butterfly类方法,由评论中的@Pranav Hosangadi提出。

我只是想向您介绍一下isinstance方法。

有了这个,如果您可以使Butterfly的构造函数接受“whatever”或Caterpillar的实例作为arg1,您可以像以下这样做:

class Butterfly(Insect):
    def __init__(self, arg1, arg2):
        if isinstance(arg1, Caterpillar):
            print("OMG!蛻變了!!Digievolution!")
            super().__init__(arg1.arg1)
            # 或者只需:
            # self.arg1 = arg1.arg1
        else:
            super().__init__(arg1)

        self.arg2 = arg2
英文:

There are several ways of doing this. Probably the most readable and "clean" (well... at least to me) are the @classmethods: Either through a from_caterpillar classmethod in the Butterfly proposed by @Samwise in another answer, or have a become_butterfly classmethod in your Caterpillar as proposed by @Pranav Hosangadi in one of the comments.

I just wanted to introduce you to the isinstance method.

With that, if you could make the Butterfly's constructor accept a "whatever" OR an instance of a Caterpillar as arg1, you could do something like the following:

class Butterfly(Insect):
    def __init__(self, arg1, arg2):
        if isinstance(arg1, Caterpillar):
            print("OMG! Metamorphosed!! Digievolution!")
            super().__init__(arg1.arg1)
            # Or just:
            # self.arg1 = arg1. arg1
        else:
            super().__init__(arg1)

        self.arg2 = arg2

huangapple
  • 本文由 发表于 2023年4月6日 22:54:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75950919.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定