9

So I am trying to override __new__ and let it exist as a factory to create derived instances. After reading a bit on SO, I am under the impression that I should be calling __new__ on the derived instance as well.

BaseThing

class BaseThing:
    def __init(self, name, **kwargs):
        self.name = name

    # methods to be derived

ThingFactory

class Thing(BaseThing):
    def __new__(cls, name, **kwargs):
        if name == 'A':
           return A.__new__(name, **kwargs)
        if name == 'B':
           return B.__new__(name, **kwargs)        

    def __init__(self, *args, **kwargs):
        super().__init__(name, **kwargs)

   # methods to be implemented by concrete class (same as those in base)

A

class A(BaseThing):
    def __init__(self, name, **kwargs):
       super().__init__(name, **kwargs)

B

class B(BaseThing):
    def __init__(self, name, **kwargs):
       super().__init__(name, **kwargs)
 

what I am expecting was that it'd just work.

>>> a = Thing('A')

gives me TypeError: object.__new__(X): X is not a type object (str)

I am bit confused by this; when I just return a concrete instance of derived classes, it just worked. i.e.

def __new__(cls, name, **kwargs):
        if name == 'A':
           return A(name)
        if name == 'B':
           return B(name)

I don't think this is the correct way to return in __new__; it may duplicate the calls to __init__.

when I am checking signatures of __new__ in object it seems be this one:

@staticmethod # known case of __new__
def __new__(cls, *more): # known special case of object.__new__
    """ Create and return a new object.  See help(type) for accurate signature. """
    pass

I didn't expect this was the one; I'd expect it came with args and kwargs as well. I must have done something wrong here.

it seems to me that I need to inherit object directly in my base but could anyone explain the correct way of doing it?

stucash
  • 1,078
  • 1
  • 12
  • 23
  • 1
    You seem to have assigned something to `X`. – user2357112 Dec 01 '17 at 22:19
  • @user2357112 updated the question. – stucash Dec 01 '17 at 22:20
  • 1
    Your thing factory doesn't need to be a subclass of `Thing`; a regular function like `def thing_factory(name, *args, **kwargs): if name == "A": return A(*args, **kwargs) ...` would suffice. – chepner Dec 01 '17 at 22:43
  • Where 'A' is coming from: `return A.__new__(name, **kwargs)` ? As error explains, 'A' is not an object. – Elis Byberi Dec 01 '17 at 22:51
  • It doesn't look like you actually tried to run any code version you posted. This code wouldn't have produced the error you claim, and it even contains obvious syntactical errors like `return return A(name)`. Posting code that doesn't reproduce your error tends to attract downvotes. – user2357112 Dec 01 '17 at 22:52
  • @ElisByberi ok yes, I guess that's a further evidence proving I am calling __new__ wrong. thanks – stucash Dec 01 '17 at 22:52
  • @stucash Why do you need to call new? It is called whenever you create a new class object! – Elis Byberi Dec 01 '17 at 22:54
  • @user2357112 thanks for that, but I guess that'd be something once you pointed out I could easily rectify. and for not running this codes, I guess it's yes and no, I won't argue this. I'll take this blame. I do have live codes which is far more complex so I could only create same skeleton here. – stucash Dec 01 '17 at 22:54
  • @stucash Test your code before coming here. You are welcome! Come prepared next time! :-) – Elis Byberi Dec 01 '17 at 22:55
  • @ElisByberi it's half learning to be honest; making correct use of `__new__` we could implement the factory pattern in a different way for it looks a bit nicer.. – stucash Dec 01 '17 at 22:56
  • @ElisByberi yep will defo do that, now at least I think the downvote is not wrong. thanks for helping out! – stucash Dec 01 '17 at 22:57
  • @stucash This will help you for next time: https://stackoverflow.com/questions/674304/why-is-init-always-called-after-new – Elis Byberi Dec 01 '17 at 22:58
  • @user2357112 thanks for letting me, I genuinely appreciated it. – stucash Dec 01 '17 at 23:08

1 Answers1

11

You're calling __new__ wrong. If you want your __new__ to create an instance of a subclass, you don't call the subclass's __new__; you call the superclass's __new__ as usual, but pass it the subclass as the first argument:

instance = super().__new__(A)

I can't guarantee that this will be enough to fix your problems, since the code you've posted wouldn't reproduce the error you claim; it has other problems that would have caused a different error first (infinite recursion). Particularly, if A and B don't really descend from Thing, that needs different handling.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • thanks and I have actually fixed circula dependency by using this implementation, but with returning instances directly. calling `__new__` wrong I guess might be my real problem; I am going to try it and update it later.. peculiar that you didn't get the same error. – stucash Dec 01 '17 at 22:51
  • Sincere apologies, I have corrected the codes now. if you run them you should be able to reproduce the error. – stucash Dec 02 '17 at 17:25