Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python, how to deal with A(a) when type(a) is yet A

I need to create a class that mimics this behavior (in mathematics, we say list, dict, are "idempotent"):

>>> list(list([3,4]))
[3, 4]
>>> dict({'a':1,'b':2})
{'a':1,'b':2}

So, if A is my class, I want to write

>>> a = A(1)
>>> b = A(a)
>>> b == a
True

I imagine my class A has to look like this :

class A(object):
   def __init__(self,x):
       if isinstance(x, A) : 
           self = x
       else : 
           self.x = x
           self.y = 'hello'

I try it

>>> A(1).x
1
>>> A(A(1)).x
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'A' object has no attribute 'x'

It does not work !

I don't want to copy x attributes in self, i just want self to BE x or "point" x

Some idea ? Thanks

like image 721
Pierre Puiseux Avatar asked Dec 21 '22 13:12

Pierre Puiseux


1 Answers

What you are looking for is the __new__() method, which takes is run before the class is constructed, as opposed to __init__(), which takes place after. With __new__() you can hook in and replace the object being created.

def __new__(cls, x):
    if isinstance(x, A):
        return x
    else:
        return object.__new__(cls, x)

You can't do this in __init__() as the object has already been created. Changing self simply changes the value of the local variable, it doesn't affect the object.

It's also worth noting that type-checking is almost always the wrong thing to do in Python. Instead, check to see if the class has the information/attributes you need. This way, someone can create a class that acts like yours and works with your code.

As a final word of warning, this is pretty confusing behaviour - people won't expect your class to act like this and it's generally not a great idea. Your example of list() and dict() isn't accurate to what you are doing here, as list(some_list) does not give some_list, it gives a new list which is a copy of some_list - the same is true for dict():

>>> x = [1, 2, 3]
>>> list(x) is x
False 

When you call a constructor, it's natural to expect a new object, rather than a reference to the existing one. I would recommend making A(some_a) copy some_a, and restructure your calling code not to rely on A(some_a) is some_a).

like image 92
Gareth Latty Avatar answered Jan 04 '23 05:01

Gareth Latty