Reputation: 409
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
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
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 (likeint
,str
, ortuple
) 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
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