On 02Jul2017 11:02, Andrew Z <form...@gmail.com> wrote:
I'd appreciate your suggestions for a better approach to the following task.

I have 2 files ( 2 classes). One (ClassA) has all logic related to the main 
workflow of the program. Another (DB), I like to offload all operations with a 
DB ( sql3 in this case).

I'm trying to pass the connection to the main class, but having problems. One 
of them, is i can't pass the conn as a parameter to the function in one 
(ClassA.abc()), because i inherit it ( function abc() ).
I created a self.DBConnection field, but i'm not sure if i'm on the right 
path...
Code is gutted to highlight the problem.

Unfortunately you have gutted the "writeTicks" method, making it harder to see your intent.

You separation is ok, but normally one would try to entire conceal the unerlying db connection (the sqlite3 connection) from the man class. So you wouldn't "pass the connection to the main class".

Normally I would have your DB class represent an open (or openable, if you wanted to defer that) database connection. So your main class would go:

 def __init__(self, ...other args...):
   self.db = DB(location="blah.sqlite")

 def abc(self, reqId: int):
   self.db.writeTicks(reqId)

I wouldn't be passing in "self" (your ClassA instance) or self.DBconnection at all. You'd only pass "self" if the "DB" instance needed more information from ClassA; normally you'd just pass that information to writeTicks() along with reqId, so that the DB needs no special knowledge about ClassA.

I've also got a bunch of fine grained remarks about your code that you can take or leave as you see fit:

one.py:
from .DB import *

Try to avoid importing "*". It sucks all the names from "DB" into your own namespace. Arguably you only need the DB class itself - all the other functionality comes with it as methods on the class. So:

 from DB import DB

class ClassA(OtherObject):
        def __init__(self):
                self.DBConnection = sql3.Connection

It isn't obvious why you need this. In my example above I'd just make a DB instance and save it as self.db; unless you're controlling different backends that would be all you need.

        def abc(self, reqId: int):
                DB.writeTicks(self,self.DBConnection,reqId))

Here you're calling the writeTicks method on the DB class itself. I wouldn't be making that a class method; I'd make it an instance method on a DB instance, so:

 self.db.writeTicks(reqId)

unless there's more to writeTicks (which you've left out).

DB.py:

Try not to name modules that same as their classes - it leads to confusion. I'd call it "db.py" and make the earlier import:

 from db import DB

import sqlite3 as sql3

This feels like an pointless abbreviation.

[...]
class DB(object):
        db_location = ''
        # db_location = '../DB/pairs.db'

db_location appears to be a class default. These are normally treats as one would a "constant" in other languages. Stylisticly, this normally means you'd write the name in upper case, eg:

   DEFAULT_DB_LOCATION = '../DB/pairs.db'

        def __init__(self, location='../DB/pairs.db'):
                db_location = location

And using that would normally look like this:

   def __init__(self, location=None):
       if location is None:
           location = self.DEFAULT_DB_LOCATION

                print(current_fn_name(),' self.db_location = 
{}'.format(db_location))
                try:
                        with open(db_location) as file:
                                pass
                except IOError as e:
                        print("Unable to locate the Db @ 
{}".format(db_location))

I'd just os.path.exists(db_location) myself, or outright make the db connection immediately.

Also, and this actually is important, error messages should got the the program's standard error output (or to some logging system). So your print would look like:

   print("Unable to locate the Db @ {}".format(db_location), file=sys.stderr)

Also, normal Python practie here would not be to issue an error message, but to raise an exception. That way the caller gets to see the problem, and also the caller cannot accidentally start other work in the false belief that the DB instance has been made successfully. So better would be:

   raise ValueError("Unable to locate the Db @ {}".format(db_location))

        def reqConnection(self):
                try:
                        con = sql3.connect(self.db_location)
                        con.text_factory = str
                except sql3.Error as e:
                        print("Error %s:".format( e.args[0]))
                        sys.exit(1)

It is generally bad for a class method (or, usually, any funtion) to abort the program. Raise an exception; that way (a) the caller gets to see the actual cause of the problem and (b) the caller can decide to abort or try to recover and (c) if the caller does nothing the program will abort on its own, doing this for free.

Effectively you have embedded "polciy" inside your reqConnection method, generally unwelcome - it removes the ability for the caller to implement their own policy. And that is an architectural thing (where the policy lies).

                return con

The easy way to raise the exception here is just to not try/except at all, thus:

 def reqConnection(self):
     return sql3.connect(self.db_location)

or if you really need that text_factory:

 def reqConnection(self):
     con = sql3.connect(self.db_location)
     con.text_factory = str
     return con

        def write(self, con : sql3.Connection, tickId: int):
                        con.execute( blah)

However I'd make the connection a singleton attribute of the DB class. So I'd usually have __init__ make the connection immediately (which saves you having to "probe" the location:

 def __init__(self, ...):
   ...
   self.con = sql3.connect(self.db_location)

and then write() would go:

 def write(self, tickId: int):
   self.con.execute(blah)

and as you can see that _removes_ any need to pass the connection back to the caller - you don't need to expose an reqConnection method at all, or manage it in the caller. Instead, ClassA can just store the DB instance itself, and let DB look after all the specifics. That is exactly the kind of thing class encapsulation is meant to achieve: the caller (Class A) can wash its hands of the mechanisms, which are not its problem.

Cheers,
Cameron Simpson <c...@zip.com.au>
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to