Ruby vs. Python - operacje na plikach

Posted by Jarosław Zabiełło Tue, 11 Apr 2006 19:47:00 GMT

Ostatnio miałem potrzebę napisania skryptu kopiującego całe pliki z całego drzewa folderów. Problem w tym, że chciałem kopiować tylko pliki, które nie miały ustawionych atrybutów tylko do odczytu. I to musiało być zrobione pod Windowsami. Zwykle jestem przyzwyczajony że tego typu zadania to piszę w Pythonie w kilka minut. Nie spodziewałem się że tu będzie inaczej….

Okazało się że Python (2.4.3) nie ma porządnie zaimplementowanych metod sprawdzania atrybutów pliku. Na próżno szukałem w manualu, Python Cookbook , internecie.

Zgodnie z prawami Murphego rozwiązanie znalazłem jak już było niepotrzebne (w międzyczasie zdążyłem napisać to w Ruby).

>>> os.stat(r'c:\tmp\test1')[stat.ST_MODE] & stat.S_IWRITE
128
>>> os.stat(r'c:\tmp\test2')[stat.ST_MODE] & stat.S_IWRITE
0

Wygląda to raczej paskudnie na tle analogicznego kodu Rubiego:

>>> File.writable?('c:\tmp\test1')
true
>>> File.writable?('c:\tmp\test2')
false

Może to szczególny przypadek, ale jakoś nie sprawdziła się w tym wypadku prostota Pythona. Metody Rubiego w tym temacie są znacznie bardziej intuicyjne. Zresztą zobaczmy poniższy przykład z manuala Pythona:

import os, sys
from stat import *
def walktree(top, callback):
    for f in os.listdir(top):
        pathname = os.path.join(top, f)
        mode = os.stat(pathname)[ST_MODE]
        if S_ISDIR(mode):
            walktree(pathname, callback)
        elif S_ISREG(mode):
            callback(pathname)
        else:
            print 'Skipping %s' % pathname
def visitfile(file):
    print 'visiting', file
if __name__ == '__main__':
    walktree(sys.argv[1], visitfile)

Ten sam kod w języku Ruby wygląda tak:

require 'find'
if $0 == __FILE__
  Find.find(ARGV[0]) do |f|
    if File.file?(f)
      puts 'visiting ' + f 
    elsif not File.directory?(f)
      puts 'skipping ' + f
    end
  end
end

I kto tu powie, że Ruby jest mniej czytelny od Pythona?

Posted in ,  | Tags ,  | 8 comments

Comments

  1. Avatar Michał Kwiatkowski said 41 minutes later:

    Co do pierwszego, to będzie wyglądać paskudnie, póki nie użyjesz przeznaczonej do tego funkcji. A jest to os.access.

    Co zaś do tej pętli, to też wymyśliłeś na nowo koło. Istnieje w bibliotece os funkcja walk. Kod wtedy znacznie się upraszcza:

    import os
    import sys
    
    def walktree(top, callback):
        for dirpath, dirnames, filenames in os.walk(top):
            map(lambda x: callback(os.path.join(dirpath, x)), filenames)
    
    def visitfile(file):
        print 'visiting', file
    
    if __name__ == '__main__':
        walktree(sys.argv[1], visitfile)

    Jeżeli zaś zapiszę to podobnie jak Ty zapisałeś to w Ruby’m, to będzie nawet krócej:

    import os
    import sys
    
    if __name__ == '__main__':
        for dirpath, dirnames, filenames in os.walk(sys.argv[1]):
            for file in filenames:
                print 'visiting %s' % os.path.join(dirpath, file)

    To, że znasz lepiej bibliotekę standardową jednego języka nie znaczy od razu, że jest on bardziej czytelny.

  2. Avatar Jarosław Zabiełło said about 1 hour later:

    Co do tego drugiego przykładu, to faktycznie os.walk() upraszcza sprawę (nie wiem dlaczego w manualu do Python 2.4.3 wciąż widnieje stary, mętny przykład) Ale podany przykład nie jest do końca odpowiednikiem tego z artykułu. Powinien np. wyświetlać “missing ” dla obiektów nie będących plikami ani folderami.

    Pierwszy też da się uprościć do:

    >>> import os
    >>> os.access(r'c:\tmp\test1',os.W_OK)
    True
    >>> os.access(r'c:\tmp\test2',os.W_OK)
    False

    Nie ma już zatem takiego kontrastu, ale nadal uważam, że konstrukcja File.writable?() jest czytelniejsza i bardziej intuicyjna. Guido mógłby używać jakieś lepsze nazwy.

  3. Avatar MySZ said about 7 hours later:

    Co do czytelności ‘is_writable’, ‘is_readable’ to kwestia gustu i podejścia. Można stosować różne funkcje, i mnożyć byty nad potrzebę (jak w PHP), albo zastosować jedną funkcję, spełniającą dokładnie taką samą rolę. Mi osobiście nie robi różnicy.

    A o co chodzi z mętnym przykładem os.walk() ? Nie wiem czy zwróciłeś uwagę, że w module os jest jedna funkcja walk(), a w os.path – druga. I w manualu wyrażnie jest napisane, której lepiej używać.

  4. Avatar Jarosław Zabiełło said about 8 hours later:

    W głównym manualu do Pythona wypowiadającym się na temat interpretacji wyników funkcji stat() widnieje przykład taki, jaki podałem w artykule. Nie wspominają nic o os.walk().

    Ruby nie ma żadnego is_writable, ma za to writable? (Znaki zapytania dotyczą metod które zwracają wartość boolowską). Poza tym Python powinien (zgodnie ze swoimi założeniami) minimalizować ilość metod wykonujących tą samą czynność. Może to i banał, ale trochę musiałem się naszukać aby odnaleźć właściwy moduł do sprawdzania czy plik ma ustawiony atrybut do zapisu. I wiem, że nie tylko ja byłem tu zdezorientowany.

  5. Avatar MySZ said about 12 hours later:

    is_writable etc to było nawiązanie do PHP. Co do manuala: manual pokazuje funkcje które są omawiane, i akurat ktoś kto dokumentował moduł stat wolał sam rekurencyjnie ‘biegać’ po katalogach. A może w czasach gdy go opisywał nie było jeszcze os.walk ? Nie wiem, nie wnikam. Jak dla mnie, jeśli chcę dokonać operacji na katalogach/plikach, to zaczynam szukać w os lub os.path. Stamtąd mnie odeślą najwyżej na stat, shutils czy inne.

    A co do dezorientacji: zaczynałem uczyć się pythona od operacji właśnie na plikach i katalogach, i jakoś nie miałem problemu ze sprawdzaniem praw do pliku. Ani grama dezorientacji :)

  6. Avatar univac said about 22 hours later:

    E-e, wolimy pythona ;)

  7. Avatar misiek said 26 days later:

    Eeee, my wolimy Ruby.

  8. Avatar Maciek said 2 months later:

    Zdecydowanie wolimy Pythona. Jest piekny.

(leave url/email »)

   Comment Markup Help Preview comment