1

I have a class that I want it to accept an instance of that same class as initialization; in such case, it will simply return that instance.

The reason is that I want this class to accept a myriad of initialization values and then the proceeding code can use this as an object with known properties, independent on how it was initialized.

I have thought of something like:

class c(object):
  def __new__(cls, *args, **kwargs):
    if isinstance(args[0], c):
      return args[0]
    else:
      return super(c, cls).__new__(cls, *args, **kwargs)

The problem is that I don't want __init__() to be called when initialized in this manner. Is there any other way?

Thanks!

Pato
  • 113
  • 7
  • Why don't you want `__init__()` to be called? Would it be OK if `__init__()` were called, but didn't do anything? – John Y Jul 25 '16 at 17:27
  • `__init__()` should be called only when args[0] is not of the class – Pato Jul 25 '16 at 21:56
  • Yes, you already stated that in your question. But I am asking you: Why is that important? How important is it that you avoid the call? You can easily make the call do nothing when initializing with an instance, or do something when initializing with other values. – John Y Jul 25 '16 at 22:30
  • Hmmm, you are right... for some reason at first it did not look too pythonic to have the `if` clause checked twice in two different functions, but it could look better than factory or initializing in `__new__`, and simpler than metaclassing. Thanks everyone! – Pato Jul 25 '16 at 23:20

3 Answers3

1

You probably want to use a factory (f.e. see this question for details or google). Or just use a class method for what you want, f.e.:

class C(object):
    @classmethod
    def new(cls, *args, **kwargs):
        if isinstance(args[0], cls):
            return args[0]
        else:
            return cls(*args, **kwargs)

obj = C.new()
obj2 = C.new(obj)
Community
  • 1
  • 1
kstera
  • 11
  • 2
1

You can use a metaclass

class InstanceReturnMeta(type):  # You should probably think of a better name
    def __call__(cls, *args, **kwargs):
        if args and isinstance(args[0], cls):
            return args[0]
        instance = cls.__new__(cls, *args, **kwargs)
        instance.__init__(*args, **kwargs)
        return instance


class Test(object):
    __metaclass__ = InstanceReturnMeta
    def __init__(self, value):
        self.value = value

Let's test it

In [3]: instance1 = Test(0)
In [4]: instance2 = Test(instance1)
In [5]: print id(instance1) == id(instance2)
Out[5]: True

The ids are identical, hence both variables reference the same instance.

P.S. I assume you are on Python 2, since your class explicitly inherits from object.

Eli Korvigo
  • 10,265
  • 6
  • 47
  • 73
1

The standard way to do this is to simply not do your initialization in __init__. Do it in __new__.

user2357112
  • 260,549
  • 28
  • 431
  • 505