Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using base class constructor as factory in Python?

Tags:

python

factory

I'm using base class constructor as factory and changing class in this constructor/factory to select appropriate class -- is this approach is good python practice or there are more elegant ways?

I've tried to read help about metaclasses but without big success.

Here example of what I'm doing.

class Project(object):
  "Base class and factory."
  def __init__(self, url):
      if is_url_local(url):
        self.__class__ = ProjectLocal
      else:
        self.__class__ = ProjectRemote
      self.url = url

class ProjectLocal(Project):
  def do_something(self):
    # do the stuff locally in the dir pointed by self.url

class ProjectRemote(Project):
  def do_something(self):
    # do the stuff communicating with remote server pointed by self.url

Having this code I can create the instance of ProjectLocal/ProjectRemote via base class Project:

project = Project('http://example.com')
project.do_something()

I know that alternate way is to using fabric function that will return the class object based on url, then code will looks similar:

def project_factory(url):
      if is_url_local(url):
        return ProjectLocal(url)
      else:
        return ProjectRemote(url)

project = project_factory(url)
project.do_something()

Is my first approach just matter of taste or it has some hidden pitfalls?

like image 392
bialix Avatar asked Feb 13 '09 10:02

bialix


2 Answers

You shouldn't need metaclasses for this. Take a look at the __new__ method. This will allow you to take control of the creation of the object, rather than just the initialisation, and so return an object of your choosing.

class Project(object):
  "Base class and factory."
  def __new__(cls, url):
    if is_url_local(url):
       return super(Project, cls).__new__(ProjectLocal, url) 
    else:
       return super(Project, cls).__new__(ProjectRemote, url) 

  def __init__(self, url):
    self.url = url
like image 91
Brian Avatar answered Sep 21 '22 10:09

Brian


I would stick with the factory function approach. It's very standard python and easy to read and understand. You could make it more generic to handle more options in several ways such as by passing in the discriminator function and a map of results to classes.

If the first example works it's more by luck than by design. What if you wanted to have an __init__ defined in your subclass?

like image 42
scottynomad Avatar answered Sep 20 '22 10:09

scottynomad