简体   繁体   English

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

[英]How do I change out the underlying object inside a method call?

I thought all function and method arguments in Python were passed by reference, leading me to believe that the following code would work: 我认为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! :(

How do I go about changing the object itself within a method call? 如何在方法调用中更改对象本身?

-- EDIT: - 编辑:

So far, the responses have been: 到目前为止,答复是:

1) Change self.__class__ to change the class associated with the current object. 1)更改self .__ class__以更改与当前对象关联的类。
2) "Don't hold it that way." 2)“不要那样抱。”

The original question: How do I make a new object that takes the place of the old one from within a method call defined on the old class? 原始问题:如何在旧类中定义的方法调用中创建一个取代旧对象的对象?

I think the answer might actually be "you can't." 我认为答案可能实际上是“你做不到”。

-- EDIT2: - EDIT2:

Why is everyone deathly afraid of saying "It's not possible"? 为什么每个人都害怕说“这是不可能的”?

When you say self = Butterfly() all you're doing is changing what the variable self points to; 当你说self = Butterfly()你所做的就是改变变量self指向的内容; you're not changing what self is, any more than: 你不是在改变自己,不仅仅是:

x = 1
x = 2

... changes the number 1 to 2. ...将数字1更改为2。

In general, when people need this, rather than change the type of the object (which many languages flat out forbid) people use a composition strategy: 一般来说,当人们需要这个时,不是改变对象的类型(许多语言都是禁止的),人们使用组合策略:

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()

self.__class__ = Butterfly may work, because it's rebinding a qualified name (which is a completely different thing from rebinding a bare name). self.__class__ = Butterfly可以正常工作,因为它重新绑定一名合格的名称(这是一个完全不同的事情,从重新绑定名)。

Rebinding a barename never has any effect on whatever object was previously bound to it (unless that barename was the only reference to that "previous object", in which case the latter may be destroyed -- but that's obviously not the case here, since self is a reference to the same objects as my_obj and so self cannot be the only reference to it). 重新绑定一个裸名称对以前绑定到它的任何对象都没有任何影响(除非该barename是对该“前一个对象”的唯一引用,在这种情况下后者可能被破坏 - 但这显然不是这里的情况,因为self是对my_obj的相同对象的my_obj ,因此self不能是对它的唯一引用)。

Edit : 编辑

You refer to needing to initialize the newly-re-classed object -- that's easy: 你指的是需要初始化新重新分类的对象 - 这很容易:

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

But then in a comment you mention needing to "back up the attributes" -- I don't know what exactly you mean, or why that would at all differ from your "dream case" of assigning directly to self (which would of course blow the attributes away, too). 但随后你提到需要对“备份属性”的注释-我不知道你在说什么,或者为什么这样做,在所有从你的“梦想案”不同,直接分配的self (这当然会把属性吹走了。) Maybe the idea is that (some of) the arguments to __init__ need to be attributes of self ? 也许这个想法是__init__ (某些)参数需要是self属性? Then, for example, you could code it like: 然后,例如,您可以将其编码为:

d, self.__dict__ = self.__dict__, {}
self.__class__ = Butterfly
self.__init__(d['whatever'], d['you'], d['want'])

Remember that variables in Python are names that are bound to objects. 请记住,Python中的变量是绑定到对象的名称。 Read section 4.1 of the Language Reference for more details. 有关更多详细信息,请阅读语言参考的4.1节 The gist of it is that a method parameter is a name that is bound to the object that is passed in the invocation. 它的要点是方法参数是绑定到调用中传递的对象的名称。

When you "assign a new value" to the variable, you are simply binding the local name to a new object. 为变量“分配新值”时 ,只需将本地名称绑定到新对象即可。 The object that it originally referred to is unchanged. 它最初提到的对象没有变化。

When you invoke a mutating method on a variable, the method can change the internal state of the object by using a qualified name such as self.var which is what Alex is referring to in his answer . 当您对变量调用变异方法时,该方法可以使用限定名称(例如self.var来更改对象的内部状态,这是Alex在其答案中所指的内容 If you rebind self , you are not modifying the object. 如果重新绑定self ,则不会修改该对象。

This is one of the more interesting features of Python. 这是Python的一个更有趣的功能。 It is a little different than most other languages in that you really have no way to access the raw object pointer (eg, this ) within a method. 它与大多数其他语言略有不同,因为您实际上无法在方法中访问原始对象指针(例如, this )。 The only thing that you have access to is the bound name of the instance. 您可以访问的唯一内容是实例的绑定名称。

Edit: I don't know how I managed to forget to answer your actual question, but the answer is certainly that you cannot change the underlying object from within a method call. 编辑:我不知道我是如何忘记回答你的实际问题的,但答案肯定是你不能在方法调用中改变底层对象。 My rambling was the why part without actually providing an answer. 我的漫无边际是为什么没有实际提供答案的部分。

Your design is fundamentally flawed. 你的设计存在根本缺陷。 A caterpillar and butterfly are not different creatures, they are different states of the same creature . 毛虫和蝴蝶不是不同的生物,它们是同一生物的不同状态。

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)

If you needed something more sophisticated, the states should be more sophisticated objects that take extra parameters (or simply let the Lepidoptera class take care of it internally). 如果你需要更复杂的东西,状态应该是更复杂的对象,需要额外的参数(或简单地让Lepidoptera类在内部处理它)。

You are effectively asking how to change a lepidoptera into a cricket, which cannot happen. 你实际上是在问如何将鳞翅目变成板球,这是不可能发生的。 This is why you cannot find a way to represent this concept in your program (or why it seems so hacky). 这就是为什么你找不到在你的程序中表示这个概念的方法(或者为什么它看起来如此hacky)。 You've decided to use objects to represent insects , but then you actually create classes that represent insect states . 您决定使用对象来表示昆虫 ,但实际上您创建了表示昆虫状态的类。 Then you wonder why you can't model the insect behaviour properly. 然后你想知道为什么你不能正确模拟昆虫的行为。

Working the other way, if you want classes to represent insect states, then clearly you need other classes to represent to insects themselves, which would then contain these states and the methods to change them. 另一方面,如果你想让类代表昆虫状态,那么显然你需要其他类来代表昆虫本身,然后它们将包含这些状态和改变它们的方法。

In terms of your real problem, it sounds like you're confusing "the thing with the state" with "the state itself". 就你的真实问题而言,听起来你把“与国家的关系”与“国家本身”混为一谈。

As Alex puts it clearly, there is no way to do that strictly. 正如亚历克斯所说,没有办法严格遵守。 And I can't think of a way it such a thing could be desirable. 我无法想到这样的事情是可取的。 Maybe you need a factory function insetad? 也许你需要一个工厂函数insetad? Or different states of the same object? 或同一对象的不同状态?

However, Python being what it is, you can have a "shell" object whose only purpose would be to grant access to an underlyign object. 但是,Python就是这样,你可以拥有一个“shell”对象,其唯一目的是授予对underlyign对象的访问权限。 The underlying object could then be modified: while the shell object remains the same, the underlying object is another one. 然后可以修改底层对象:当shell对象保持不变时,底层对象是另一个。

One possible implementation is as follows (and I've tried again with an unclean implementation for __getattribute__ - it can be made better) 一个可能的实现如下(我再次尝试使用__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)

WIth this you can get stuff like: 有了这个,你可以得到这样的东西:

>>>
>>> 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'>
>>>

. On the change function you can backup whatever attributes you want, and you can exempt other attributes from change in the __getattribute__ method. 在更改功能上,您可以备份所需的任何属性,并且可以在__getattribute__方法中免除其他属性的更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM