Hi,

I built a Polygon() class that originally only was initialized with veriticies:

Polygon(verts=(1,2,3,4)

But as the code evolved, I added a few @classmethods to construct the Polygon from other inputs. For example:

Polygon.from_center(center=(1,2), length=5)

Now my problem is that I'm trying to find the canonical way to choose which method my polygon should be built with. I don't want users to have to use both constructors; rather, I want polygon to inspect its own keyword args and realize "hey, there's a center keyword, I'll construct from_center()" or "hey, verts were passed, I'll construct from verts" etc...

I thought that new would be the way to go; that is, new would do the inspection, and call the appropriate object. I'm not sure if this is correct, but I built a minimal example, but keep getting recursion erros. I can follow why, but don't know how to correct them. Any help would be tremendously appreciated.

class Foo(object):

    def __init__(self, *args, **kwargs):    
        print 'initialized' % (args, kwargs)

    def __new__(cls, *args, **kwargs):
        print 'in new, deciding how to inialize'
        if 'a' in kwargs:
            return Foo.from_a(cls, *args, **kwargs)
        else:
            return Foo.not_from_a(cls, *args, **kwargs)      

    @classmethod
    def from_a(cls, *args, **kwargs):
        print 'initializing "from_a()"'
        return Foo(*args, **kwargs)


    @classmethod
    def not_from_a(cls, *args, **kwargs):
        print 'initializing "not_from_a()"'
        return Foo(*args, **kwargs)        


if __name__ == '__main__':
    Foo(a=2)    

Edited 2 Years Ago by dashing.adamhughes

One thing is clear: the __new__() method is not the place where this should be implemented. The role of the __new__() method is to create an instance before this instance is initialized. Normally, you don't need to use it unless you subclass a builtin type or you are playing with metaclasses.

As a simple solution, I suggest to turn your methods into instance methods and add a method keyword to the constructor. Client code could use

p = Polygon(method = "from-center", center = (0, 0), length = 3)

then

class Foo(object):
    def __init__(self, *args, **kwargs):
        method = self._guess_method(args, kwargs)
        if method == 'from-a':
            self.init_from_a(*args, **kwargs)
        elif method == 'default':
            self.init_default(*args, **kwargs)
        else:
            raise ValueError(('Unknown method value for Foo', method))

    def _guess_method(self, args, kwargs):
        try:
            return kwargs['method']
        except KeyError:
            pass
        if 'a' in kwargs:
            return 'from-a'
        else:
            return 'default'

    def init_from_a(self, *args, **kwargs):
        print 'initializing "from_a()"'


    def init_default(self, *args, **kwargs):
        print 'Foo default initializer'

if __name__ == '__main__':
    Foo(method = 'from-a', a = 2)
    Foo(a = 3)
    Foo(5, 6, bar = 'baz')

Another nice thing is that you can still define later

from functools import partial
polygon_from_center = partial(Polygon, method = 'from-center')

It seems to me that all this is very flexible and pythonic.

Edited 2 Years Ago by Gribouillis

That's a nice solution, thanks Gribs. Thanks for clariying new as well. I guess I was way off base there.

Do you think a class decorator might be a suitable way to do this?

I don't know what you mean exactly. This would make sense in order to add automatically some code to a class with multiple choices of initializer. Unfortunately, most of the code is specific to each class: the contents of the various init_ methods, their arguments and also the algorithm to guess which method to choose. So I'm not sure wether it is worth building a formal mechanism for this pattern.

Edited 2 Years Ago by Gribouillis

Hey,

I opted to just add a new class method "figure_it_out" which I'll call in the place of Foo() directly. I think this is the best solution suited to my needs since it doesn't overwrite init() in any way.

class Foo(object):

    def __init__(self, *args, **kwargs):    
        print 'initialized %s %s' % (args, kwargs)


    @classmethod
    def from_a(cls, *args, **kwargs):
        print 'initializing "from_a()"'
        return Foo(*args, **kwargs)


    @classmethod
    def from_b(cls, *args, **kwargs):
        print 'initializing "from_b()"'
        return Foo(*args, **kwargs)        

    @classmethod
    def figure_it_out(cls, *args, **kwargs):
        if 'a' in kwargs:
            return cls.from_a(*args, **kwargs)
        elif 'b' in kwargs:
            return cls.from_b(*args, **kwargs)
        else:
            print 'Initializing with __init__'
            return cls(*args, **kwargs)


if __name__ == '__main__':
    Foo.figure_it_out(a=2)
    Foo.figure_it_out(b=2)
    Foo.figure_it_out()

Edited 2 Years Ago by dashing.adamhughes

PS, wanted to point out that I think the method gribs supplied is more suitable in general. I didn't want to change anything in the init of my library, so I opted for this solution, at the cost that the functions are instantiated through the figure_it_out method. I am hiding this behavior from users in the public API, but in general, probably nto the best practice.

You can also rename the class _Foo and expose an alias

class _Foo(object):
    ...

Foo = _Foo.figure_it_out

In client code, you get Foo(a=2) etc, and you are still free to change the implementation later.

Edited 2 Years Ago by Gribouillis

This question has already been answered. Start a new discussion instead.