Python assignment to self in constructor does not make object the same

天大地大妈咪最大 提交于 2019-12-18 05:12:41

问题


I am making a constructor in Python. When called with an existing object as its input, it should set the "new" object to that same object. Here is a 10 line demonstration:

class A:
    def __init__(self, value):
        if isinstance(value, A):
            self = value
        else:
            self.attribute = value
a = A(1)
b = A(a)#a and b should be references to the same object
print("b is a", b is a)#this should be true: the identities should be the same
print("b == a", b == a)#this should be true: the values should be the same

I want the object A(a) constructed from the existing object a to be a. Why is it not? To be clear, I want A(a) to reference the same object as a, NOT a copy.


回答1:


self, like any other argument, is among the local variables of a function or method. Assignment to the bare name of a local variable never affects anything outside of that function or method, it just locally rebinds that name.

As a comment rightly suggests, it's unclear why you wouldn't just do

b = a

Assuming you have a sound reason, what you need to override is not __init__, but rather __new__ (then take some precaution in __init__ to avoid double initialization). It's not an obvious course so I'll wait for you to explain what exactly you're trying to accomplish.

Added: having clarified the need I agree with the OP that a factory function (ideally, I suggest, as a class method) is better -- and clearer than __new__, which would work (it is a class method after all) but in a less-sharply-clear way.

So, I would code as follows:

class A(object):

    @classmethod
    def make(cls, value):
        if isinstance(value, cls): return value
        return cls(value)

    def __init__(self, value):
        self.attribute = value

Now,

a = A.make(1)
b = A.make(a)

accomplishes the OP's desires, polymorphically over the type of argument passed to A.make.




回答2:


The only way to make it work exactly as you have it is to implement __new__, the constructor, rather than __init__, the initialiser (the behaviour can get rather complex if both are implemented). It would also be wise to implement __eq__ for equality comparison, although this will fall back to identity comparison. For example:

>>> class A(object):
    def __new__(cls, value):
        if isinstance(value, cls):
            return value
        inst = super(A, cls).__new__(cls)
        inst.attribute = value
        return inst
    def __eq__(self, other):
        return self.attribute == other.attribute


>>> a = A(1)
>>> b = A(a)
>>> a is b
True
>>> a == b
True
>>> a == A(1)
True  # also equal to other instance with same attribute value

You should have a look at the data model documentation, which explains the various "magic methods" available and what they do. See e.g. __new__.




回答3:


__init__ is an initializer, not a constructor. You would have to mess around with __new__ to do what you want, and it's probably not a good idea to go there.

Try

a = b = A(1)

instead.




回答4:


If you call a constructor, it's going to create a new object. The simplest thing is to do what hacatu suggested and simply assign b to a's value. If not, perhaps you could have an if statement checking if the value passed in is equal to the object you want referenced and if it is, simply return that item before ever calling the constructor. I haven't tested so I'm not sure if it'd work.



来源:https://stackoverflow.com/questions/28531939/python-assignment-to-self-in-constructor-does-not-make-object-the-same

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!