성공 또는 실패가 유일한 관심사 일 때 부울 리턴


15

한 곳에서 해당 방법에 대한 모든 논리를 포함하기 위해 여러 위치에서 사용되는 메서드에서 부울을 반환하는 경우가 종종 있습니다. 알아야 할 모든 (내부) 호출 방법은 작업의 성공 여부입니다.

나는 파이썬을 사용하고 있지만 질문은 반드시 해당 언어에만 국한되는 것은 아닙니다. 내가 생각할 수있는 두 가지 옵션 만 있습니다

  1. 상황이 예외적이지는 않지만 예외를 제기하고 함수가 호출되는 모든 곳에서 예외를 포착해야합니다.
  2. 내가하고있는 것처럼 부울을 반환하십시오.

이것은 내가 말하는 것을 보여주는 정말 간단한 예입니다.

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()

그것이 기능적이지만, 나는 무언가를하는이 방식을 정말로 싫어합니다. 그것은 "냄새가 나고"때때로 많은 중첩 된 if를 초래할 수 있습니다. 그러나 나는 더 간단한 방법을 생각할 수 없습니다.

os.path.exists(filename)삭제를 시도하기 전에 더 많은 LBYL 철학으로 전환하여 사용할 수는 있지만 그 동안 파일이 잠기지 않았다는 보장은 없습니다 (아마도 가능하지는 않지만 가능합니다). 삭제가 성공했는지 여부를 결정해야합니다.

이것이 "허용 가능한"디자인입니까, 그렇지 않은 경우 더 나은 디자인 방법은 무엇입니까?

답변:


11

boolean방법 / 기능이 논리적 결정을 내리는 데 유용 할 때 돌아와야 합니다.

exception메소드 / 함수가 논리적 의사 결정에 사용되지 않을 때를 던져야합니다 .

장애의 중요성과 처리 여부를 결정해야합니다. 실패를 경고로 분류 할 수 있으면를 반환하십시오 boolean. 객체가 나중에 불쾌한 상태가되는 잘못된 상태가되면를 던집니다 exception.

또 다른 방법은 objects결과 대신 반환 하는 것입니다. 를 호출 open하면 File객체를 반환 하거나 null열 수없는 경우 반환해야합니다 . 이를 통해 프로그래머는 사용할 수있는 유효한 상태에있는 객체 인스턴스를 갖게됩니다.

편집하다:

대부분의 언어는 유형이 부울이거나 정수인 경우 함수의 결과를 버립니다. 결과에 왼손이 할당되지 않은 경우 함수를 호출 할 수 있습니다. 부울 결과로 작업 할 때는 항상 프로그래머가 반환 된 값을 무시한다고 가정하고 예외를 사용해야하는지 여부를 결정하는 데 사용하십시오.


내가하고있는 일에 대한 검증이므로 대답 :-)을 좋아합니다. 객체에 대해서는 어디에서 왔는지 이해하지만 이것이 대부분의 경우에 어떻게 도움이되는지 모르겠습니다. 한 가지만하고 싶기 때문에 DRY를 원하므로 객체를 단일 메소드로 다시 조정하려고합니다. 그런 다음 지금 가지고있는 것과 동일한 코드를 남겨두고 추가 방법으로 저장하십시오. (또한 주어진 예제에서 파일을 삭제하므로 null 파일 객체는별로 말하지 않습니다 :-)
Ben

보장되지 않으므로 삭제가 까다 롭습니다. 파일 삭제 메소드에서 예외가 발생하는 것을 본 적이 없지만 프로그래머가 실패하면 어떻게 할 수 있습니까? 지속적으로 루프 재시도? 아니요, OS 문제입니다. 코드는 결과를 기록하고 계속 진행해야합니다.
Reactgular

4

이것에 대한 당신의 직감은 정확합니다.이를 수행하는 더 좋은 방법이 있습니다 : monads .

Monads 란 무엇입니까?

모나드는 연결 메커니즘을 숨기면서 (위키 백과를 역설하기 위해) 서로 연결하는 방법입니다. 귀하의 경우 연결 메커니즘은 중첩 if입니다. 그것을 숨기면 코드가 훨씬 멋지게 나옵니다.

그냥 그렇게 할 두 개의 모나드 ( "Maybe"와 "Either")가 있으며 운 이 좋은 파이썬 모나드 라이브러리의 일부입니다 !

그들이 당신의 코드를 위해 할 수있는 것

다음은 "Either"모나드 (연결된 라이브러리의 "Failable")를 사용하는 예입니다. 여기서 함수는 발생한 상황에 따라 성공 또는 실패를 반환 할 수 있습니다.

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."))

이제는 현재와 크게 다르지 않지만 더 많은 작업을 수행하여 오류가 발생할 수있는 상황은 다음과 같습니다.

    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."))

함수 yield의 각 s에서 process_file함수 호출이 실패를 리턴하면 해당 시점에서process_file 함수가 종료되고 나머지 부분을 계속 진행하는 대신 실패한 함수에서 실패 값을 리턴합니다.Success("All ok.")

이제 중첩 된 ifs로 위를 수행한다고 상상해보십시오 ! (반환 값을 어떻게 처리하겠습니까!?)

결론

모나드는 좋다 :)


노트:

저는 파이썬 프로그래머가 아닙니다. 프로젝트 자동화를 위해 닌자 스크립트로 위의 링크 된 모나드 라이브러리를 사용했습니다. 그러나 일반적으로 선호되는 관용적 접근법은 예외를 사용하는 것입니다.

IIRC는 ATM의 위치를 ​​잊었지만 링크 된 페이지의 lib 스크립트에 오타가 있습니다. 기억이 나면 업데이트하겠습니다. 내 페이지와 페이지를 비교하여 다음을 발견했습니다 def failable_monad_examle():.-> def failable_monad_example():- p에서 example누락되었습니다.

(같은 Failable 장식 된 함수의 결과를 얻기 위하여 process_file당신이에 결과를 캡처 할 수있다) variable과을 variable.value그것을 얻을 수 있습니다.


2

함수는 계약이며, 그 이름은 어떤 계약을 이행 할 것인지 제안해야합니다. IMHO, remove_file파일 이름을 지정 하면 파일이 제거되고 그렇지 않으면 예외가 발생합니다. 반면, 이름을 try_remove_file"으로 지정하면 파일을 제거했는지 여부를 알리기 위해 부울을 제거하고 반환하도록"시도 "해야합니다.

그것은이어야한다 - 이것은 또 다른 질문으로 이어질 것 remove_filetry_remove_file? 전화 사이트에 따라 다릅니다. 실제로 두 가지 방법을 모두 사용할 수 있으며 다른 시나리오에서 사용할 수는 있지만 파일 당 제거는 성공 가능성이 높으므로 remove_file실패 할 때 예외가 발생 하는 것을 선호합니다 .


0

이 특정한 경우 파일을 제거 할 수없는 이유에 대해 생각하는 것이 유용 할 수 있습니다. 문제가 파일이 존재하거나 존재하지 않는다고 가정 해 봅시다. 그런 다음 doesFileExist()true 또는 false를 반환하는 함수와 removeFile()파일을 삭제 하는 함수 가 있어야합니다.

코드에서 먼저 파일이 존재하는지 확인합니다. 그렇다면 전화하십시오 removeFile. 그렇지 않은 경우 다른 작업을 수행하십시오.

이 경우 removeFile권한과 같은 다른 이유로 파일을 제거 할 수없는 경우에도 예외가 발생할 수 있습니다.

요약하면 예외적 인 것들에 대해서는 예외를 던져야합니다. 따라서 삭제하려는 파일이 존재하지 않는 것이 정상이라면 예외가 아닙니다. 이를 확인하기 위해 부울 술어를 작성하십시오. 반면에 파일에 대한 쓰기 권한이 없거나 갑자기 액세스 할 수없는 원격 파일 시스템에있는 파일은 예외적 인 조건 일 수 있습니다.


그것은 내가 주어진 예에 매우 구체적이며, 오히려 피하고 싶습니다. 아직 작성하지 않았으므로 파일을 보관하고 데이터베이스에서 발생한 사실을 기록합니다. 파일은 언제든지 다시로드 할 수 있습니다 (로드 된 파일을 다시로드 할 가능성은 훨씬 적지 만) 검사와 삭제 시도 사이의 다른 프로세스에 의해 파일을 잠글 수 있습니다. 실패에 대한 예외는 없습니다. 먼저 검사를 귀찮게하지 말고 (필요한 경우) 예외를 포착하지 않는 것이 표준 파이썬입니다. 이번에는 아무것도하지 않으려 고합니다.
Ben

실패에 대한 예외가 없으면 파일을 제거 할 수 있는지 확인하는 것이 프로그램 논리의 합법적 인 부분입니다. 단일 책임 원칙에 따라 check 기능과 removeFile 기능이 있어야합니다.
Dima
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.