Ivan
Ivan

Reputation: 409

Python: instantiate an object to a reference

What I am asking might not be possible in Python and probably stems from my being used to pointers, but this is how it goes: is the following achievable in python?

class A(object):
    #body of class

a = A() #create a new object
b = A(a) #"b" is now an alias for "a"

My naive attempt to do this was

class A(object):
    def __init__(self, x):
        if isinstance(x, A):
             self = x

But, of course, this is non-sense since self is just a local variable.

EDIT: To be clear, this is not about simple variable assignment (i.e. b=a creates an alias but I don't care). This question came while I was trying to construct a loop with lots of objects of the same type:

class A(object):
    #blah

l = []
for cond in conditions:
    tmp = A(*cond)
    l.append(tmp)

If cond[0] is another object of type A, then tmp is just a reference to that object. Now, there is a work-around for everything, but that's not what I am asking. I'd like to know if there is a solution to my original question.

Upvotes: 0

Views: 856

Answers (3)

freakish
freakish

Reputation: 56487

You can achieve this with __new__:

class A(object):
    def __new__(cls, param=None):
        if isinstance(param, A):
            return param
        return super().__new__(cls)

    def __init__(self, param=None):  # should match __new__ signature
        if isinstance(param, A):
            return
        self.param = param
        # normal initialization

that being said you probably want to rethink your architecture.

Upvotes: 2

juanpa.arrivillaga
juanpa.arrivillaga

Reputation: 96098

I think the best approach is actually to use a meta-class here, especially if you plan on using inheritance, although, meta-classes do add some complexity to that situation. If only, I would appeal to the fact that this is the common approach to creating a singleton class, which is very similar to what you are doing in principle. But here is a sketch:

In [43]: class MyMeta(type):
    ...:     def __call__(cls, *args, **kwargs):
    ...:         if isinstance(args[0], cls):
    ...:             return args[0]
    ...:         else:
    ...:             return super(MyMeta, cls).__call__(*args, **kwargs)
    ...:

In [44]: class A(metaclass=MyMeta):
    ...:     def __init__(self, arg1, arg2):
    ...:         print("initialized")
    ...:

In [45]: a1 = A(1, 1)
initialized

In [46]: a2 = A(2, 2)
initialized

In [47]: a3 = A(a1, 3)

In [48]: [hex(id(x)) for x in (a1, a2, a3)]
Out[48]: ['0x103ccd160', '0x103ccd198', '0x103ccd160']

In [49]: a1 is a2, a1 is a3
Out[49]: (False, True)

Note, from the docs

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

Also, note that using this approach, __init__ isn't called again, which may be what you want.

In [53]: class A(object):
    ...:     def __new__(cls, *args, **kwargs):
    ...:         if isinstance(args[0], A):
    ...:             return args[0]
    ...:         return super().__new__(cls)
    ...:
    ...:     def __init__(self, arg1, arg2):  # should match __new__ signature
    ...:         print("initialized")
    ...:

In [54]: a1 = A(1, 1)
initialized

In [55]: a2 = A(2, 2)
initialized

In [56]: a3 = A(a1, 3)
initialized

In [57]: a1 is a2, a1 is a3
Out[57]: (False, True)

Upvotes: 0

Patrick Haugh
Patrick Haugh

Reputation: 61032

IF you want to do this it has to happen in the objects __new__ method. That's where you can affect the creation of new objects, as opposed to __init__ which modifies them after their creation. One way to do this would be

class A:
    def __new__(cls, copy=None):
        if copy: # This assumes that instances of A cannot be falsy
            if isinstance(copy, A):
                return copy
            else:
                raise ValueError("Argument not of type A")
        else:
            return super().__new__(cls)

a = A()
b = A(a)
b is a # True

Upvotes: 2

Related Questions