Frage Überprüfen Sie, ob das Objekt in Python dateiartig ist


Dateiähnliche Objekte sind Objekte in Python, die sich wie eine echte Datei verhalten, z. habe eine read () - und eine write-Methode (), habe aber eine andere Implementierung. Es ist und Realisierung der Ente tippen Konzept.

Es wird als gute Praxis angesehen, ein dateiähnliches Objekt überall dort zuzulassen, wo eine Datei erwartet wird, so dass z. ein StringIO oder ein Socket-Objekt kann anstelle einer echten Datei verwendet werden. Es ist also schlecht, einen solchen Check durchzuführen:

if not isinstance(fp, file):
   raise something

Was ist der beste Weg zu überprüfen, ob ein Objekt (z. B. ein Parameter einer Methode) "dateiähnlich" ist?


75
2017-11-02 13:13


Ursprung


Antworten:


Es ist im Allgemeinen keine gute Praxis, solche Überprüfungen in Ihrem Code zu haben, es sei denn, Sie haben spezielle Anforderungen.

In Python ist die Typisierung dynamisch, warum müssen Sie überprüfen, ob das Objekt dateiartig ist, anstatt es nur so zu verwenden, als ob es eine Datei wäre und den resultierenden Fehler zu behandeln?

Irgendein Scheck, den Sie tun können, wird zur Laufzeit sowieso passieren, so etwas zu tun if not hasattr(fp, 'read') und das Anheben einer Ausnahme bietet wenig mehr Nutzen als nur das Anrufen fp.read() und Behandeln des resultierenden Attributfehlers, wenn die Methode nicht existiert.


43
2017-11-02 13:29



Wie andere gesagt haben, sollten Sie solche Kontrollen generell vermeiden. Eine Ausnahme ist, wenn das Objekt rechtmäßig unterschiedliche Typen sein kann und Sie je nach Typ ein unterschiedliches Verhalten wünschen. Die EAFP-Methode funktioniert hier nicht immer, da ein Objekt mehr als nur eine Entenart darstellen kann!

Zum Beispiel könnte ein Initialisierer eine Datei, einen String oder eine Instanz einer eigenen Klasse nehmen. Sie könnten dann Code wie haben:

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

Die Verwendung von EAFP könnte hier alle möglichen subtilen Probleme verursachen, da jeder Initialisierungspfad teilweise ausgeführt wird, bevor eine Ausnahme ausgelöst wird. Im Wesentlichen ahmt diese Konstruktion eine Überladung der Funktionen nach und ist daher nicht sehr pythonisch, aber sie kann nützlich sein, wenn sie vorsichtig verwendet wird.

Als Nebenbemerkung können Sie die Dateiprüfung in Python 3 nicht auf die gleiche Weise durchführen. Sie benötigen etwas wie isinstance(f, io.IOBase) stattdessen.


43
2017-11-02 13:52



Für 3.1+ eine der folgenden:

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

Für 2.x ist "dateiähnliches Objekt" zu vage, um nach etwas zu suchen, aber die Dokumentation für welche Funktion (en) Sie beschäftigen, wird Ihnen hoffentlich sagen, was sie wirklich brauchen; Wenn nicht, lesen Sie den Code.


Wie andere Antworten zeigen, ist die erste Frage, was genau Sie suchen. Normalerweise ist EAFP ausreichend und idiomatischer.

Das Glossar sagt "dateiähnliches Objekt" ist ein Synonym für "Dateiobjekt", was letztlich bedeutet, dass es eine Instanz von einem der drei ist abstrakte Basisklassen definiert in das io Modul, die selbst alle Unterklassen von sind IOBase. Der Weg zur Überprüfung ist genau wie oben gezeigt.

(Allerdings überprüft IOBase ist nicht sehr nützlich. Können Sie sich einen Fall vorstellen, in dem Sie eine tatsächliche Datei unterscheiden müssen? read(size) von einigen Ein-Argument-Funktion namens read das ist nicht dateiähnlich, ohne auch zwischen Textdateien und rohen Binärdateien unterscheiden zu müssen? So, wirklich, Sie möchten fast immer überprüfen, z. B. "ist ein Textdateiobjekt", nicht "ist ein dateiähnliches Objekt".


Für 2.x, während der io Modul existiert seit 2.6+, eingebaute Dateiobjekte sind keine Instanzen von io Klassen sind keines der dateiähnlichen Objekte in der stdlib, und auch die meisten dateiähnlichen Objekte von Drittanbietern, denen Sie wahrscheinlich begegnen werden. Es gab keine offizielle Definition dessen, was "dateiähnliches Objekt" bedeutet; es ist einfach "so etwas wie ein eingebauter Dateiobjekt"und verschiedene Funktionen bedeuten verschiedene Dinge mit" wie ". Solche Funktionen sollten dokumentieren, was sie bedeuten; wenn sie dies nicht tun, müssen Sie den Code betrachten.

Die gebräuchlichsten Bedeutungen sind jedoch "hat read(size)", "hat read()", oder" ist ein iterable von Strings ", aber einige alte Bibliotheken können erwarten readline statt einer davon mögen manche Bibliotheken close() Dateien, die Sie ihnen geben, werden einige erwarten, dass wenn fileno vorhanden ist, dann ist andere Funktionalität verfügbar usw. Und ähnlich für write(buf) (obwohl es viel weniger Möglichkeiten in dieser Richtung gibt).


39
2017-07-17 19:36



Das vorherrschende Paradigma ist hier EAFP: Leichter kann man um Vergebung bitten als um Erlaubnis. Gehen Sie weiter und verwenden Sie die Dateischnittstelle, dann behandeln Sie die resultierende Ausnahme oder lassen Sie sie an den Aufrufer weitergeben.


27
2017-11-02 13:25



Es ist oft nützlich, einen Fehler zu melden, indem Sie eine Bedingung prüfen, wenn dieser Fehler normalerweise erst viel später ausgelöst wird. Dies gilt insbesondere für die Grenze zwischen "user-land" und "api" -Code.

Sie würden keinen Metalldetektor bei einer Polizeistation an der Ausgangstür platzieren, Sie würden es am Eingang platzieren! Wenn das Überprüfen einer Bedingung nicht bedeutet, dass ein Fehler auftreten könnte, der 100 Zeilen früher oder in einer Superklasse aufgefangen werden könnte, anstatt in der Unterklasse ausgelöst zu werden, dann sage ich, dass mit der Überprüfung nichts falsch ist.

Die Überprüfung auf richtige Typen ist auch sinnvoll, wenn Sie mehr als einen Typ akzeptieren. Es ist besser, eine Ausnahme auszulösen, die besagt: "Ich benötige eine Unterklasse von basestring oder eine OR-Datei" anstatt nur eine Ausnahme auszulösen, weil eine Variable keine "seek" -Methode hat ...

Das bedeutet nicht, dass Sie verrückt werden und das überall tun. Ich stimme dem Konzept der Ausnahmeerstellung zu, aber wenn Sie Ihre API drastisch vereinfachen oder unnötige Codeausführung vermeiden können, weil eine einfache Bedingung nicht erfüllt wurde tun Sie dies!


10
2017-07-29 02:04



Sie können die Methode aufrufen und dann die Ausnahme abfangen:

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

Wenn Sie nur eine Lese- und eine Schreibmethode möchten, können Sie dies tun:

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

Wenn ich du wäre, würde ich mit der try / except-Methode gehen.


6
2017-11-02 13:15



In den meisten Fällen ist der beste Weg, damit umzugehen, nicht. Wenn eine Methode ein dateiähnliches Objekt annimmt und sich herausstellt, dass das Objekt nicht übergeben wird, ist die Ausnahme, die ausgelöst wird, wenn die Methode versucht, das Objekt zu verwenden, nicht weniger informativ als jede Ausnahme, die Sie explizit ausgelöst haben.

Es gibt jedoch mindestens einen Fall, in dem Sie diese Art der Überprüfung durchführen möchten, und wenn das Objekt nicht sofort von dem verwendet wird, an das Sie es übergeben haben, z. wenn es im Konstruktor einer Klasse gesetzt ist. In diesem Fall würde ich meinen, dass das Prinzip des EAFP durch das Prinzip des "Fail Fasten" übertroffen wird. Ich würde das Objekt überprüfen, um sicherzustellen, dass es die Methoden implementiert, die meine Klasse benötigt (und dass sie Methoden sind), z.

class C():
    def __init__(self, file):
        if type(getattr(file, 'read')) != type(self.__init__):
            raise AttributeError
        self.file = file

2
2017-11-03 16:42