views:

101

answers:

3

Hi,

I have a conceptual Python design dilemma.

Say I have a City class, which represents a city in the database. The City object can be initialized in two ways:

  1. An integer (actually, an ID of an existing city in a database)
  2. A list of properties (name, country, population, ...), which will generate a new city in the database, and retrieve its ID.

This means that the City object will always have an ID - either the initialized ID or a newly-created ID derived from the database.

The classic Java approach would overload the constructor - One constructor would get a single intparameter, and the other would get numerous strongly-typed parameters.

I've failed to find an elegant way to do it in Python:

  • I can create a base class with a single method get_city_id, and derive CityFromID and CityFromNewData from it, but that's a lot of effort to work around this language lacuna.
  • Using class methods seems awkward.
  • Using a constructor with a long list of parameters is also awkward: I'd put both city id and the alternatives, and verify within the method that that only a specific subset have values.

Using **kargs seems very inelegant, because the signature of the constructor does not clearly state the required input parameters, and docstrings just ain't enough:

class City(object):
    def __init__(self, city_id=None, *args, **kargs):
        try:
            if city_id==None:
                self.city_id=city_id
            else:
                self.city_name=kargs['name']
        except:
            error="A city object must be instanciated with a city id or with"+\
            " full city details."
            raise NameError(error)

Is there a Pythonic, elegant solution to constructor overloading?

Adam

+1  A: 

I think that the class (factory) method is the best one because already the method name states explicitly what is done. Two free-standing functions would also be fine:

def load_existing_city(id):
    ...
def create_new_city(name, population, ...):
    ...
Philipp
Basically, I'll initialize an object and then call the "Real constructor". It's possible, but a very ugly replacement for constructor overloading!
Adam Matan
Sometimes I miss overloading, too, but I think that the disadvantages outweigh the advantages in dynamic languages like Python. Python allows you to refer to the function *f* just by typing `f`—something that would be impossible if *f* could be overloaded.
Philipp
+4  A: 

There is a design pattern called Data Access Object that is usually used in your case. According to it you should separate fetching and creation of data objects in two classes City and CityDAO:

class City:

    def __init__(self, name, country):
        self.name = name
        self.country = country 


class CityDAO:

    def fetch(self, id):
        return query(...)

    def insert(self, city):
        query(...)
Vitalii Fedorenko
+1 Loading from the DB and the actual class don't belong on the same object in the first place. After loading the data there are many good ways so create a City instance.
THC4k
+4  A: 

How about:

class City(object):
   def __init__(self, name, description, country, populations):
      self.city_name = name
      # etc.

   @classmethod
   def from_id(cls, city_id):
       # initialise from DB 

Then you can do normal object creation:

 >>> c = City('Hollowberg', '', 'Densin', 3)
 >>> c.id
 1233L

 >>> c2 = City.from_id(1233)

~~~~~~

Also you might want to check out SQLAlchemy (and Elixir) for nicer ways to do these things

EoghanM