tags:

views:

85

answers:

2

I believe that readability and the KISS principle are the most important things in programming. That's why I use Python :)
And here is exact situation, which I encounter very often:

Say, I have a nice and clean script, which is a wrapper for database handling:

import database_schema as schema
loader = schema.Loader("sqlite:///var/database.db")
session = loader.session

def addUser(name, full_name, password):
    user = schema.User(name, full_name, password)
    session.add(user)
    session.commit()

def listUsers():
    all_users = session.query(schema.User).all()
    return all_users

Which is used like this:

import database
database.addUser("mike", "Mike Driscoll", "password")
database.listUsers()

At some point, I want to rewrite that module, so that it can work with databases on different path (for unit testing, for instance).

So, what are my options?

  1. The most intuitive thing is to add database_path == "" variable, and then... what? Setting it with setPath(new_path) function, and then adding exception (if database_path == "": raise SomeException) to every single function, is just ugly and shouldn't be done by anyone.

  2. Full featured class, with setting the self._database_path at initialization time.

Which is then used this way:

from database import Database
database = Database("sqlite:///var/database.db")
database.addUser("mike", "Mike Driscoll", "password")
database.listUsers()

This is already more lines of code than in the first example, and addition of the naming problem: having a class called Database in the module database is kind of dumb, no?

Sorry for the long read, here my final questions:

  1. Why there's no such thing as __init__ functions for modules, in Python?
  2. Am I missing something (static class variables, etc), that can do what I want (a constant set at the import time) the easy and clean way, that is still not far away from a module with a bunch of simple functions that was at first?

P.S. sorry for my English.

Edit:

So, to make this clear, how this code may look like in my imaginative Python universe:

import database_schema as schema

def __init__(database_path):
    loader = schema.Loader(database_path)
    global session
    session = loader.session

def addUser(name, full_name, password):
    user = schema.User(name, full_name, password)
    session.add(user)
    session.commit()

def listUsers():
    all_users = session.query(schema.User).all()
    return all_users

And used like this:

import database("sqlite:///var/database.db")

database.addUser("mike", "Mike Driscoll", "password")
database.listUsers()
+2  A: 

A module is a Python object with arbitrarily named attributes that you can bind and reference. The Python code for a module named mod normally resides in a file named mod.py. When you try to import it, a new namespace is created that contains all the attributes of that module.

Though all said, it is not the same as class and creation of object instances of that class. These are different abstractions and should be used as such.

instead of testing for

if database_path == "":
    ....

do it pythonic way

if database_path:
   ....

And rather than raising exception, you could use assert

assert database_path != "", 'database path empty'

A module does not exist in flavors like object instances of class does. Importing a module will create a namespace with the same set of attributes every time you import it. In such situation, init may not make much sense.

There is nothing wrong with the second form of code you have provided. and you if you do not want to do that them some of the above idioms may ease your pain :)

pyfunc
Thanks for the tips, not enough reputation to upvote though. Still don't quite understand why it's impossible to have a function which is ran right after module is imported (see update to the original post). And still believe that it's bad to have to remember to do that manually (call setPath), as well as to put something as repetitive as "assertion database_path, 'database path empty'" into every single function that uses that variable.
kolobos
@kolobos I don't have an interpreter in front of me right now, but is not the code in the module _executed_ when the module is imported? I.e., making your `__init__` routine's inner statements global would work.
mlvljr
@mlvljr You are right, everything on "global level" is executed when the module is imported. Question here is how to tell database path to the module, and how to do it the most "proper" and elegant way. As you can see in my answer, it seems that upgrading to classes is only way to go.
kolobos
You cannot (as far as I know) have parameterized modules, yes (this is not Newspeak!).
mlvljr
@mlvljr Yep. The problem is, "parameterized modules" feel like the most intuitive thing to do when you transition from simple script, and I myself tried to simulate them several times. But they become more and more ugly as project evolves. So, maybe we need more support for classes and OOP in general (special file format for one-class modules, etc.) in Python?
kolobos
Seems, truly modular modules are not there yet (hehe :(( ). As for inserting them into the language, that should definetly be another (SO) question..
mlvljr
A: 

Ok, it seems that what I really missed, is that modules are imported only once, which means that a module as in example code couldn't open 2 different databases at the same time.

That also makes it quite useless in terms of future (re)use: it's unintuitive and prone to bugs to, for instance, run tests on one database then switch to main one, because it will depend on when exactly database_path is set/changed.

I still believe that it's ugly to have "initialization functions" in modules, as well as assertions for variables that should be previously defined.

That said, OOP is still looks as only "proper" way for bigger application structures, especially when you expect that you will need instances in the future.

kolobos