¿Debo pasar los nombres de archivo para abrir, o abrir archivos?

45

Supongamos que tengo una función que hace cosas con un archivo de texto, por ejemplo, lee y elimina la palabra 'a'. Podría pasarle un nombre de archivo y manejar la apertura / cierre de la función, o podría pasarle el archivo abierto y esperar que quienquiera que lo llame se ocupe de cerrarlo.

La primera forma parece ser una mejor manera de garantizar que no queden archivos abiertos, pero me impide usar objetos como StringIO

La segunda forma podría ser un poco peligrosa: no hay forma de saber si el archivo se cerrará o no, pero podría usar objetos parecidos a archivos

def ver_1(filename):
    with open(filename, 'r') as f:
        return do_stuff(f)

def ver_2(open_file):
    return do_stuff(open_file)

print ver_1('my_file.txt')

with open('my_file.txt', 'r') as f:
    print ver_2(f)

¿Es uno de estos generalmente preferido? ¿Se espera generalmente que una función se comporte de una de estas dos formas? ¿O debería estar bien documentado para que el programador pueda utilizar la función según corresponda?

    
pregunta Dannnno 10.11.2014 - 20:09

3 respuestas

33

Las interfaces convenientes son agradables y, a veces, el camino a seguir. Sin embargo, la mayoría de las veces, una buena capacidad de es más importante que la conveniencia , ya que una abstracción composable nos permite implementar otras funcionalidades (incluidos los envoltorios de conveniencia) que están encima.

La forma más general para que su función use archivos es tomar un identificador de archivo abierto como parámetro, ya que esto le permite usar también identificadores de archivo que no forman parte del sistema de archivos (por ejemplo, tuberías, enchufes, ...):

def your_function(open_file):
    return do_stuff(open_file)

Si deletrear with open(filename, 'r') as f: result = your_function(f) es mucho pedir a sus usuarios, puede elegir una de las siguientes soluciones:

  • your_function toma un archivo abierto o un nombre de archivo como parámetro. Si es un nombre de archivo, el archivo se abre y se cierra, y se propagan las excepciones. Existe un pequeño problema con la ambigüedad que podría solucionarse mediante el uso de argumentos con nombre.
  • Ofrezca un envoltorio sencillo que se encargue de abrir el archivo, por ejemplo,

    def your_function_filename(file):
        with open(file, 'r') as f:
            return your_function(f)
    

    Por lo general, percibo funciones como el aumento de API, pero si proporcionan una funcionalidad de uso común, la conveniencia obtenida es un argumento suficientemente sólido.

  • Envuelva la funcionalidad with open en otra función composible:

    def with_file(filename, callback):
        with open(filename, 'r') as f:
            return callback(f)
    

    utilizado como with_file(name, your_function) o en casos más complicados with_file(name, lambda f: some_function(1, 2, f, named=4))

respondido por el amon 10.11.2014 - 20:34
17

La verdadera pregunta es de integridad. ¿Es su función de procesamiento de archivos el procesamiento completo del archivo, o es solo una pieza en una cadena de pasos de procesamiento? Si está completo en sí mismo, entonces siéntase libre de encapsular todo el acceso a los archivos dentro de una función.

def ver(filepath):
    with open(filepath, "r") as f:
        # do processing steps on f
        return result

Esto tiene la muy buena propiedad de finalizar el recurso (cerrar el archivo) al final de la declaración with .

Si, sin embargo, es posible que sea necesario procesar un archivo ya abierto, entonces la distinción entre ver_1 y ver_2 tiene más sentido. Por ejemplo:

def _ver_file(f):
    # do processing steps on f
    return result

def ver(fileobj):
    if isinstance(fileobj, str):
        with open(fileobj, 'r') as f:
            return _ver_file(f)
    else:
        return _ver_file(fileobj)

Este tipo de pruebas de tipo explícitas a menudo son mal vistas , especialmente en lenguajes como Java, Julia y Go, donde se admite directamente el envío basado en tipo o interfaz. En Python, sin embargo, no hay soporte de idioma para el envío basado en tipos. Es posible que ocasionalmente veas críticas de pruebas de tipo directas en Python, pero en la práctica es extremadamente común y bastante efectivo. Permite que una función tenga un alto grado de generalidad, manejando cualquier tipo de datos que probablemente se presente, también conocido como "tipificación de pato". Tenga en cuenta el guión bajo en _ver_file ; esa es una forma convencional de designar una función (o método) "privada". Si bien técnicamente se puede llamar directamente, sugiere que la función no está destinada al consumo externo directo.

    
respondido por el Jonathan Eunice 10.11.2014 - 21:04
4

Si pasa el nombre del archivo en lugar del identificador del archivo, no hay garantía de que el segundo archivo sea el mismo que el primero cuando se abre; Esto puede llevar a errores de corrección y agujeros de seguridad.

    
respondido por el Mehrdad 11.11.2014 - 01:47

Lea otras preguntas en las etiquetas