to be pythonic: should caller or callee log?

Gildor Oronar gildororonar at mail-on.us
Tue Sep 3 12:07:26 EDT 2013


What would you choose? Do you put logging routine into caller or callee? 
My intuitive answer is "callee does the logging, because that's where 
action takes place", like this:

class Account():
     def transaction(self, amount, target):
         logging.info("Start transaction of %s to %s" % (amount, target))
         ...

if '__name__' == '__main__'
     ....
     account.transaction(amount, target)


Simple and easy. Put logging code to the caller would be tedious - the 
function is called about 20 times in different places.

So far so good, but we grew up to have 10 different account classes:

class AbsctractAccount():

class CreditAccount(AbstractAccount):
     def transaction(self, amount, target):
         logging.info("Start transaction of %s to %s" % (amount, target))
         ...

class DebitAccount(AbstractAccount):
     def transaction(self, amount, target):
         logging.info("Start transaction of %s to %s" % (amount, target))
         ...

class SomeOtherAccount(...)
     ....

Then letting the callee do the logging is also tedious now.

What is the best practise here?

If, for the convenience, we define transaction function in 
AbstractAccount to just do the logging, and change inherited classes, 
like this:

class AbsctractAccount():
     def transaction(self, amount, target):
         logging.info("Start transaction of %s to %s" % (amount, target))

class DebitAccount(AbstractAccount):
     def transaction(self, amount, target):
         super().transaction(amount,target)
         ....

Then we gethered logging code pieces all to one place, but it begets two 
other problems.

1. It is a "surprise" that super().transaction must be called first, not 
last, and that it does only the logging.

2. The code used to check whether "transaction" is implemented:

     if hasAttr(DebitAccount, 'transaction'): # clear to read

has to be changed to check whether "transaction" is inherited:

     if not DebitAccount.transaction is AbstractAccount.transaction:

whose purpose is confusing, and whose cause is a little surprise too. 
Also, the pythonic "boldly calling and watching for exception" stopped 
working, because instead of getting AttributeError, we would get a line 
of log and nothing more.




More information about the Python-list mailing list