英文:
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
的实例从毛虫过渡到(成年)蝴蝶,而不会:
- 从空气中创建成年蝴蝶,或
- 从单个毛虫创建多个成年蝴蝶。
英文:
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
- Creating an adult butterfly from thin air, or
- 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 @classmethod
s: 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论