Obiekty podobne do plików to obiekty w Pythonie, które zachowują się jak prawdziwy plik, np. Mają metodę read () i metodę zapisu (), ale mają inną implementację. To realizacja koncepcji Duck Typing .
Uważa się, że dobrą praktyką jest dopuszczenie obiektu podobnego do pliku wszędzie tam, gdzie oczekiwany jest plik, tak aby np. Obiekt StringIO lub Socket mógł być użyty zamiast prawdziwego pliku. Więc źle jest wykonać taką kontrolę:
if not isinstance(fp, file):
raise something
Jaki jest najlepszy sposób sprawdzenia, czy obiekt (np. Parametr metody) jest „podobny do pliku”?
why
co operatorzy podoba__add__
,__lshift__
lub__or__
w niestandardowych klas? (obiekt pliku i API: docs.python.org/glossary.html#term-file-object )W przypadku wersji 3.1+, jedno z poniższych:
W przypadku 2.x „obiekt plikopodobny” jest zbyt niejasny, aby go sprawdzić, ale dokumentacja wszelkich funkcji, z którymi masz do czynienia, powie ci, czego faktycznie potrzebują; jeśli nie, przeczytaj kod.
Jak wskazują inne odpowiedzi, pierwszą rzeczą, o którą należy zapytać, jest to, czego dokładnie szukasz. Zwykle EAFP jest wystarczający i bardziej idiomatyczny.
Słowniczek mówi „plikopodobnym obiektu” jest synonimem „obiektu pliku”, co ostatecznie oznacza, że jest to przykład jednej z trzech klas abstrakcyjnych zasad określonych w tym
io
module , które same są wszystkie podklasyIOBase
. Tak więc sposób sprawdzenia jest dokładnie taki, jak pokazano powyżej.(Jednak sprawdzanie
IOBase
nie jest zbyt przydatne. Czy możesz sobie wyobrazić przypadek, w którym musisz odróżnić rzeczywisty plik podobnyread(size)
do jakiejś jednoargumentowej funkcji o nazwie,read
która nie jest podobna do pliku, bez konieczności rozróżniania między plikami tekstowymi i nieprzetworzonymi pliki binarne? Więc tak naprawdę prawie zawsze chcesz sprawdzić, np. „czy jest obiektem pliku tekstowego”, a nie „jest obiektem podobnym do pliku”).W przypadku wersji 2.x, chociaż
io
moduł istnieje od wersji 2.6+, wbudowane obiekty plików nie są instancjamiio
klas, ani żadne z obiektów typu plikowego w standardowej bibliotece, ani też większość obiektów podobnych do plików innych firm. mogą się spotkać. Nie było oficjalnej definicji tego, co oznacza „obiekt podobny do pliku”; jest to po prostu „coś w rodzaju wbudowanego obiektu pliku ”, a różne funkcje oznaczają różne rzeczy przez „polubienie”. Takie funkcje powinny dokumentować ich znaczenie; jeśli nie, musisz spojrzeć na kod.Jednak najczęstsze znaczenia to „ma
read(size)
”, „maread()
” lub „jest iterowalna z ciągami znaków”, ale niektóre stare biblioteki mogą oczekiwaćreadline
zamiast jednego z nich, niektóre biblioteki lubiąclose()
pliki, które im dajesz, inne będą oczekiwać, że jeślifileno
jest obecny, wtedy dostępna jest inna funkcjonalność itp. I podobnie w przypadkuwrite(buf)
(chociaż jest o wiele mniej opcji w tym kierunku).źródło
IOBase
. Na przykład pytest daję oprawy_pytest.capture.EncodedFile
które nie dziedziczą z niczego.Jak powiedzieli inni, generalnie należy unikać takich kontroli. Jedynym wyjątkiem jest sytuacja, gdy obiekt może mieć różne typy i chcesz mieć różne zachowanie w zależności od typu. Metoda EAFP nie zawsze działa tutaj, ponieważ obiekt może wyglądać jak więcej niż jeden rodzaj kaczki!
Na przykład inicjator może pobrać plik, ciąg znaków lub wystąpienie własnej klasy. Możesz wtedy mieć kod taki jak:
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
Używanie EAFP tutaj może powodować różnego rodzaju subtelne problemy, ponieważ każda ścieżka inicjalizacji jest częściowo uruchamiana przed rzuceniem wyjątku. Zasadniczo ta konstrukcja naśladuje przeciążenie funkcji, a więc nie jest zbyt Pythonic, ale może być przydatna, jeśli jest używana ostrożnie.
Na marginesie, nie możesz sprawdzić pliku w ten sam sposób w Pythonie 3. Będziesz potrzebować czegoś takiego jak
isinstance(f, io.IOBase)
.źródło
Dominującym paradygmatem jest tutaj EAFP: łatwiej prosić o przebaczenie niż o pozwolenie. Śmiało i użyj interfejsu pliku, a następnie obsłuż wynikowy wyjątek lub pozwól im propagować do wywołującego.
źródło
x
nie jest podobny do pliku,x.read()
zgłosi swój własny wyjątek. Po co pisać dodatkowe oświadczenie „if”? Po prostu użyj obiektu. To zadziała lub się zepsuje.Często warto zgłosić błąd, sprawdzając warunek, gdy normalnie błąd ten nie zostanie zgłoszony dużo później. Jest to szczególnie prawdziwe w przypadku granicy między „obszarem użytkownika” a kodem „api”.
Nie umieściłbyś wykrywacza metalu na posterunku policji przy drzwiach wyjściowych, umieściłbyś go przy wejściu! Jeśli brak sprawdzenia warunku oznacza, że może wystąpić błąd, który mógł zostać przechwycony 100 linii wcześniej lub w superklasie zamiast zostać podniesiony w podklasie, to mówię, że nie ma nic złego w sprawdzaniu.
Sprawdzanie poprawnych typów ma również sens, gdy akceptujesz więcej niż jeden typ. Lepiej jest zgłosić wyjątek, który mówi „Wymagam podklasy bazowej, plik OR”, niż tylko zgłosić wyjątek, ponieważ pewna zmienna nie ma metody wyszukiwania ...
Nie oznacza to, że oszalałeś i robisz to wszędzie, w większości zgadzam się z koncepcją podnoszących się wyjątków, ale jeśli możesz drastycznie uczynić swoje API drastycznie przejrzystym lub uniknąć niepotrzebnego wykonywania kodu, ponieważ prosty warunek nie został spełniony Zrób tak!
źródło
Możesz spróbować wywołać metodę, a następnie złapać wyjątek:
try: fp.read() except AttributeError: raise something
Jeśli potrzebujesz tylko metody odczytu i zapisu, możesz to zrobić:
if not (hasattr(fp, 'read') and hasattr(fp, 'write')): raise something
Na twoim miejscu wybrałbym metodę try / except.
źródło
try
jest zawsze pierwszym wyborem. Tehasattr
kontrole są tylko - dla niektórych naprawdę niejasnych powodów - nie można po prostu używaćtry
.fp.read(0)
zamiastfp.read()
, aby uniknąć umieszczania całego kodu wtry
bloku, jeśli chcesz przetworzyć dane zfp
później.fp.read()
przypadku dużych plików natychmiast zwiększy się użycie pamięci.Flask
zrobiłem to i zdałem sobie sprawę, że podstawowyFileStorage
obiekt wymaga resetowania wskaźnika po przeczytaniu.W większości przypadków najlepszym sposobem na rozwiązanie tego problemu jest nie. Jeśli metoda przyjmuje obiekt podobny do pliku i okaże się, że przekazany obiekt nie jest, wyjątek, który jest zgłaszany, gdy metoda próbuje użyć obiektu, nie jest mniej informacyjny niż jakikolwiek wyjątek, który mógłbyś zgłosić jawnie.
Jest przynajmniej jeden przypadek, w którym możesz chcieć wykonać tego rodzaju sprawdzenie, a to wtedy, gdy obiekt nie jest natychmiast używany przez to, do czego go przekazałeś, np. Jeśli jest ustawiany w konstruktorze klasy. W takim przypadku myślę, że zasada EAFP jest podważona przez zasadę „szybkiej porażki”. Sprawdziłbym obiekt, aby upewnić się, że zaimplementował metody, których potrzebuje moja klasa (i że są to metody), np .:
class C(): def __init__(self, file): if type(getattr(file, 'read')) != type(self.__init__): raise AttributeError self.file = file
źródło
getattr(file, 'read')
zamiast po prostufile.read
? Robi dokładnie to samo.file
instancję. (Metody instancji typów wbudowanych / rozszerzeń C są typubuiltin_function_or_method
, podczas gdy metody klas starego typu sąinstancemethod
). Fakt, że jest to klasa w starym stylu i że używa==
na typach zamiastininstance
lubissubclass
, jest kolejnym problemem, ale jeśli podstawowa idea nie działa, to nie ma znaczenia.Skończyło się na tym, że natknąłem się na twoje pytanie, kiedy pisałem
open
podobną do funkcji funkcję, która mogła akceptować nazwę pliku, deskryptor pliku lub wstępnie otwarty obiekt podobny do pliku.Zamiast testować
read
metodę, jak sugerują inne odpowiedzi, w końcu sprawdziłem, czy obiekt można otworzyć. Jeśli tak, jest to ciąg lub deskryptor, a na podstawie wyniku mam w ręku prawidłowy obiekt podobny do pliku. Jeśliopen
podnosiTypeError
, to obiekt jest już plikiem.źródło