Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transition from small scripts to bigger apps is not easy

Tags:

python

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()
like image 543
kolobos Avatar asked Oct 25 '22 05:10

kolobos


2 Answers

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 :)

like image 52
pyfunc Avatar answered Dec 08 '22 19:12

pyfunc


For this type of situations, I use the optional parameter.

def addUser(name, full_name, password, _loader=None):
    user = schema.User(name, full_name, password)
    if (_loader is None):
        # Use global values.
        session.add(user)
        session.commit()
    else:
        _session = _loader.session
        # ...

I, myself, stay away from initializing stuffs like this at module loading. Imagine you want to create documentation with tools like epydoc. It makes no sense to create a connection in that context just because epydoc loads the module. I would definitely go with the class approach.

like image 38
Danosaure Avatar answered Dec 08 '22 17:12

Danosaure