2

I have a script which different people may use. I have print statements so people can follow along what the script is doing, and if it breaks where it went wrong, and if a certain step takes a long time which step it was.

Which way for print statements is better in this case and why?

This:

def do_something():
    ...

print 'doing something'
do_something()

Or this:

def do_something():
    print 'doing something'
    ...

do_something()

I am inclined to think the first is better as it is slightly clearer what is going on when people look at your script; they know there is a print statement before the function is called, and you can add extra detail when sometimes the function name isn't as informative as it could be.

However I see how it could be considered duplication of code. And so the second is better.

But more important than this quite specific question is what general principle would lead you to prefer one over the other?

gnat
  • 21,442
  • 29
  • 112
  • 288
Will Beauchamp
  • 209
  • 1
  • 2
  • 7
  • [Sharing your research helps everyone](http://meta.programmers.stackexchange.com/questions/6559/why-is-research-important). Tell us what you've tried and why it didn’t meet your needs. This demonstrates that you’ve taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer. Also see [ask] – gnat Nov 07 '14 at 16:16
  • 4
    What are you trying to accomplish with those print statements? You may be asking the wrong question. –  Nov 07 '14 at 16:25
  • I have a script which different people may use. I have print statements so people can follow along what the script is doing, and if it breaks where it went wrong, and if a certain step takes a long time which step it was. – Will Beauchamp Nov 07 '14 at 16:31
  • 2
    Is that necessary to build in? Who will be using it? If it breaks the traceback will tell you where, and if you want to find bottlenecks you should profile it. – jonrsharpe Nov 07 '14 at 16:52
  • one may argue that conceptually, this has been addressed in [How would you know if you've written readable and easily maintainable code?](http://programmers.stackexchange.com/a/141010/31260) If your peers keep complaining about your way of doing things, be it one way or another, you better change to make them feel better – gnat Nov 07 '14 at 16:59
  • @WillBeauchamp just as I suspected, you were trying to implement _trace logging_. MainMa's answer describes the correct way to do this. –  Nov 07 '14 at 17:47

2 Answers2

7

Note: don't use print for logging purposes. Using print may work for small applications, but when the application starts increasing, moving to the actual logging would quickly become unavoidable. To avoid future maintenance cost, consider using logging from the beginning. Make sure you use existent predefined logging frameworks and don't reinvent the wheel.


Is the print statement part of the function? In other words, does it make sense to run the function without the print statement?

If the specific statement is inherent to the function, put it inside. If the specific statement is inherent to the context of the caller, put it there.

Example:

def add_product(product):
    logger.debug("The product %s is about to be added to the database.", product.id)
    ...

Here, the logging is inherent to the function: it doesn't matter when and where the function was called, so whenever it is, the log entry should be added.

def add_product(product):
    ...

logger.debug("Adding the product %s created by the administrator.", product.id)
add_product(self.newProduct)

Here, on the other hand, the context is important: the log message indicates why the product is being added to the database—it is added because the administrator just created it. Thus, the message makes no sense within the function.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
  • I don't agree with your answer. It is certainly better than the OPs approach but it still is conflicting with the SRP. The function `add_product` should do what it says: add a product. Not add a product and log something. Even worse: where does the `log` function come from? Is it global? This makes the function behave on global state and therefore harder to test and harder to reason about. If no AOP is possible, I would suggest to at least remove the logging from `add_product` and make a `add_product_and_log(product, logger)` that adds the product *and* logs the action. – valenterry Nov 08 '14 at 00:01
  • 1
    I don't think names such as `add_product_and_log` are a sustainable solution. In large applications, nearly every function logs something (most at verbose level); you wouldn't suffix every name by `_and_log`, would you? – Arseni Mourzenko Nov 08 '14 at 10:40
  • I agree, `log` function shouldn't be global. I did it this way for the sake of simplicity, but indeed, it is important to note that one should use a logging framework instead of inventing his own logging functions. – Arseni Mourzenko Nov 08 '14 at 10:40
  • using `*_and_log` functions is only a workaround for the underlying problem that no AOP can be used. If, however, the logging is part of the functional features of the program, then it should be named like it (or beeing encapsulated in a decorator). – valenterry Nov 10 '14 at 11:48
2

There is better ways to enable your users to follow the scripts actions. One way is to return an object that describes what steps happend, what the result is and how long the steps took. If it breaks, you should throw an exception which includes additional information.

Another way to do this is using AOP features. These are however not builtin python and you have little IDE support when using reflection/introspection to achieve the desired AOP behaviour. The advantage is, that your logging code and the functional code is seperated and the logging can be enabled and disabled or even extended without having to touch the functional code.

If you want to use neither of these solutions, at least make your users to be able to inject a service that is used for printing, so they can decide what the service should do with your messages. It is really bad if you are using a third-party script and the script just prints to stdout or stderr. You then have to do dirty piping stuff to prevent unwanted output.

valenterry
  • 2,429
  • 16
  • 21