Nice programing

“허가가 아닌 용서를 구하십시오”-설명

nicepro 2020. 12. 28. 22:31
반응형

“허가가 아닌 용서를 구하십시오”-설명


나는이 철학에 대한 개인적인 "종교적"의견을 요구하는 것이 아니라 좀 더 기술적 인 것입니다.

이 문구는 코드가 "pythonic"인지 확인하기위한 여러 리트머스 테스트 중 하나라는 것을 이해합니다. 그러나 나에게 pythonic은 깨끗하고 간단하며 직관적이며 잘못된 코딩을위한 예외 처리기가로드되지 않은 것을 의미합니다.

그래서 실용적인 예입니다. 클래스를 정의합니다.

class foo(object):
    bar = None

    def __init__(self):
        # a million lines of code
        self.bar = "Spike is my favorite vampire."
        # a million more lines of code

이제 절차 적 배경에서 왔으며 다른 함수에서 이렇게하고 싶습니다.

if foo.bar:
    # do stuff

참을성이없고 초기 foo = None을 수행 하지 않으면 속성 예외가 발생합니다 . 그래서 "허가가 아닌 용서를 구하라"는 대신 이것을해야한다고 제안 하는가?

try:
    if foo.bar:
        # do stuff
except:
    # this runs because my other code was sloppy?

클래스 정의를 더 모호하게 남겨 둘 수 있도록 try 블록에 추가 논리를 추가하는 것이 왜 더 나을까요 ? 처음에 모든 것을 정의하지 않고 명시 적으로 권한을 부여 하는 이유는 무엇 입니까?

(try / except 블록을 사용하는 것에 대해 저를 때리지 마십시오 ... 저는 모든 곳에서 사용합니다. 철저한 프로그래머가 아니기 때문에 제 자신의 오류를 포착하는 데 사용하는 것이 옳지 않다고 생각합니다.)

아니면 ... "용서 요청"만트라를 완전히 오해하고 있습니까?


고전적인 "권한이 아닌 용서 요청"예제는 dict존재하지 않을 수 있는 값에 액세스하는 것 입니다. 예 :

names = { 'joe': 'Joe Nathan', 'jo': 'Jo Mama', 'joy': 'Joy Full' }
name = 'hikaru'

try:
    print names[name]
except KeyError:
    print "Sorry, don't know this '{}' person".format(name)

여기에 발생할 수있는 예외 ( KeyError)가 명시되어 있으므로 발생할 수있는 모든 오류에 대해 용서를 구하는 것이 아니라 자연스럽게 발생할 수있는 오류에 대해서만 용서를 구하는 것입니다. 비교를 위해 "권한을 먼저 요청"접근 방식은 다음과 같습니다.

if name in names:
    print names[name]
else:
    print "Sorry, don't know this '{}' person".format(name)

또는

real_name = names.get(name, None)
if real_name:
    print real_name
else:
    print "Sorry, don't know this '{}' person".format(name)

그러한 "용서를 구하라"의 예는 종종 너무 간단합니다. IMO는 try/ except블록이 본질적으로 if/ 보다 낫다는 것이 명확하지 않습니다 else. 실제 값은 구문 분석과 같이 다양한 방식으로 실패 할 수있는 작업을 수행 할 때 훨씬 더 명확합니다. 사용 eval(); 운영 체제, 미들웨어, 데이터베이스 또는 네트워크 리소스에 액세스 또는 복잡한 수학 수행. 여러 가지 잠재적 인 실패 모드가있을 때 용서를받을 준비를하는 것은 매우 중요합니다.

코드 예제에 대한 기타 참고 사항 :

모든 변수 사용에 대해 래들 try/ except블록 할 필요가 없습니다 . 그것은 끔찍할 것입니다. 그리고 당신은 설정할 필요가 없습니다 self.bar당신에 __init__()가 당신에 설정되어 있기 때문에 class위의 정의. 클래스 (클래스의 모든 인스턴스간에 공유 될 가능성이있는 데이터 인 경우) 또는 __init__()(인스턴스 데이터 인 경우 각 인스턴스에 특정한 )에서 정의하는 것이 일반적 입니다.

의 값은 None정의되지 않았거나 오류가 아닙니다. 특정하고 합법적 인 값으로 none, nil, null 또는 없음을 의미합니다. 프로그래머가 없다 "과부하"이렇게 많은 언어는 값이 0, -1, ''(빈 문자열) 또는 이와 유사한 유용한 값을.


“허가가 아닌 용서를 구하라”는 두 가지 프로그래밍 스타일에 반대합니다. "권한 요청"은 다음과 같습니다.

if can_do_operation():
    perform_operation()
else:
    handle_error_case()

"용서를 구하십시오"는 다음과 같이 진행됩니다.

try:
    perform_operation()
except Unable_to_perform:
    handle_error_case()

이것은 작업을 수행하려는 시도가 실패 할 것으로 예상되는 상황이며 어떤 식 으로든 작업이 불가능한 상황을 처리해야하는 상황입니다. 예를 들어 작업이 파일에 액세스하는 경우 파일이 없을 수 있습니다.

용서를 구하는 것이 더 좋은 이유는 크게 두 가지입니다.

  • 동시 세계 (다중 스레드 프로그램에서 또는 작업에 파일, 기타 프로세스, 네트워크 리소스 등과 같은 프로그램 외부의 개체가 포함 된 경우)에서 상황은 실행 can_do_operation()시간과 다음 시간 사이에 변경 될 수 있습니다. 당신은 실행합니다 perform_operation(). 따라서 어쨌든 오류를 처리해야합니다.
  • 허가를 요청하는 데 정확한 기준을 정확히 사용해야합니다. 잘못하면 수행 할 수있는 작업을 수행 할 수 없거나 결국 작업을 수행 할 수 없어서 오류가 발생합니다. 예를 들어 파일을 열기 전에 파일이 존재하는지 테스트하는 경우 파일이 존재하지만 권한이 없기 때문에 열 수 없습니다. 반대로 파일을 열 때 생성 될 수 있습니다 (예를 들어 파일이 있는지 확인하기 위해 찌르는 경우가 아니라 실제로 파일을 열 때만 실행되는 네트워크 연결을 통해 제공되기 때문에).

용서 요청 상황의 공통점은 작업을 수행하려고하고 작업이 실패 할 수 있다는 것을 알고 있다는 것입니다.

당신이 쓸 때 foo.bar, 존재 bar하지 않는 것은 일반적으로 객체의 실패로 간주되지 않습니다 foo. 일반적으로 프로그래머 오류입니다. 설계되지 않은 방식으로 객체를 사용하려고합니다. 파이썬에서 프로그래머 오류의 결과는 처리되지 않은 예외입니다 (운이 좋다면 : 물론 일부 프로그래머 오류는 자동으로 감지 할 수 없습니다). 따라서 bar객체의 선택적 부분 인 경우이 문제를 처리하는 일반적인 방법은로 bar초기화 필드 를 가지고 None선택적 부분이있는 경우 다른 값으로 설정하는 것입니다. bar존재 여부를 테스트하려면

if foo.bar is not None:
     handle_optional_part(foo.bar)
else:
     default_handling()

부울로 해석 될 때 항상 true 인 경우 에만 축약 if foo.bar is not None:수 있습니다. 0 , 또는 거짓 진리 값을 가진 다른 객체 가 될 수 있으면 . 옵션 부분을 테스트하는 경우에도 더 명확합니다 ( 사이의 테스트와 반대 ).if foo.bar:barbar[]{}is not NoneTrueFalse

이 시점에서 질문 할 수 있습니다. 초기화가 bar없을 때 초기화를 생략하고 처리기로 존재 여부를 테스트 hasattr하거나 잡으십시오 AttributeError. 코드는 두 가지 경우에만 의미가 있기 때문입니다.

  • 개체에 bar필드 가 없습니다 .
  • 객체에는 bar의미하는 바를 의미 하는 필드가 있습니다.

So when writing or deciding to use the object, you need to make sure that it doesn't have a bar field with a different meaning. If you need to use some different object that has no bar field, that's probably not the only thing you'll need to adapt, so you'll probably want to make a derived class or encapsulate the object in another one.


You're right -- the purpose of try and except are not to cover your sloppy coding. That just leads to more sloppy coding.

Exception handling should be used to handle exceptional circumstances (sloppy coding is not an exceptional circumstance). However, often, it is easy to predict which exceptional circumstances might happen. (e.g. a your program accepts user input and uses that to access a dictionary, but the user's input wasn't a key in the dictionary ...)


There's lots of good answers here, I just thought I would add a point I have not seen mentioned so far.

Often asking for forgiveness instead of permission has performance improvements.

  • When you ask for permission, you have to perform an extra operation to ask for permission every time.
  • When asking for forgiveness, you only have to perform an extra operation sometimes, i.e. when it fails.

Usually the failure cases are rare, which means if you are only asking for permission, then you hardly ever have to do any extra operations. Yes, when it fails it throws an exception, as well as performing an extra operation, but exceptions in python are very fast. You can see some timings here: https://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/


My personal non-religious opinion is that said mantra applies mainly to documented and well understood exit conditions and edge cases (e.g. I/O errors), and should never be used as a get-out-of-jail card for sloppy programming.

That said, try/except is often used when "better" alternatives exist. For example:

# Do this    
value = my_dict.get("key", None)

# instead of this
try:
  value = my_dict["key"]
except KeyError:
  value = None

As for your example, do use if hasattr(foo, "bar") if your have no control over foo and need to check conformance with your expectations, otherwise simply use foo.bar and let the resulting error be your guide to identifying and fixing sloppy code.


In the Python context "Ask forgiveness not permission" implies a style of programming where you don't check for things to be as you expect beforhand, but rather you handle the errors that result if they are not. The classical example is not to check that a dictionary contains a given key as in:

d = {}
k = "k"
if k in d.keys():
  print d[k]
else:
  print "key \"" + k + "\" missing"

But rather to handle the resulting error if the key is missing:

d = {}
k = "k"
try:
  print d[k]
except KeyError:
  print "key \"" + k + "\" missing"

However the point is not to replace every if in your code with a try/except; that would make your code decidedly messier. Instead you should only catch errors where you really can do something about them. Ideally this would reduce the amount of overall error handling in your code, making its actual purpose more evident.


While there's already a number of high quality answers, most primarily discuss this from a stylistic stand point, as appose to a functional one.

There are certain cases where we need to ask forgiveness, not permission to insure correct code (outside of a multithreaded programs).

A canonical example being,

if file_exists: 
    open_it()

In this example, the file could have been deleted between the check and trying to actually open the file. This is avoided by using try:

try:
    open_it()
except FileNotFoundException:
    pass # file doesn't exist 

This crops up in a variety of places, often working with filesystems or external APIs.


Ask forgiveness not permission is meant to simplify code. It's meant for code to be written like this when there's a reasonable expectation that .bar might trigger an AttributeError.

 try:
     print foo.bar
 except AttributeError as e
     print "No Foo!" 

Your code appears to both ask permission AND forgiveness :)

The thing is, if you reasonably expect something to fail, use a try/catch. If you don't expect something to fail, and it fails anyway, well the exception that gets thrown becomes the equivalent of a failed assertion in other languages. You see where the unexpected exception is occurring and adjust your code/assumptions accordingly.

ReferenceURL : https://stackoverflow.com/questions/12265451/ask-forgiveness-not-permission-explain

반응형