2011-07-21 15 views
11

Chcę napisać funkcję, która akceptuje ścieżkę jako ciąg lub obiekt pliku. Do tej pory mam:Funkcja Pythona akceptująca obiekt lub ścieżkę pliku

def awesome_parse(path_or_file): 
    if isinstance(path_or_file, basestring): 
     f = open(path_or_file, 'rb') 
    else: 
     f = path_or_file 
    with f as f: 
     return do_stuff(f) 

gdzie do_stuff trwa otwarty obiekt pliku.

Czy jest lepszy sposób to zrobić? Czy with f as f: ma jakieś konsekwencje?

Dzięki!

Odpowiedz

14

Dziwną rzeczą w twoim kodzie jest to, że jeśli zostanie przekazany otwarty plik, to go zamknie. To nie jest dobre. Bez względu na to, jaki kod jest otwarty, plik powinien być odpowiedzialny za jego zamknięcie. To sprawia, że ​​funkcja nieco bardziej skomplikowany choć:

def awesome_parse(path_or_file): 
    if isinstance(path_or_file, basestring): 
     f = file_to_close = open(path_or_file, 'rb') 
    else: 
     f = path_or_file 
     file_to_close = None 
    try: 
     return do_stuff(f) 
    finally: 
     if file_to_close: 
      file_to_close.close() 

Można streszczenie tego z dala poprzez pisanie własnego menedżera kontekstowego:

@contextlib.contextmanager 
def awesome_open(path_or_file): 
    if isinstance(path_or_file, basestring): 
     f = file_to_close = open(path_or_file, 'rb') 
    else: 
     f = path_or_file 
     file_to_close = None 

    try: 
     yield f 
    finally: 
     if file_to_close: 
      file_to_close.close() 

def awesome_parse(path_or_file): 
    with awesome_open(path_or_file) as f: 
     return do_stuff(f) 
+0

Oh ... dobry haczyk przy zamykaniu pliku, gdy nie powinien! Zdecydowanie nie chcę, żeby tak się stało. Dzięki! – TorelTwiddler

+1

Czy "wydajność" nie powinna być "yield f"? –

+0

Co dokładnie robi '@ contextlib.contextmanager'? Dlaczego otrzymuję bez niego atrybut "AttributeError: __exit__"? Dzięki! –

3

Można zrobić:

def awesome_parse(do_stuff): 
    """Decorator to open a filename passed to a function 
     that requires an open file object""" 
    def parse_path_or_file(path_or_file): 
     """Use a ternary expression to either open the file from the filename 
      or just pass the extant file object on through""" 
     with (open(path_or_file, 'rb') 
       if isinstance(path_or_file, basestring) 
       else path_or_file) as f: 
      return do_stuff(f) 
    return parse_path_or_file 

a kiedy zadeklarować dowolną funkcję, która robi rzeczy na otwartym obiekcie Plik:

@awesome_parse 
def do_things(open_file_object): 
    """This will always get an open file object even if passed a string""" 
    pass 

@awesome_parse 
def do_stuff(open_file_object): 
    """So will this""" 
    pass 

Edycja 2: Więcej szczegółowych informacji na dekoratora.

+0

Ach, tak, przepraszam. Po prostu zmieniłem go na 'awesome_parse', aby nie mylić z' super'. – TorelTwiddler

+0

Ok, będę edytować moje, aby dopasować. – agf

+1

Wygląda na to, że twoja odpowiedź mieści się w tej samej pułapce, do której odnosi się odpowiedź @Ned Batchelder (zamykanie pliku przedwcześnie). Robię to jednak, podobnie jak pomysł dekoratora, więc wykorzystam również twój pomysł! – TorelTwiddler

Powiązane problemy