1

In python (3) I want to create a derived class dynamically from several base classes.

Concrete example: In selenium, in order to run GUI based tests, you can initiate a driver from e.g Firefox or Chrome in the following way:

driver = webdriver.Firefox()
driver = webdriver.Chrome()

Now I want to create a derived class to which additional functionalities are added. Something like

class MyDriver(webdriver.Firefox):
    def find_button_and_click_on_it_no_matter_what(self, params):
        ...

But the base class can be either the firefox driver or the chrome driver. I found something related here, but it does not seem to work:

class MyDriver(object):
    def __new__(cls, base_type, *args, **kwargs):
        return super(HBPDriver, cls).__new__(base_type, *args, **kwargs)

    def __init__(self):
        pass

Calling this command

driver = mydriver.MyDriver(webdriver.Firefox())    

gives an error

TypeError: object.__new__(X): X is not a type object (WebDriver)

How to do it right? And how to call __init__ on the derived class...?

I hope it is clear what I want to achieve...

Alex
  • 41,580
  • 88
  • 260
  • 469

1 Answers1

3

By the time you call __new__ it is too late to choose the class to instantiate; __new__ is an attribute of the class. Instead you need a trivial factory function:

def my_driver(cls, *args, **kwargs):
    class NewThing(cls):
        ...
    return NewThing(*args, **kwargs)

ff = my_driver(webdriver.Firefox)

Another option is to define the base class with an expression:

if ...:
  base = webdriver.Firefox
elif ...:
  base = webdriver.Chrome
class MyDriver(base):
    ...

A third option is to skip the class statement and use type directly, although I wouldn't recommend this unless the body of the class statement was going to be empty.

class MyDriver(base):
    pass

is equivalent to MyDriver = type('MyDriver', (base,), {}).

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Tanks for the options - but where/how can I define additional class methods in the first option? – Alex Sep 26 '17 at 14:19
  • The first option, to some extent, is a wrapper around the other two (except it didn't define a subclass, but did instantiate the object). I've updated it to show how it uses the same idea as the second example to define a new class locally in the function. – chepner Sep 26 '17 at 15:13
  • Ah I see what you mean. I would say these are same ways to do it. Anyway, your suggestion seems to work... – Alex Sep 26 '17 at 15:24