开发者

Python: How can I merge these 2 overloaded constructors into 1 constructor

I have a class called Portfolio & right now I have 2 constructors (although as I just discovered u cant overload functions in python) so I have to merge these 2 constructors.

I am an experienced python programmer but this merging 2 constructors that are pretty contradictory is really doing my head in! :P

The first constructor:

+ Purpose is to create a .db file & initialise class variables

- Initialises class member variables

- Checks whether a file with the path&name db_path exists: if it does we exit coz we dont want to overwrite an existing database so we return None

- We create the database (sql database) & set up the necessary tables

- import portfolio data (date created, etc)

The 2nd constructor:

+ Purpose is to import all stock data from an existing sql database file & initialise variables

- Initialises class member variables

- Checks whether a file with the path&name db_path exists: if it does NOT we exit coz we dont want to overwrite an existing database so we return None

- We call the function to import all stocks from the database

- import portfolio data (date created, etc)

How can I merge these two constructors, ugh, why doesn't python allow overloading

class Portfolio:
""" Portfolio class implementation """

# Class Variables:

# self.db_path
# self.app_parent
# self.tracking_stocks
# self.registered_stocks
# self.total_purchase_exp
# self.ytd_roi
# self.cash_reserve


# Class Functions:

def __init__( self, _app_parent, _db_path, stock_list ):
    """ Constructor: """

    self.db_path            = _db_path
    self.app_parent         = _app_parent
    self.tracking_stocks    = []
    self.registered_stocks  = []
    self.total_purchase_exp = 0
    self.ytd_roi            = 0
    self.cash_reserve       = 500

    if database_exists( self.db_path ):
        return None

    self.create_database()
    self.import_portfolio_data()

    for stock_data in stock_list:

        self.add_stock( stock_data )


def __init__( self, _app_parent, _db_path ):
    """ Constructor: """

    self.db_pat开发者_如何学Ch            = _db_path
    self.app_parent         = _app_parent
    self.tracking_stocks    = []
    self.registered_stocks  = []
    self.total_purchase_exp = 0
    self.ytd_roi            = 0
    self.cash_reserve       = 500

    if not self.database_exists( self.db_path ):
        return None

    self.import_portfolio_data()
    self.import_stocks()


Why does this all have to happen in constructors?

You could construct your object, then call separate methods on it for for importing the stock lists, or creating the database.

Wrapping that up in the Builder pattern may suit your existing design.


def __init__(self, _app_parent, _db_path, stock_list=None):
    self.db_path            = _db_path
    self.app_parent         = _app_parent
    self.tracking_stocks    = []
    self.registered_stocks  = []
    self.total_purchase_exp = 0
    self.ytd_roi            = 0
    self.cash_reserve       = 500

    if stock_list:
        if database_exists( self.db_path ):
            return None
        self.create_database()
        self.import_portfolio_data()
        for stock_data in stock_list:
            self.add_stock( stock_data )
    else:
        if not self.database_exists( self.db_path ):
            return None

        self.import_portfolio_data()
        self.import_stocks()

Most of this is copy-pasted, so I can't attest to the accuracy of the indentation, etc., but this is the general pattern for creating versatile __init__ methods in Python (note that __init__ by itself is not a constructor, it's an initializer). Since Python supports keyword args and default values for keyword args, there's really no reason to ever use overloading (in fact, we save a lot of repetitious code by not having overloading). Instead, we just use a keyword arg stock_list with a default value of None.

This isn't perfectly fixed up, either; some of the problems persist (since it's copy-pasted). I don't know the details of what the constructor does, but I think with a little more refactoring you could get things set up where the __init__ is much shorter and much more elegant (consider separating out pieces of the initialization into methods, like _init_db() and call those from __init__).


I would use class methods to handle the different cases. This is really a variant of the C++ Named Constructor idiom adapted to Python. Instead of calling the constructor directory, you call a class method to create the instance based on your needs. Here's how I would code this one up:

class Portfolio:
    def __init__(self, app_parent, db_path):
        """Create a fully initialized and empty instance."""
        self.db_path = db_path
        self.app_parent = app_parent
        self.tracking_stocks = []
        self.registered_stocks = []
        self.total_purchase_exp = 0
        self.ytd_roi = 0
        self.cash_reserve = 500

    @classmethod
    def new_database(cls, app_parent, db_path, stock_list):
        """Create a Portfolio instance wrapped around a new database and
        initialize it with ``stock_list``.  If a database already exists at
        ``db_path``, then an exception is thrown."""
        instance = cls(app_parent, db_path)
        if database_exists(instance.db_path):
            raise Exception('Database already exists at '+instance.db_path)
        instance.create_database()
        instance.import_portfolio_data()
        for stock_data in stock_list:
            instance.add_stock(stock_data)
        return instance

    @classmethod
    def from_database(cls, app_parent, db_path):
        """Create a Portfolio instance from the database located at
        ``db_path``.  If the database does not exist, then an exception
        is thrown."""
        instance = cls(app_parent, db_path)
        if not database_exists(instance.db_path):
            raise Exception('Database does not exist at '+instance.db_path)
        instance.import_portfolio_data()
        instance.import_stocks()
        return instance

The usage is easy:

new_portfolio = Portfolio.new_database('/data/port.db')
existing_portfolio = Portfolio.from_database('/data/port.db')

There are a few things that are pretty important from a usability and maintainability standpoint though. First, use the class instance argument (cls) to create the new instance in the named constructor instead of the class. Instead of instance = cls(...), I could have used instance = Portfolio(...). This breaks if you add a subclass to Portfolio and call one of the class methods.

The second (and probably more important) point is that you should be using exceptions instead of returning None in the case of errors. It is more pythonic and just safer in general.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜