简体   繁体   中英

In a loop, optionally write output to files

I wrote a function which iteratively computes some quantities X,Y , returning the final result. Additionally, this code saves each iteration of X,Y to a file. Here is the basic structure:

def myFunc():
    X,Y = 0,0
    file1 = open(output1,"w")
    file2 = open(output2,"w")
    for i in range(1000):
        X,Y = someCalculation(X,Y) #calculations happen here
        file1.write(X)
        file2.write(Y)
    file1.close()
    file2.close()
    return X,Y

However, if the filename output1 or output2 is omitted when the function is called, I need this function to perform the same calculation without appending anything to the relevant file .

Here is my messy solution:

def myFunc(output1=None,output2=None):
    X,Y = 0,0
    if (output1 != None): file1 = open(output1,"w")
    if (output2 != None): file2 = open(output2,"w")
    for i in range(1000):
        X,Y = someCalculation(X,Y) #calculations happen here
        if (output1 != None): file1.write(X)
        if (output2 != None): file2.write(Y)
    if (output1 != None): file1.close()
    if (output2 != None): file2.close()
    return X,Y

Is there a better, cleaner way to write this?

Make a dummy file object that ignores writes, and supports the context manager interface:

class NoFile:
    def __enter__(self): return self
    # Propagate any exceptions that were raised, explicitly.
    def __exit__(self, exc_type, exc_value, exc_tb): return False
    # Ignore the .write method when it is called.
    def write(self, data): pass
    # We can extend this with other dummy behaviours, e.g.
    # returning an empty string if there is an attempt to read.

Make a helper function that creates one of these instead of a normal file when the filename is None :

def my_open(filename, *args, **kwargs):
    return NoFile() if filename is None else open(filename, *args, **kwargs)

Use with blocks to manage the file lifetimes, as you should do anyway - but now use my_open instead of open :

def myFunc(output1=None,output2=None):
    X, Y = 0, 0
    with my_open(output1, 'w') as f1, my_open(output2, 'w') as f2:
        for i in range(1000):
            X, Y = someCalculation(X, Y) #calculations happen here
            f1.write(X)
            f2.write(Y)
    return X, Y

You can create a dummy class that has a do-nothing write method. ExitStack is used to ensure any opened files are closed automatically.

from contextlib import ExitStack


class NoWrite:
    def write(self, value):
        pass


def myFunc(output1=None, output2=None):
    X,Y = 0,0
    with ExitStack() as es:
        file1 = es.enter_context(open(output1, "w")) if output1 is not None else NoWrite()
        file2 = es.enter_context(open(output2, "w")) if output2 is not None else NoWrite()

        for i in range(1000):
            X,Y = someCalculation(X, Y) #calculations happen here
            file1.write(X)
            file2.write(Y)
    return X,Y

As you appear to be logging the X and/or Y values at each step, you may want to look into using the logging module instead, creating a FileHandler for the appropriate logger instead of passing output file names to myFunc itself.

import logging

# configuration of the logger objects is the responsibility
# of the *user* of the function, not the function itself.
def myFunc():
    x_logger = logging.getLogger("myFunc.x_logger")
    y_logger = logging.getLogger("myFunc.y_logger")

    X,Y = 0,0
    for i in range(1000):
        X,Y = someCalculation(X, Y)
        x_logger.info("%s", X)
        y_logger.info("%s", Y)
    return X,Y

Simpler: write to a "null device" file defined by your operating system to have no effect when written to.

The standard library os module defines a string constant devnull which identifies the path to this file: '/dev/null' on Mac and Linux, 'NUL' on Windows (I suppose the latter is not actually a path).

import os
def myFunc(output1=os.devnull,output2=os.devnull):
    # as before, with no special conditional logic

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM