Silas Ray
Silas Ray

Reputation: 26160

Better way to dynamically create methods

So I need to mutate a method at object initialization based upon input to __init__ (for those interested, I'm changing the navigate_to method in a test framework based upon which type of automator is being instantiated (Selenium, mobile device automator, etc)). I've come up with a solution using a conditionally created closure in __init__, but it seems like there should be a more elegant and optimized way to do this. As an example of the approach:

class Foo(object):
    def __init__(self, x):
        self.x = x
        if x % 2:
            def odd_or_even():
                return '%d odd' % self.x
        else:
            def odd_or_even():
                return '%d even' % self.x
        self.odd_or_even = odd_or_even

Resulting in:

>>> foo1 = Foo(1)
>>> foo2 = Foo(2)
>>> foo1.odd_or_even()
'1 odd'
>>> foo2.odd_or_even()
'2 even'

This works, but I feel like there should be some better way to do this. Suggestions?

Upvotes: 2

Views: 125

Answers (2)

Hugh Bothwell
Hugh Bothwell

Reputation: 56674

I would suggest delegating this - something like

class Automator(object):
    def navigate_to(self, url):
        pass

class SeleniumAutomator(Automator):
    def navigate_to(self, url):
        # do it the Selenium way
        pass

class MobileAutomator(Automator):
    def navigate_to(self, url):
        # do it the mobile-browser way
        pass

class Foo(object):
    def __init__(self, x, automator):
        self.x = x
        self.automator = automator

    def navigate_to(self, url):
        return self.automator.navigate_to(url)

f = Foo(3, SeleniumAutomator())
f.navigate_to('http://www.someplace.org/')

... you could do this just with functions, but I presume there are a bunch of interface-dependent methods, and it seems cleanest to keep them grouped in a class.

Edit: oh - then what you want isn't a Foo, it's an automator factory - something like

def makeAutomator(type, *args, **kwargs):
    return {
        "selenium": SeleniumAutomator,
        "mobile":   MobileAutomator
    }[type](*args, **kwargs)

myauto = makeAutomator("selenium")

Upvotes: 3

Ben
Ben

Reputation: 71535

I would make a different method for each type of automator, and then a generic method that uses the state of self to determine which of the specific methods to call.

Why do you need to create a closure containing the decision, when you can just record the decision in self?

Upvotes: 1

Related Questions