کار کردن با چند فایل

می‌خواهیم کتاب‌های بیشتری برای تحلیل اضافه کنیم. امّا پیش از این کار اجازه دهید بدنه‌ی این برنامه را به داخل تابعی به نام count_words() منتقل کنیم. با این کار تحلیل کردن چند کتاب آسان‌تر خواهد بود:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def count_words(filename):
    """Count the approximate number of words in a file."""
    try:
        with open(filename, encoding='utf-8') as f:
            contents = f.read()
    except FileNotFoundError:
        print(f"Sorry, the file {filename} does not exist.")
    else:
        words = contents.split()
        num_words = len(words)
        print(f"The file {filename} has about {num_words} words.")

filename = 'alice.txt'
count_words(filename)

بیشتر کد دست‌نخورده است. صرفاً آن را با تورفتگی داخل بدنه‌ی count_words() قرار داده‌ایم. به‌روز نگه‌داشتنِ توضیحات هنگام تغییر دادن برنامه عادت خوبی است بنابراین توضیح را به یک docstring تبدیل کرده و کمی آن را تغییر داده‌ایم (۱).

حال حلقه‌ی ساده‌ای می‌نویسیم که تعداد کلمات هر متن99 ی را محاسبه کند. این کار را با ذخیره کردنِ نام فایل‌ها در یک لیست و سپس فراخوانی count_words() برای هر فایل در لیست انجام می‌دهیم. سعی می‌کنیم تعداد کلمات آلیس در سرزمین عجایب، سیذارتا، mobی دیک و زنان کوچک را که همگی در قلمرو عمومی هستند، تحلیل کنیم. من به عمد فایل siddhartha.txt را از پوشه‌ی برنامه‌ی word_count.py بیرون گذاشته‌ام تا ببینم برنامه چگونه فقدان فایل را مدیریّت می‌کند:

1
2
3
4
5
6
7
def count_words(filename):
    --snip--

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']

for filename in filenames:
    count_words(filename)

فقدانِ فایل siddhartha.txt تاثیری بر اجرای باقی برنامه ندارد:

1
2
3
4
The file alice.txt has about 29465 words.
Sorry, the file siddhartha.txt does not exist.
The file moby_dick.txt has about 215830 words.
The file little_women.txt has about 189079 words.

به‌کارگیریِ بلوک try-except در این مثال دو مزیّت مهم دارد. مانع این می‌شویم که کاربر traceback‌ای ببیند و باعث می‌شویم برنامه به تحلیل متن‌هایی که می‌تواند پیدا کند ادامه دهد. اگر مانع خطای FileNotFoundError را که Siddhartha.txt موجب آن شده بود، نمی‌شدیم کاربر یک traceback کامل را مشاهده می‌کرد و برنامه پس از تلاش برای تحلیل سیذارتا متوقّف می‌شد. در نتیجه هیچ‌گاه mobی‌دیک و زنان کوچک را بررسی نمی‌کرد.

رد کردنِ بی‌سروصدای یک خطا

در مثال پیشین به کاربران خود اطّلاع دادیم که یکی از فایل‌ها موجود نیست. امّا نیازی نیست هر استثنایی را که رخ می‌دهد گزارش کنید. گاهی شما می‌خواهید که مشکلات برنامه بدون هیچ سروصدایی رد شوند درست مثل این که هیچ اتّفاقی نیفتاده است. برای رسیدن به این مقصود یک بلوک try مانند همیشه می‌نویسید امّا به پایتون به طور صریح می‌گویید که در بلوک except هیچ کاری نکند. دستوری به نام pass به پایتون می‌گوید که هیچ کاری انجام ندهد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def count_words(filename):
    """Count the approximate number of words in a file."""
    try:
        --snip--
    except FileNotFoundError:
        pass
    else:
        --snip--

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']

for filename in filenames:
    count_words(filename)

تنها تفاوت این برنامه با قبلی عبارت pass در ۱ است. حال وقتی FileNotFoundError رخ می‌دهد کد داخل بلوک except اجرا می‌شود امّا هیچ اتّفاقی نمی‌افتد. هیچ traceback‌ای و هیچ خروجی‌ای در پاسخ به خطای رخ‌داده تولید نمی‌شود. کاربران تعداد کلمات هر فایل موجود را می‌بینند امّا در مورد وجود نداشتنِ یک فایل هیچ علامتی نمی‌بینند:

1
2
3
The file alice.txt has about 29465 words.
The file moby_dick.txt has about 215830 words.
The file little_women.txt has about 189079 words.

دستور pass همچنین به عنوان یک جای‌بان هم عمل می‌کند. به شما یادآوری می‌کند که شما تصمیم گرفته‌اید در جایی از برنامه هیچ کاری نکنید و این که ممکن است بعداً بخواهید در آن نقطه کاری انجام دهید. برای مثال در این برنامه ممکن است تصمیم بگیرید که نام فایل‌های یافت‌نشده را در فایلی به نام missing_files.txt بنویسید. کاربران ما این فایل را نمی‌بینند ولی ما قادر هستیم که آن را بخوانیم و در مورد متن‌های پیدانشده کاری بکنیم.

کدام خطا را گزارش کنیم؟

چگونه بفهمیم که کی باید خطا را به کاربر گزارش کنیم و چه زمانی باید از کنار آن به آرامی بگذریم؟ اگر کاربران بدانند که چه متن‌هایی قرار است تحلیل شوند، ممکن است از دیدنِ پیامی که می‌گوید چرا برخی از متن‌ها تحلیل نشده‌اند استقبال کنند. اگر کاربران منتظرِ دیدن برخی نتایج هستند و نمی‌دانند چه کتاب‌هایی قرار است تحلیل شود، شاید نیازی نباشد که از نبودنِ برخی از متن‌ها مطّلع شوند. دادنِ اطّلاعات غیرمنتظره به کاربران می‌تواند باعث کاهش کارایی برنامه‌ی شما شود. ساختارهای مدیریّت خطای پایتون به شما امکان می‌دهد که در زمان بروز خطا اطّلاعاتی را که در اختیار کاربر قرار می‌دهید با جزئیات بسیاری کنترل کنید؛ این که چه‌قدر اطّلاعات با کاربر به اشتراک گذاشته شود به شما بستگی دارد.

برنامه‌ای که به خوبی نوشته و به درستی آزموده شده خیلی در معرضِ خطاهای داخلی مانند خطاهای نحوی یا منطقی نیست. امّا هر بار برنامه‌ی شما به چیزی خارج از خود وابسته باشد، مانند ورودیِ کاربر، وجودِ یک فایل یا ارتباط شبکه، امکان رخ دادنِ یک استثنا وجود دارد. کمی تجربه به شما کمک می‌کند که بدانید کی از بلوک‌های مدیریّت استثنا استفاده کنید و به کاربر در مورد خطاهایی که رخ می‌دهد چه‌قدر اطّلاعات بدهید.