Compruebe si el objeto es similar a un archivo en Python


Los objetos tipo archivo son objetos en Python que se comportan como un archivo real, por ejemplo, tienen un método read() y un write (), pero tienen una implementación diferente. Es la 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 para que, por ejemplo, un StringIO o un objeto Socket se pueda usar en lugar de un archivo real. Así que es malo realizar un chequeo como este:

if not isinstance(fp, file):
   raise something

¿Cuál es el mejor ¿cómo comprobar si un objeto (por ejemplo, un parámetro de un método) es "similar a un archivo"?

 75
Author: dmeister, 2009-11-02

8 answers

Generalmente no es una buena práctica tener comprobaciones como esta en su código a menos que tenga requisitos especiales.

En Python la escritura es dinámica, ¿por qué siente la necesidad de verificar si el objeto es como un archivo, en lugar de simplemente usarlo como si fuera un archivo y manejar el error resultante?

Cualquier comprobación que pueda hacer va a suceder en tiempo de ejecución de todos modos, por lo que hacer algo como if not hasattr(fp, 'read') y levantar alguna excepción proporciona poca más utilidad que simplemente llamar a fp.read() y manejar el error de atributo resultante si el método no existe.

 44
Author: Tendayi Mawushe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-10 20:34:07

Como otros han dicho, generalmente debe evitar tales controles. Una excepción es cuando el objeto puede ser legítimamente diferentes tipos y desea un comportamiento diferente dependiendo del tipo. El método EAFP no siempre funciona aquí como un objeto podría parecer más de un tipo de pato!

Por ejemplo, un inicializador podría tomar un archivo, cadena o instancia de su propia clase. Es posible que tenga un código como:

class A(object):
    def __init__(self, f):
        if isinstance(f, A):
            # Just make a copy.
        elif isinstance(f, file):
            # initialise from the file
        else:
            # treat f as a string

El uso de EAFP aquí podría causar todo tipo de problemas sutiles como cada ruta de inicialización se ejecuta parcialmente antes de lanzar una excepción. Esencialmente esta construcción imita la sobrecarga de funciones y así no es muy Python, pero puede ser útil si se utiliza con cuidado.

Como nota al margen, no puede hacer la comprobación de archivos de la misma manera en Python 3. Necesitarás algo como isinstance(f, io.IOBase) en su lugar.

 43
Author: Scott Griffiths,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-03 12:09:12

Para 3.1+, una de las siguientes opciones:

isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)

Para 2.x, "file-like object" es una cosa demasiado vaga para verificar, pero la documentación de cualquier función(s) con la que esté tratando con suerte le dirá lo que realmente necesitan; si no, lea el código.


Como señalan otras respuestas, lo primero que debes preguntar es qué es exactamente lo que estás buscando. Por lo general, EAFP es suficiente, y más idiomático.

El glosario dice que "file-like object" es un sinónimo de "file object", que en última instancia significa que es una instancia de una de las tres clases base abstractas definidas en el módulo io , que son a su vez todas las subclases de IOBase. Por lo tanto, la forma de comprobar es exactamente como se muestra arriba.

(Sin embargo, comprobar IOBase no es muy útil. ¿Puede imaginar un caso en el que necesita distinguir un archivo real como read(size) de alguna función de un argumento llamada read que no es similar a un archivo, sin necesidad de distinguir también entre texto archivos y archivos binarios raw? Por lo tanto, en realidad, casi siempre desea comprobar, por ejemplo, "es un objeto de archivo de texto", no "es un objeto similar a un archivo".)


Para 2.x, mientras que el módulo io ha existido desde 2.6+, los objetos de archivo incorporados no son instancias de clases io, ni tampoco ninguno de los objetos similares a archivos en el stdlib, y tampoco lo son la mayoría de los objetos similares a archivos de terceros que es probable que encuentre. No había una definición oficial de lo que significa "objeto similar a un archivo"; es solo " algo como builtin file object", y diferentes funciones significan diferentes cosas por "like". Tales funciones deben documentar lo que significan; si no lo hacen, tienes que mirar el código.

Sin embargo, los significados más comunes son "has read(size)", "has read()", o "is an iterable of strings", pero algunas bibliotecas antiguas pueden esperar readline en lugar de uno de esos, a algunas bibliotecas les gusta close() los archivos que les das, algunos esperarán que si fileno está presente entonces otra funcionalidad está disponible, etc. Y del mismo modo para write(buf) (aunque hay muchas menos opciones en esa dirección).

 42
Author: abarnert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-17 19:36:16

El paradigma dominante aquí es EAFP: más fácil pedir perdón que permiso. Siga adelante y use la interfaz de archivos, luego maneje la excepción resultante o deje que se propaguen al llamante.

 27
Author: drxzcl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-02 13:25:38

A menudo es útil generar un error comprobando 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 un detector de metales en una estación de policía en la puerta de salida, se coloca en la entrada! Si no comprobar una condición significa que puede ocurrir un error que podría haber sido capturado 100 líneas antes, o en una super-clase en lugar de ser elevado en la subclase entonces Yo digo que no hay nada malo en comprobarlo.

Comprobar los tipos adecuados también tiene sentido cuando se acepta más de un tipo. Es mejor levantar una excepción que diga "Requiero una subclase de basestring, O file" que simplemente levantar una excepción porque alguna variable no tiene un método 'seek'...

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 se elevan a sí mismas, pero si puedes hacer que tu API drásticamente claro, o evitar la ejecución innecesaria de código porque una condición simple no se ha cumplido hacerlo!

 10
Author: Ben DeMott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-29 02:04:21

Puede intentar llamar al método y luego capturar la excepción:

try:
    fp.read()
except AttributeError:
    raise something

Si solo desea un método de lectura y escritura, puede hacer esto:

if not (hasattr(fp, 'read') and hasattr(fp, 'write')):
   raise something

Si yo fuera tú, usaría el método try/except.

 6
Author: Nadia Alramli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-02 14:09:37

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 lo que se lo ha pasado, por ejemplo, si se está estableciendo en el constructor de una clase. En ese caso, creo que el principio del EAFP es superado por el principio de "fallar rápido"."Revisaría el objeto para asegurarme de que implementa los métodos que mi clase necesita (y que son métodos), por ejemplo:

class C():
    def __init__(self, file):
        if type(getattr(file, 'read')) != type(self.__init__):
            raise AttributeError
        self.file = file
 2
Author: Robert Rossney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-03 16:42:42

Terminé encontrándome con tu pregunta cuando estaba escribiendo una función similar a open que podría aceptar un nombre de archivo, descriptor de archivo u objeto tipo archivo pre-abierto.

En lugar de probar un método read, 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 a mano del resultado. Si open genera un TypeError, entonces el objeto ya es un archivo.

 1
Author: Mad Physicist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-28 17:13:04