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 en la función, o podría pasarle el archivo abierto y esperar que quien lo llame se ocupe de cerrarlo.
La primera forma parece una mejor manera de garantizar que no se dejen archivos abiertos, pero me impide usar cosas como objetos 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 similares a un archivo
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? ¿Generalmente se espera que una función se comporte de una de estas dos maneras? ¿O debería estar bien documentado para que el programador pueda usar la función según corresponda?
fuente
your_function
puede usar un argumento opcional "stream_name" para este respecto.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 por sí solo, no dude en encapsular todo el acceso a archivos dentro de una función.
Esto tiene la muy buena propiedad de finalizar el recurso (cerrar el archivo) al final de la
with
declaración.Sin embargo, si existe la necesidad de procesar un archivo ya abierto, entonces la distinción de su
ver_1
yver_2
tiene más sentido. Por ejemplo:Este tipo de prueba de tipo explícito a menudo está mal visto , especialmente en lenguajes como Java, Julia y Go, donde el despacho basado en tipo o interfaz es directamente compatible. En Python, sin embargo, no hay soporte de idioma para el despacho basado en tipos. Es posible que de vez en cuando vea críticas de las 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 puedan aparecer, también conocido como "escritura de pato". Tenga en cuenta el guión bajo principal 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.Actualización de 2019: Dadas las actualizaciones recientes en Python 3, por ejemplo, que las rutas ahora se almacenan potencialmente como
pathlib.Path
objetos no solostr
obytes
(3.4+), y ese tipo de sugerencia ha pasado de esotérico a mainstream (circa 3.6+, aunque todavía evoluciona activamente), aquí está Código actualizado que tiene en cuenta estos avances:fuente
read
algo que podría ser similar a un archivo, o llamaropen(fileobj, 'r')
y capturar elTypeError
iffileobj
no es una cadena.ver
operación independientemente del tipo. También puede ser posible implementar aver
través de la escritura de pato, como usted dice. Pero generar excepciones para capturar es más lento que la simple inspección de tipo, y la OMI no produce ningún beneficio particular (claridad, generalidad, etc.) En mi experiencia, la tipificación de patos es impresionante "en general", pero neutral a contraproducente "en pequeño ".hasattr(fileobj, 'read')
prueba sería escribir pato; unaisinstance(fileobj, str)
prueba no lo es. Aquí hay un ejemplo de la diferencia: laisinstance
prueba falla con nombres de archivo unicode, yau'adsf.txt'
que no es unstr
. Has probado un tipo demasiado específico. Una prueba de tipeo de pato, ya sea basada en llamadasopen
o algunadoes_this_object_represent_a_filename
función hipotética , no tendría ese problema.is_instance(x, str)
sino más bien algo asíis_instance(x, string_types)
, constring_types
un ajuste adecuado para un funcionamiento adecuado en PY2 y PY3. Dado algo que grazna como una cuerda,ver
reaccionaría adecuadamente; dado algo que grazna como un archivo, lo mismo. Para un usuario dever
, no habría diferencia, excepto que la implementación de inspección de tipo se ejecutaría más rápido. Pura puristas: siéntase libre de estar en desacuerdo.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 conducir a errores de corrección y agujeros de seguridad.
fuente
Se trata de la propiedad y la responsabilidad de cerrar el archivo. Puede pasar en una referencia de flujo o un archivo o lo que sea cosita que debe ser cerrada / dispuestos en un cierto punto a otro método, siempre y cuando se asegure de que quede claro quién es el dueño y seguro que va a ser cerrado por el propietario cuando haya terminado . Esto generalmente implica una construcción try-finally o el patrón desechable.
fuente
Si elige pasar archivos abiertos, puede hacer algo como lo siguiente PERO no tiene acceso al nombre de archivo en la función que escribe en el archivo.
Haría esto si quisiera tener una clase que fuera 100% responsable de las operaciones de archivo / secuencia y otras clases o funciones que serían ingenuas y no se espera que abran o cierren dichos archivos / secuencias.
Recuerde que los gestores de contexto funcionan como si tuviera una cláusula finalmente. Entonces, si se lanza una excepción en la función de escritor, el archivo se cerrará sin importar qué.
fuente
with open
? ¿Cómo aborda esto la cuestión del uso de nombres de archivo frente a objetos similares a archivos?with open
embargo, ese comportamiento ya se maneja , ¿verdad? ¿Y lo que estás defendiendo efectivamente es una función que solo usa objetos similares a archivos y no le importa de dónde provienen?