Başarı veya başarısızlık tek bir sorun olduğunda boole döndürmek


15

Sık sık kendimi bir yöntemden bir boolean döndürürken, bu yöntem çevresindeki tüm mantığı tek bir yerde içermek için kullanılır. Tüm (dahili) çağrı yönteminin bilmesi gereken işlemin başarılı olup olmadığıdır.

Python kullanıyorum ama soru mutlaka bu dile özgü değil. Aklıma gelen sadece iki seçenek var

  1. Koşullar istisnai olmasa da bir istisna oluşturun ve bu istisnayı işlevin çağrıldığı her yerde yakalamayı unutmayın.
  2. Yaptığım gibi bir boole döndürün.

Bu, neden bahsettiğimi gösteren gerçekten basit bir örnek.

import os

class DoSomething(object):

    def remove_file(self, filename):

        try:
            os.remove(filename)
        except OSError:
            return False

        return True

    def process_file(self, filename):

        do_something()

        if remove_file(filename):
            do_something_else()

İşlevsel olmasına rağmen, bu tür bir şey yapmayı gerçekten sevmiyorum, "kokuyor" ve bazen çok fazla iç içe ifs ile sonuçlanabilir. Ama daha basit bir yol düşünemiyorum.

Daha fazla LBYL felsefesine dönebilir ve silmeye os.path.exists(filename)çalışmadan önce kullanabilirim ancak dosyanın bu arada kilitlenmeyeceğine dair bir garanti yok (olası değil ancak mümkün) ve hala silme işleminin başarılı olup olmadığını belirlemek zorundayım.

Bu "kabul edilebilir" bir tasarım mıdır ve değilse bunu tasarlamanın daha iyi bir yolu olabilir mi?

Yanıtlar:


11

booleanYöntem / işlev mantıksal kararlar vermede yararlı olduğunda geri dönmelisiniz.

exceptionYöntem / fonksiyonun mantıksal kararlarda kullanılması muhtemel olmadığında bir süre atmalısınız .

Arızanın ne kadar önemli olduğu ve ele alınıp alınmayacağı konusunda bir karar vermelisiniz. Hatayı bir uyarı olarak sınıflandırabiliyorsanız, geri dönün boolean. Nesne, gelecekteki çağrıları kararsız hale getiren kötü bir duruma girerse, bir exception.

Başka bir uygulama objectssonuç yerine geri dönmektir . Eğer ararsanız open, bir Filenesne döndürmelidir veya nullaçılamıyorsa. Bu, programcıların kullanılabilecek geçerli bir durumda olan bir nesne örneğine sahip olmasını sağlar.

DÜZENLE:

Çoğu dil, tür boole veya tamsayı olduğunda bir işlevin sonucunu atacağını unutmayın. Sonuç için sol el ataması olmadığında işlevi çağırmak mümkündür. Boole sonuçlarıyla çalışırken, programlayıcının döndürülen değeri yok saydığını varsayalım ve bunu bir istisna olup olmayacağına karar vermek için kullanın.


Yaptığım şeyin bir doğrulaması bu yüzden cevabı beğendim :-). Nesnelerde, nereden geldiğinizi anlasam da, çoğu durumda bunun nasıl kullanacağımı göremiyorum. Ben KURU olmak istiyorum, bu yüzden sadece bir şey yapmak istiyorum gibi nesneyi tek bir yöntem yeniden olacak. Daha sonra ben şimdi aynı kod ile ek bir yöntemle kaydedin. (ayrıca verilen örnek için boş bir dosya nesnesi çok fazla söylemeyecek şekilde dosyayı siliyorum :-)
Ben

Silinmesi zordur, çünkü garanti edilmez. Bir dosya silme yönteminin bir istisna attığını görmedim, ancak programcı başarısız olursa ne yapabilir? Sürekli döngü yeniden deneniyor mu? Hayır, bu bir işletim sistemi sorunu. Kod sonucu günlüğe kaydetmeli ve devam etmelidir.
Reactgular

4

Bununla ilgili sezgileriniz doğru, bunu yapmanın daha iyi bir yolu var: monadlar .

Monadlar Nedir?

Monadlar zincirleme mekanizmasını gizlerken operasyonları birlikte zincirlemenin bir yoludur (Wikipedia'yı yorumlamak için); sizin durumunuzda zincirleme mekanizması iç içe geçmiş ifs'dir. Bunu saklayın ve kodunuz çok daha güzel kokacak .

Sadece ("Belki" ve "İkisi") yapacak bir çift monad var ve onlar için gerçekten güzel bir python monad kütüphanesinin bir parçası olduklarını !

Kodunuz için yapabilecekleri

Aşağıda, bir işlevin ne olduğuna bağlı olarak bir Başarı veya Hata döndürebileceği "İkisinden biri" monadını (bağlı olan kitaplıkta "Başarısız") kullanan bir örnek verilmiştir:

import os

class DoSomething(object):

    def remove_file(self, filename):
        try:
            os.remove(filename)
            return Success(None)
        except OSError:
            return Failure("There was an OS Error.")

    @do(Failable)
    def process_file(self, filename):
        do_something()
        yield remove_file(filename)
        do_something_else()
        mreturn(Success("All ok."))

Şimdi, bu şu anda sahip olduğunuzdan çok farklı görünmeyebilir, ancak bir Arıza ile sonuçlanabilecek daha fazla işleminiz varsa nasıl olacağını düşünün:

    def action_that_might_fail_and_returns_something(self):
        # get some random value between 0 and 1 here
        if value < 0.5:
            return Success(value)
        else:
            return Failure("Bad value! Bad! Go to your room!")

    @do(Failable)
    def process_file(self, filename):
        do_something()
        yield remove_file(filename)
        yield action_that_might_fail(somearg)
        yield another_action_that_might_fail(someotherarg)
        some_val = yield action_that_might_fail_and_returns_something()
        yield something_that_used_the_return_value(some_val)
        do_something_else()
        mreturn(Success("All ok."))

İşlevdeki her yields'de process_file, işlev çağrısı bir Arıza döndürürse, process_fileişlev o noktada çıkıp geri kalanı boyunca devam etmek ve geri dönmek yerine Arıza değerini başarısız işlevden döndürür.Success("All ok.")

Şimdi, yukarıdakileri iç içe ifs ile yaptığınızı hayal edin ! (Dönüş değerini nasıl idare edersiniz !?)

Sonuç

Monadlar güzel :)


Notlar:

Ben bir Python programcısı değilim - bazı proje otomasyonu için ninja'd bir betiğe bağlı monad kütüphanesini kullandım. Yine de, genel olarak tercih edilen deyimsel yaklaşımın istisnaları kullanmak olduğunu düşünüyorum.

IIRC, ATM'nin nerede olduğunu unuttuğum halde bağlı sayfadaki lib komut dosyasında bir yazım hatası var. Hatırlarsam güncelleyeceğim. Sürümümü sayfanın karşısına çıkardım ve buldum: def failable_monad_examle():-> def failable_monad_example():- pin exampleeksikti.

Failable dekor fonksiyonunun (örneğin process_file) sonucunu elde etmek için sonucu a olarak almalı variableve bunu variable.valueelde etmek için a yapmalısınız.


2

İşlev bir sözleşmedir ve adı hangi sözleşmeyi yerine getireceğini önermelidir. IMHO, remove_filedosya adını kaldırmanız ve bunu yapmamanız durumunda bir istisna oluşması gerekir. Öte yandan, adlandırırsanız try_remove_file, kaldırılıp dosyanın kaldırılıp kaldırılmadığını söylemek için boole döndürmek için "denemek" gerekir.

Bu başka bir soruya yol açar - öyle mi remove_fileyoksa değil try_remove_filemi? Bu, çağrı sitenize bağlıdır. Aslında, her iki yönteme de sahip olabilirsiniz ve bunları farklı senaryoda kullanabilirsiniz, ancak dosya per-se kaldırmanın başarı şansı yüksek olduğunu düşünüyorum, bu yüzden başarısız olduğunda sadece remove_filebu istisna atmayı tercih ederim .


0

Bu özel durumda, dosyayı neden kaldıramayacağınızı düşünmek yararlı olabilir. Diyelim ki sorun dosyanın var olup olmaması olabilir. Ardından doesFileExist(), true veya false döndüren bir işleve removeFile()ve yalnızca dosyayı silen bir işleve sahip olmalısınız .

Kodunuzda önce dosyanın var olup olmadığını kontrol edersiniz. Varsa arayın removeFile. Değilse, başka şeyler yapın.

Bu durumda removeFile, dosya izinler gibi başka bir nedenden dolayı kaldırılamazsa yine de bir istisna atmak isteyebilirsiniz .

Özetlemek gerekirse, istisnai şeyler için istisnalar atılmalıdır. Bu nedenle, silmeye çalıştığınız dosyanın mevcut olmaması normalse, bu bir istisna değildir. Bunu kontrol etmek için bir boole yüklemi yazın. Öte yandan, dosya için yazma izniniz yoksa veya aniden erişilemeyen uzak bir dosya sistemindeyse, bunlar çok iyi durumlar olabilir.


Bu, verdiğim örneğe çok spesifik ve kaçınmayı tercih ederim. Bunu henüz yazmadım, dosyaları arşivleyecek ve bunun veritabanında gerçekleştiğini kaydedecek. Dosyalar herhangi bir zamanda yeniden yüklenebilir (yüklenen dosyaların yeniden yüklenmesi çok daha az olası olsa da), bir dosyanın denetim ve silme girişimi arasındaki başka bir işlemle kilitlenmesi mümkündür. Bir başarısızlık hakkında istisnai bir şey yoktur. İlk kontrol rahatsız etmeyin ve yükseltildiğinde istisna yakalamak için standart Python (gerekirse), ben sadece bu sefer onunla bir şey yapmak istemiyorum.
Ben

Hata ile ilgili istisnai bir şey yoksa, bir dosyayı kaldırabileceğinizi kontrol etmek program mantığınızın meşru bir parçasıdır. Tek sorumluluk ilkesi, bir kontrol işlevine ve removeFile işlevine sahip olmanız gerektiğini belirtir.
Dima
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.