如何更改方法调用中的基础对象?

时间:2010-07-09 02:27:09

标签: python oop

我认为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)“不要那样抱。”

最初的问题:如何在旧类中定义的方法调用中创建一个取代旧的 new 对象?

我认为答案可能实际上是“你做不到。”

- EDIT2:

为什么每个人都害怕说“这是不可能的”?

5 个答案:

答案 0 :(得分:3)

当你说self = Butterfly()时,你正在做的就是改变变量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()

答案 1 :(得分:2)

self.__class__ = Butterfly可能会有效,因为它会重新绑定一个合格的名称(与重新绑定名称相比,完全不同)。

重新绑定一个裸名称对以前绑定到它的任何对象都不会有任何效果(除非该裸名称是对该“前一个对象”的唯一引用,在这种情况下后者可能会被销毁 - 但这显然不是这种情况,因为self是对my_obj相同对象的引用,因此self不能是对它的引用)。

修改

你指的是需要初始化新重新分类的对象 - 这很容易:

self.__dict__.clear()
self.__class__ = Butterfly
self.__init__(whatever, you, want)

但是在评论中你提到需要“备份属性” - 我不知道你究竟是什么意思,或者为什么这与你直接分配到{{1的“梦想案例”完全不同(这当然也会破坏属性)。也许这个想法是self的(有些)参数需要是__init__的属性?然后,例如,您可以将其编码为:

self

答案 2 :(得分:1)

请记住,Python中的变量是绑定到对象的名称。阅读section 4.1 of the Language Reference了解更多详情。它的要点是方法参数是绑定到调用中传递的对象的名称。

当您为变量“分配新值”时,您只需将本地名称绑定到新对象即可。它最初提到的对象没有改变。

当您对变量调用变异方法时,该方法可以使用限定名称来更改对象的内部状态,例如self.var Alex is referring to in his answer 。如果重新绑定self,则不会修改对象。

这是Python的一个更有趣的功能。它与大多数其他语言略有不同,因为您实际上无法访问方法中的原始对象指针(例如this)。您唯一可以访问的是实例的绑定名称。

编辑:我不知道我是如何忘记回答您的实际问题的,但答案肯定是您无法在方法调用中更改基础对象。我的漫无边际是为什么部分而没有真正提供答案。

答案 3 :(得分:1)

您的设计存在根本缺陷。毛虫和蝴蝶不是不同的生物,它们是同一生物的不同状态

class Lepidoptera(Insect):

    # Just use strings for states
    CATERPILLAR = "caterpillar"
    BUTTERFLY = "butterfly"

    def __init__(self):
        self.state = Lepidoptera.CATERPILLAR

    def grow_up(self):
        self.state = Lepidoptera.BUTTERFLY

    def status(self):
        """ Override superclass method. """
        print "I am a %s, currently in the state: %s" % (self, self.state)

如果你需要更复杂的东西,状态应该是更复杂的对象,需要额外的参数(或者只是让Lepidoptera类在内部处理它)。

你实际上是在问如何将鳞翅目变成蟋蟀,这是不可能发生的。这是为什么你找不到在你的程序中表示这个概念的方法(或者为什么它看起来如此hacky)。您决定使用对象来表示昆虫,但实际上您创建的代表昆虫状态的类。然后你想知道为什么你不能正确模拟昆虫的行为。

另一种方式,如果你想让类代表昆虫状态,那么显然你需要其他类来代表昆虫本身,然后它们将包含这些状态和改变它们的方法。

就你的真实问题而言,听起来你把“与国家的关系”与“国家本身”混为一谈。

答案 4 :(得分:1)

正如亚历克斯所说,严格来说,没有办法做到这一点。 我无法想到这样的事情是可取的。也许你需要一个工厂函数insetad?或同一对象的不同状态?

然而,Python就是这样,你可以有一个“shell”对象,其唯一目的是授予对underlyign对象的访问权限。然后可以修改底层对象:当shell对象保持不变时,底层对象是另一个。

一种可能的实现如下(我再次尝试使用__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__方法中免除其他属性的更改。