How can I get __init__ to take a new value or an existing instance?

I have a template that looks something like this:

class Foobar(object): # instances of this class will be referenced by others def __init__(self, value): self.value = value class Foo(object): def __init__(self, value, foobar) self.value = value if isinstance(foobar, Foobar): self.foobar = foobar else: self.foobar = Foobar(foobar) class Bar(object): def __init__(self, value, foobar) self.value = value if isinstance(foobar, Foobar): self.foobar = foobar else: self.foobar = Foobar(foobar) 

This allows Foo and Bar to accept either a new value (to create a Foobar ) or an existing instance of Foobar as an argument to Foobar .


I would like to get rid of this redundant code:

 # ... if isinstance(foobar, Foobar): self.foobar = foobar else: self.foobar = Foobar(foobar) 

I reviewed the following, but this does not work due to infinite recursion in Foobar.__new__() :

 class Foobar(object): def __new__(cls, value): if isinstance(value, cls): return value else: return Foobar(value) def __init__(self, value): self.value = value class Foo(object): def __init__(self, value, foobar) self.value = value self.foobar = Foobar(foobar) class Bar(object): def __init__(self, value, foobar) self.value = value self.foobar = Foobar(foobar) 

What is the best way to let classes create new instances or use existing instances depending on the values ​​passed to __init__ ?

+4
source share
2 answers

Another option is to split the duplicated code into a mixin class ...

 class Foobar(object): def __init__(self, value): self.value = value class FoobarMixin(object): def __init__(self, **kwargs): foobar = kwargs['foobar'] if isinstance(foobar, Foobar): self.foobar = foobar else: self.foobar = Foobar(foobar) class Foo(FoobarMixin): def __init__(self, value, **kwargs): super(Foo, self).__init__(**kwargs) self.value = value print self.value, self.foobar class Bar(FoobarMixin): def __init__(self, value, **kwargs): super(Bar, self).__init__(**kwargs) self.value = value print self.value, self.foobar foo = Foo('foo', foobar='foobar') bar = Bar('bar', foobar=Foobar('foobar')) 

... which prints ...

 foo <__main__.Foobar object at 0x7fa0fedf6050> bar <__main__.Foobar object at 0x7fa0fedeaf10> 
+1
source

You can get rid of recursion by calling the base class __new__() :

 class Foobar(object): def __new__(cls, value): if isinstance(value, cls): return value else: return object.__new__(cls, value) def __init__(self, value): self.value = value 

Note that the first parameter of __new__() is the class, not self .

However, I'm not sure if this is a useful model. In general, I would recommend taking instances in the constructor and leaving the object construction to the calling code. Although the magic that does the right thing often seems convenient, it usually causes more problems along the way than it costs.

+4
source

Source: https://habr.com/ru/post/1488546/


All Articles