Back

Explore Courses Blog Tutorials Interview Questions
0 votes
4 views
in Python by (19.9k points)

I am thinking about this for some time now with several examples and I truly think it would be handy to directly reference a method-object from inside the method code or more generally speaking: to reference a namespace from inside. Let's take a look at my current code:

def do_stuff(args):

    logtarget, someargs = process_input(args)

    processed_data = do_other_stuff(someargs, outputdir)

    log("Template string".format(processed_data), outputdir=outputdir,  filename="resultfile.txt")

def do_other_stuff(someargs, logtarget):

    step = process_data(someargs)

    if requires_manual_postproceccing(step):

        log(step, outputdir=outputdir, filename="handle manually.txt")

    return get_result(step)

def log(message, outputdir="default", filename="default.txt"):

    with open("{}/{}".format(outputdir, filename), "a") as file:

        file.write(message)

As you can see, I am passing the outputdir around quite a bit and I didn't even mention that do_stuff and do_other_stuff might log things too (but since I don't need these logs as long as the script runs normally, I don't really care about it's location). Wouldn't it be nice if one could set the parameter once and then access it in the log message? The target is processed by the script so I can't hardcode it.

Since the outputdir is processed within a function (namespace), we can't access it from other functions (namespaces) unless we use the glogal keyword, whitch - after all I read about it - I hoped to never have to use. My gutts tell me to handle this problem like this:

def do_stuff(args):

    outputdir, someargs = process_input(args)

    log.outputdir = outputdir

    processed_data = do_other_stuff(someargs)

    log(processed_data.format("somehow"), filename="resultfile.txt")

def do_other_stuff(someargs):

    step = process_data(someargs)

    if requires_manual_posproceccing(step):

        log(step, filename="handle manually.txt")

    return get_result(step)

def log(message, filename="default.txt"):

    with open("{}/{}".format(this.outputdir, filename), "a") as file:

        file.write(message)

It's easier to read, uses less arguments and does not invite strange behaviour and bugs - but it obviousely does not work. Now I know I could (should?) use a fancy logging library or at least write a logger-class so I could use logger = Logger(outputdir) and than use self inside the methods. But keeping it slim and flat is the Python way of coding and until now I could easily follow the rule "Whenever you see a class with one method other than __init__, it should just be a function." Also, even when I used that logger class or logging library, I'd still need to pass an instance of that around.

Is the best way to handle this really to use the method's name instead of this and risk problems when someone decorates my functions or is there a way to directly reference the current namespace and its attributes? Either I don't know the right words to search for this or it's too low level to be documented or it does not exist.

[Edit:] Actually, what I really wated to do (but didn't know I wanted it) is to decorate (oop-cecorate as opposed to Python's pie-decorate) the log function. But still I would have run into the same problem since log = decorate(log) would only affect the current namespace. dir returns the current namespace not the global one so as long as one doesn't want to use global, there really is no other way but to create and pass around a callable object (callbackfunction). Right now there are two answers and I couldn't easily decide which one is clearer. The callable class suggested by Martin Bonner works with standard Python and creates only one callable instead of two. On the other hand, the solution I went with is a get_log-function that returns a function since that is even clearer, avoids creating a class with only one method other than __init__ and needs less code:

def get_log(outerdir):

    def log(message, filename="default.txt"):

        with open("{}/{}".format(outputdir, filename), "a") as file:

            file.write(message)

    return log

If we make that more generic by creating a pie-decorator that can be added to any function, we would basicly just recreate the partial function suggested by heemayl so there we go. The most Pythonic approach not invented here and using the least code.

1 Answer

0 votes
by (25.1k points)

If I understand correctly, you're looking for a way to make outputdir static for all invocations of log callable. If so, you can leverage a functools.partial:

from functools import partial

log_with_outputdir = partial(log, outputdir='/path/to/outputdir')

Now you can call log_with_outputdir just like log without the argument outputdir as it's been already applied.

Browse Categories

...