如何在方法调用中更换底层对象?
我原以为在Python中,所有函数和方法的参数都是通过引用传递的,这让我认为下面的代码会有效:
class Insect:
def status(self):
print "i am a %s" % self
class Caterpillar(Insect):
def grow_up(self):
self = Butterfly() # replace myself with a new object
return
class Butterfly(Insect):
pass
my_obj = Caterpillar()
my_obj.status() # i am a <__main__.Caterpillar instance at 0x100494710>
# ok, time to grow up:
my_obj.grow_up()
my_obj.status() # want new Butterfly object, but it is still Caterpillar! :(
那么,我该如何在方法调用中改变对象本身呢?
-- 编辑:
到目前为止,大家的回答有:
1) 改变self.__class__来更改当前对象关联的类。
2) “别那样拿。”
最初的问题是:我该如何在旧类定义的方法中创建一个新对象来替代旧对象?
我觉得答案可能是“你不能。”
-- 编辑2:
为什么大家都这么害怕说“这是不可能的”?
5 个回答
正如Alex所说,这件事没有严格的方法可以做到。 而且我也想不出有什么方法能让这种做法变得有意义。也许你需要一个工厂函数来代替?或者同一个对象的不同状态?
不过,Python就是这样,你可以创建一个“外壳”对象,它的唯一目的是让你访问一个底层对象。底层对象可以被修改:虽然外壳对象保持不变,但底层对象却可以是另一个。
下面是一个可能的实现方式(我又尝试了一下不太干净的实现方式来处理__getattribute__
- 这个可以做得更好)
# -*- coding: utf-8 -*-
class Insect(object):
pass
class Catterpillar(Insect):
a = "I am a caterpillar"
class Butterfly(Insect):
a = "I am a butterfly"
class Cocoon(object):
def __init__(self, *args, **kw):
self._true = Catterpillar(*args, **kw)
def __getattribute__(self, attr):
try:
if attr != "_true":
return getattr(object.__getattribute__(self, "_true"),attr)
except AttributeError:
pass
return object.__getattribute__(self, attr)
def __setattr__(self, attr, val):
if attr != "_true":
setattr(self._true, attr, val)
else:
object.__setattr__(self, attr, val)
def change(self, *args, **kw):
self._true = Butterfly(*args, **kw)
这样你就可以得到一些像这样的东西:
>>>
>>> a= Cocoon()
>>> a.a
'I am a caterpillar'
>>> a.__class__
<class '__main__.Catterpillar'>
>>> a.change()
>>> a.a
'I am a butterfly'
>>> a.__class__
<class '__main__.Butterfly'>
>>>
在这个更改函数中,你可以备份你想要的任何属性,并且可以在__getattribute__
方法中排除其他属性不被更改。
self.__class__ = Butterfly
可能会有效,因为它是在重新绑定一个有名的名称(这和重新绑定一个无名名称是完全不同的事情)。
重新绑定一个无名名称对之前绑定的对象没有任何影响(除非这个无名名称是唯一指向那个“之前对象”的引用,在这种情况下,那个对象可能会被销毁——但显然在这里不是这种情况,因为 self
是指向和 my_obj
相同对象的引用,所以 self
不能是它的唯一引用)。
编辑:
你提到需要初始化重新分类的对象——这很简单:
self.__dict__.clear()
self.__class__ = Butterfly
self.__init__(whatever, you, want)
但在评论中你提到需要“备份属性”——我不太明白你具体指的是什么,或者为什么这会和你直接赋值给 self
的“理想情况”有所不同(这样做当然也会丢掉属性)。也许你的意思是 __init__
的一些参数需要成为 self
的属性?那么,例如,你可以这样编码:
d, self.__dict__ = self.__dict__, {}
self.__class__ = Butterfly
self.__init__(d['whatever'], d['you'], d['want'])
当你说 self = Butterfly()
的时候,你实际上只是改变了变量 self
指向的对象;这并没有改变 self
本身,就像:
x = 1
x = 2
... 并没有把数字 1 变成 2 一样。
通常,当人们需要这样做的时候,他们并不是改变对象的类型(很多编程语言是完全不允许这样做的),而是采用一种组合策略:
class Lepidoptera(Insect):
def __init__(self):
self.stage = Caterpillar()
def status(self):
print 'I am a %s' % self.stage
def grow_up(self):
if self.stage.adult:
raise TypeError('Already an adult')
self.stage = Butterfly()