Los objetos similares a archivos son objetos en Python que se comportan como un archivo real, por ejemplo, tienen un método de lectura () y un método de escritura (), pero tienen una implementación diferente. Es una realización del concepto Duck Typing .
Se considera una buena práctica permitir un objeto similar a un archivo en todas partes donde se espera un archivo de modo que, por ejemplo, un objeto StringIO o Socket pueda usarse en lugar de un archivo real. Entonces es malo realizar una verificación como esta:
if not isinstance(fp, file):
raise something
¿Cuál es la mejor manera de comprobar si un objeto (por ejemplo, un parámetro de un método) es "similar a un archivo"?
why
¿Qué pasa con los operadores como__add__
,__lshift__
o__or__
en clases personalizadas? (objeto de archivo y API: docs.python.org/glossary.html#term-file-object )Para 3.1+, uno de los siguientes:
Para 2.x, "objeto similar a un archivo" es una cosa demasiado vaga para verificar, pero la documentación de cualquier función con la que esté tratando le dirá lo que realmente necesita; si no, lea el código.
Como señalan otras respuestas, lo primero que debe preguntar es qué es exactamente lo que está buscando. Por lo general, EAFP es suficiente y más idiomático.
El glosario dice que "objeto de archivo" es sinónimo de "objeto de archivo", lo que en última instancia significa que es una instancia de una de las tres clases base abstractas definidas en el
io
módulo , que son todas subclases deIOBase
. Entonces, la forma de verificar es exactamente como se muestra arriba.(Sin embargo, la verificación
IOBase
no es muy útil. ¿Se imagina un caso en el que necesite distinguir un archivo real similarread(size)
a una función de un solo argumentoread
que no sea similar a un archivo, sin necesidad de distinguir también entre archivos de texto y archivos sin formato? ¿archivos binarios? Entonces, realmente, casi siempre desea verificar, por ejemplo, "es un objeto de archivo de texto", no "es un objeto similar a un archivo".)Para 2.x, aunque el
io
módulo ha existido desde 2.6+, los objetos de archivo integrados no son instancias deio
clases, tampoco lo son ninguno de los objetos similares a archivos en stdlib, ni tampoco la mayoría de los objetos similares a archivos de terceros que usted es probable que encuentre. No había una definición oficial de lo que significa "objeto similar a un archivo"; es simplemente "algo así como un objeto de archivo incorporado ", y diferentes funciones significan cosas diferentes por "me gusta". Tales funciones deben documentar lo que significan; si no es así, tienes que mirar el código.Sin embargo, los significados más comunes son "tiene
read(size)
", "tieneread()
" o "es un iterable de cadenas", pero algunas bibliotecas antiguas pueden esperar enreadline
lugar de una de esas, algunas bibliotecas prefieren losclose()
archivos que les proporcionas, algunos esperarán que sifileno
está presente, entonces hay otra funcionalidad disponible, etc. Y de manera similar parawrite(buf)
(aunque hay muchas menos opciones en esa dirección).fuente
IOBase
. Por ejemplo, los accesorios de pytest le dan lo_pytest.capture.EncodedFile
que no hereda de nada.Como han dicho otros, generalmente debe evitar tales controles. Una excepción es cuando el objeto puede ser legítimamente de diferentes tipos y desea un comportamiento diferente según el tipo. El método EAFP no siempre funciona aquí, ya que un objeto podría parecerse a más de un tipo de pato.
Por ejemplo, un iniciador podría tomar un archivo, una cadena o una instancia de su propia clase. Entonces podría tener un código como:
El uso de EAFP aquí podría causar todo tipo de problemas sutiles, ya que cada ruta de inicialización se ejecuta parcialmente antes de lanzar una excepción. Esencialmente, esta construcción imita la sobrecarga de funciones y, por lo tanto, no es muy Pythonic, pero puede ser útil si se usa con cuidado.
Como nota al margen, no puede hacer la verificación de archivos de la misma manera en Python 3. Necesitará algo como
isinstance(f, io.IOBase)
.fuente
El paradigma dominante aquí es EAFP: más fácil pedir perdón que permiso. Continúe y use la interfaz de archivo, luego maneje la excepción resultante o deje que se propaguen a la persona que llama.
fuente
x
no es similar a un archivo,x.read()
generará su propia excepción. ¿Por qué escribir una declaración if adicional? Solo usa el objeto. Funcionará o se romperá.A menudo es útil generar un error al verificar una condición, cuando ese error normalmente no se generaría hasta mucho más tarde. Esto es especialmente cierto para el límite entre el código 'user-land' y 'api'.
No colocaría un detector de metales en una estación de policía en la puerta de salida, ¡lo colocaría en la entrada! Si no marcar una condición significa que podría ocurrir un error que podría haberse detectado 100 líneas antes, o en una superclase en lugar de aparecer en la subclase, entonces digo que no hay nada de malo en verificar.
También tiene sentido comprobar los tipos adecuados cuando acepta más de un tipo. Es mejor generar una excepción que diga "Necesito una subclase de cadena base, archivo OR" que simplemente generar una excepción porque alguna variable no tiene un método de 'búsqueda' ...
Esto no significa que te vuelvas loco y hagas esto en todas partes, en su mayor parte estoy de acuerdo con el concepto de excepciones que surgen, pero si puedes dejar tu API drásticamente clara o evitar la ejecución de código innecesario porque no se ha cumplido una condición simple hazlo!
fuente
Puede intentar llamar al método y luego detectar la excepción:
Si solo desea un método de lectura y escritura, puede hacer esto:
Si yo fuera usted, optaría por el método try / except.
fuente
try
es siempre la primera opción. Loshasattr
cheques son solo, por alguna razón realmente oscura, que no puede simplemente usartry
.fp.read(0)
lugar defp.read()
para evitar poner todo el código en eltry
bloque si desea procesar los datosfp
posteriormente.fp.read()
con archivos grandes aumentará inmediatamente el uso de memoria.Flask
hice esto y me di cuenta de que elFileStorage
objeto subyacente necesitaba el reinicio del puntero después de leerlo.En la mayoría de las circunstancias, la mejor manera de manejar esto es no hacerlo. Si un método toma un objeto similar a un archivo, y resulta que el objeto que se le pasa no lo es, la excepción que se genera cuando el método intenta usar el objeto no es menos informativa que cualquier excepción que haya generado explícitamente.
Sin embargo, hay al menos un caso en el que es posible que desee hacer este tipo de verificación, y es cuando el objeto no está siendo utilizado inmediatamente por aquello a lo que se lo ha pasado, por ejemplo, si está configurado en el constructor de una clase. En ese caso, pensaría que el principio de EAFP es superado por el principio de "falla rápido". Verificaría el objeto para asegurarme de que implementó los métodos que mi clase necesita (y que son métodos), por ejemplo:
fuente
getattr(file, 'read')
lugar de solofile.read
? Esto hace exactamente lo mismo.file
instancia real . (Los métodos de instancias de tipos de extensión C / incorporados son de tipobuiltin_function_or_method
, mientras que los de clases de estilo antiguo lo soninstancemethod
). El hecho de que esta sea una clase de estilo antiguo, y que use==
tipos en lugar deininstance
oissubclass
, son problemas adicionales, pero si la idea básica no funciona, eso no importa.Terminé encontrándome con tu pregunta cuando estaba escribiendo una
open
función similar a la que podría aceptar un nombre de archivo, un descriptor de archivo o un objeto similar a un archivo pre-abierto.En lugar de probar un
read
método, como sugieren las otras respuestas, terminé verificando si el objeto se puede abrir. Si puede, es una cadena o descriptor, y tengo un objeto similar a un archivo válido en la mano del resultado. Siopen
genera unTypeError
, entonces el objeto ya es un archivo.fuente