Python 에서 return 과 finally 가 공동으로 파 낸 구 덩이 를 분석 합 니 다.

10500 단어 pythonreturnfinally
머리말
본 고 는 주로 Python 에서 return 과 finally 가 공동으로 존재 하 는 구덩이 와 구덩이 메 우기 경험 을 소개 하고 참고 학습 을 제공 합 니 다.다음은 더 이상 말 하지 않 겠 습 니 다.상세 한 소 개 를 해 보 겠 습 니 다.
초 면 귀환
Python 함 수 를 사용 한 모든 어린이 신발 은 return 문 구 를 사용 한 적 이 있 을 것 이 라 고 믿 습 니 다.return 은 말 그대로 호출 자 에 게 값 을 되 돌려 주 는 것 입 니 다.예 를 들 어:

def test():
 a = 2
 return a

s = test()
print s

#     
2
위의 결과 에 대해 서 는 모두 가 의외 라 고 생각 하지 않 을 것 이 라 고 믿 습 니 다.그러면 좀 더 어렵 습 니 다.return 구문 에 코드 가 있다 면?그 코드 는 어떻게 될까요?

def test():
 a = 2
 return a
 s = 3
 print s

s = test()
print s

#      ?
나이 든 운전 자 는 결 과 를 한눈 에 알 수 있 을 것 입 니 다.하지만 아직 입문 중이 거나 return 에 대해 잘 모 르 는 어린이 신발 에 대해 서 는 어 리 석 을 수 있 습 니 다.뒤의 코드 두 마디 가 실 행 될 수 있 습 니까?
정 답 은 실행 하지 않 는 다 는 것 이다.
return 은 이름 처럼 이 코드 를 실행 하면 전체 함수 가 되 돌아 갑 니 다.전체 호출 은 끝 났 더 라 도 return 뒤에 있 는 코드 는 실행 되 지 않 습 니 다!
바로 이 특성 때문에 early return 이라는 인 코딩 규범 이 제창 되 었 다.
조건 이 충족 되 었 을 때 바로 돌아 오 라 는 뜻 이다.
예 를 들 어 설명 하 다.

def test():
 a = 2
 if a > 2:
  result = 'more than'
 else:
  result = 'less than'
 return result

s = test()
print s
위의 코드 는 쉽게 이해 할 수 있어 야 합 니 다.바로 a 의 값 에 따라 돌아 오 는 result 가 무엇 인지 결정 하 는 것 입 니 다.이러한 인 코딩 도 대부분의 어린이 신발 이 즐겨 사용 하 는 것 이 라 고 믿 습 니 다.왜냐하면 이것 은 우리 의 직관 에 비교적 부합 되 기 때 문 입 니 다.그러나 이렇게 쓰 는 것 은 낭비 하 는 것 같 습 니 다.첫 번 째 판단 이 끝 났 을 때 결과 가 사실 이 라면 more than 으로 돌아 가서 함 수 를 끝내 야 합 니 다.그렇지 않 으 면 less than 으로 돌아 갈 것 입 니 다.그래서 우 리 는 코드 를 이렇게 조정 할 수 있 습 니 다.

def test():
 a = 2
 if a > 2:
  return 'more than'
 else:
  return 'less than'

s = test()
print s
심지어:

def test():
 a = 2
 if a > 2:
  return 'more than'
 return 'less than'

s = test()
print s
결 과 는 모두 첫 번 째 문법 과 같다!이렇게 쓰 는 동 화 를 처음 봤 을 때 받 아들 이기 어 려 울 수도 있 고 가 독성 이 떨어진다 고 생각 할 수도 있 지만 사실은 이런 쓰 기 는 오히려 조금 나 을 것 같 습 니 다.왜냐하면:
  • 실 행 된 코드 수가 적어 야 호출 이 더 빨리 결 과 를 얻 을 수 있 습 니 다
  • 끼 워 넣 은 층 수 를 줄 이 고 이해 하기 쉽다.
  • 두 번 째 점 에 대해 서 는 설명 이 필요 합 니 다.코드 가 깊 게 새 겨 진 경우 가 많 습 니 다.모두 if/else 의 냄비 때 문 입 니 다.새 겨 진 if/else 가 많 기 때문에 코드 가 깊 게 새 겨 져 있 습 니 다.그러면 다른 동료 들 에 게 는 재앙 입 니 다.이 코드 를 읽 을 때 앞의 논 리 를 잊 어 버 릴 수 있 기 때 문 입 니 다.
    더 쉽게 이해 하기 위해 코드 예 를 들 어:
    
    def test():
     a = 2
     if a > 2:
      result = 'not 2'
     else:
      a += 2
      if a < 2:
       result = 'not 2'
      else:
       for i in range(2):
        print 'test ~'
       result = 'Target !'
     return result
    
    s = test()
    print s
    
    #     
    test ~
    test ~
    Target !
    코드 간소화 최적화 판:
    
    def test():
     a = 2
     if a > 2:
      return 'not 2'
     
     a += 2
     if a < 2:
      return 'not 2'
     
     for i in range(2):
      print 'test ~'
    
     return 'Target !'
    
    s = test()
    print s
    
    #     
    test ~
    test ~
    Target !
    이렇게 비교 해 보면 왜 early return 이 포 함 된 층 수 를 줄 일 수 있 는 지 잘 이해 할 수 있 을 것 같 습 니 다.의문 이 있 으 시 면 댓 글 토론 을 환영 합 니 다~
    깊 은 구덩이 에 대해 이야기 하 다.
    방금 비교적 긴 지면 을 써 서 return 을 소 개 했 습 니 다.여 기 를 보면 return 에 대해 비교적 기본 적 인 이 해 를 가 질 것 이 라 고 믿 습 니 다!그래서 더욱 혼 란 스 러 운 이 야 기 를 나 누 러 왔 다.
    return 이 try.finally 를 만나면 어떻게 될까요?
    방금 진지 하 게 봤 다 면 한 마디 가 눈 에 띄 었 을 것 이다.바로:
    return 은 전체 함수 가 되 돌아 오 는 것 을 의미 합 니 다.함수 호출 계산 이 끝 났 습 니 다.
    그런데 정말 그런 가요?보통 이렇게 물 어 봐 요.답 은 보통 아니에요~
    먼저 예 를 들 어보 자.
    
    def test():
     try:
      a = 2
      return a
     except:
      pass
    
     finally:
      print 'finally'
    
    s = test()
    print s
    이 print a 가 인쇄 되 는 지 맞 춰 볼 수 있 을까요?많은 어린이 신발 을 믿 고 잠시 생각 한 후에 아니 라 고 말 했 습 니 다.그러나 이 답 은 틀 렸 습 니 다.진정한 수출 은:
    
    finally
    2
    신대륙 을 본 것 처럼 느껴 지 는 나무 가 있 습 니 다.처음에 return 뒤의 문 구 는 실행 되 지 않 았 습 니 다.그러나 여기 서 그렇게 멀리 떨어져 있 지만 잊 지 않 았 습 니 다.이것 이 바로'사랑'일 수도 있 습 니 다!
    그런데 이런'사랑'때문에 자꾸 새 운전 기사 들 이 구덩이 에 빠 져 요.그리고 털 인지 모 르 겠 어 요.
    그들 이 다시'사랑'이라는 간판 을 빌려 우 리 를 괴 롭 히 는 것 을 피하 기 위해 서 우 리 는 함께 이'사랑'의 정 체 를 밝 혀 봅 시다!
    그래서 우 리 는 신 기 를 훔 쳐 봐 야 한다.dis,생각 만 해도 약간 흥분 된다!
    
    import dis
    def test():
     try:
      a = 2
      return a
     except:
      pass
    
     finally:
      print 'finally'
    
    print dis.dis(test)
    출력 이 길 고 단독으로 쓰기:
    
    #     
     6   0 SETUP_FINALLY   28 (to 31)
        3 SETUP_EXCEPT   14 (to 20)
    
     7   6 LOAD_CONST    1 (2)
        9 STORE_FAST    0 (a)
    
     8   12 LOAD_FAST    0 (a)
        15 RETURN_VALUE  
        16 POP_BLOCK   
        17 JUMP_FORWARD    7 (to 27)
    
     9  >> 20 POP_TOP    
        21 POP_TOP    
        22 POP_TOP   
    
     10   23 JUMP_FORWARD    1 (to 27)
        26 END_FINALLY   
      >> 27 POP_BLOCK   
        28 LOAD_CONST    0 (None)
    
     13  >> 31 LOAD_CONST    2 ('finally')
        34 PRINT_ITEM   
        35 PRINT_NEWLINE  
        36 END_FINALLY   
        37 LOAD_CONST    0 (None)
        40 RETURN_VALUE 
    여기 서 간단하게 이 열 들 이 대표 하 는 뜻 을 말 하고 있다.
    1.첫 번 째 열 은 코드 가 파일 의 줄 번호 에 있 습 니 다.
    2.두 번 째 바이트 의 오프셋
    3.바이트 이름
    4.매개 변수
    5.바이트 코드 처리 매개 변수 최종 결과
    바이트 에서 볼 수 있 습 니 다.다음은 SETUP 입 니 다.FINALLY 와 SETUPEXCEPT,이것 에 대응 하 는 것 은 finally 와 try 입 니 다.finally 는 try 뒤에 있 지만 우 리 는 보통 그들 을 하나의 전체 로 보지 만 그들 은 실제로 헤 어 집 니 다.우 리 는 finally 에 중점 을 두 기 때문에 SETUP 만 봅 니 다.FINALLY
    
    // ceval.c
    TARGET(SETUP_FINALLY)
      _setup_finally:
      {
       /* NOTE: If you add any new block-setup opcodes that
        are not try/except/finally handlers, you may need
        to update the PyGen_NeedsFinalizing() function.
        */
    
       PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
            STACK_LEVEL());
       DISPATCH();
      }
    
    
    // fameobject.c
    void
    PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level)
    {
     PyTryBlock *b;
     if (f->f_iblock >= CO_MAXBLOCKS)
      Py_FatalError("XXX block stack overflow");
     b = &f->f_blockstack[f->f_iblock++];
     b->b_type = type;
     b->b_level = level;
     b->b_handler = handler;
    }
    위의 코드 에서 분명히 알 수 있 습 니 다.SETUPFINALLY 는 바로 PyFrame 을 호출 하 는 것 입 니 다.BlockSetup 에서 Block 을 만 들 고 이 Block 을 설정 합 니 다:
  • b_type(opcode 즉 SETUPFINALLY)
  • b_level
  • b_handler (INSTR_OFFSET() + oparg)
  • handler 는 이해 하기 어 려 울 수 있 습 니 다.사실 아까 dis 출력 을 보면 어느 것 인지 볼 수 있 습 니 다.바로 13>>31 LOAD 입 니 다.CONST 2('finally'),이 화살 표 는 바로 우리 에 게 뛰 는 위 치 를 알려 주 는 것 인 데 왜 이 문장 으로 뛰 었 을 까?왜냐하면 6 0 SETUPFINALLY 28(to 31)님 께 서 31 로 넘 어 가 겠 다 고 말씀 해 주 셨 어 요.
    이것 이 밝 혀 지면 return 을 계속 보 세 요.return 에 대응 하 는 바이트 번 호 는:RETURN 입 니 다.VALUE,그래서 대응 하 는 소스 코드 는:
    
    // ceval.c
    TARGET_NOARG(RETURN_VALUE)
      {
       retval = POP();
       why = WHY_RETURN;
       goto fast_block_end;
      }
    원래 우리 가 예전 에 이 해 했 던 return 은 가짜 return 이 었 구나!이 return 은 바로 돌아 오지 않 고 스 택 의 값 을 꺼 내 retval 을 부여 한 다음 why 를 WHY 로 설정 합 니 다.리 턴,이어서 도 망 갔 어!패스 트 라 는 이름 으로 달 려 가기block_end;어 쩔 수 없 이 정 체 를 밝 히 기 위해 땅 을 석 자 나 팠 습 니 다.
    
    while (why != WHY_NOT && f->f_iblock > 0) {
       fast_block_end:
      while (why != WHY_NOT && f->f_iblock > 0) {
       /* Peek at the current block. */
       PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1];
    
       assert(why != WHY_YIELD);
       if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) {
        why = WHY_NOT;
        JUMPTO(PyInt_AS_LONG(retval));
        Py_DECREF(retval);
        break;
       }
    
       /* Now we have to pop the block. */
       f->f_iblock--;
    
       while (STACK_LEVEL() > b->b_level) {
        v = POP();
        Py_XDECREF(v);
       }
       if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
        why = WHY_NOT;
        JUMPTO(b->b_handler);
        break;
       }
       if (b->b_type == SETUP_FINALLY ||
        (b->b_type == SETUP_EXCEPT &&
         why == WHY_EXCEPTION) ||
        b->b_type == SETUP_WITH) {
        if (why == WHY_EXCEPTION) {
         PyObject *exc, *val, *tb;
         PyErr_Fetch(&exc, &val, &tb);
         if (val == NULL) {
          val = Py_None;
          Py_INCREF(val);
         }
         /* Make the raw exception data
          available to the handler,
          so a program can emulate the
          Python main loop. Don't do
          this for 'finally'. */
         if (b->b_type == SETUP_EXCEPT ||
          b->b_type == SETUP_WITH) {
          PyErr_NormalizeException(
           &exc, &val, &tb);
          set_exc_info(tstate,
              exc, val, tb);
         }
         if (tb == NULL) {
          Py_INCREF(Py_None);
          PUSH(Py_None);
         } else
          PUSH(tb);
         PUSH(val);
         PUSH(exc);
        }
        else {
         if (why & (WHY_RETURN | WHY_CONTINUE))
          PUSH(retval);
         v = PyInt_FromLong((long)why);
         PUSH(v);
        }
        why = WHY_NOT;
        JUMPTO(b->b_handler);
        break;
       }
      } /* unwind stack */
    아까 의 지식 을 되 돌아 볼 필요 가 있 습 니 다.방금 return 코드 를 봤 는데 why 를 WHY 로 설정 한 것 을 보 았 습 니 다.RETURN,그래서 이렇게 큰 판단 에서 그것 은 맨 뒤의 else 만 갔 을 뿐 동작 도 간단 합 니 다.바로 방금 return 에 저 장 된 값 retval 을 다시 push 하여 스 택 에 되 돌려 주 는 동시에 why 를 log 로 바 꾸 어 다시 스 택 에 누 른 다음 에 why 를 설 치 했 습 니 다.그 다음 에 궁 뎅 이 를 해서 방금 SETUP 를 집 행 했 습 니 다.FINALLY 가 설정 한 bhandler 코드 입 니 다~이 bhhandler 코드 가 실행 되면 END 를 통 해FINALLY 는 해 야 할 일 을 하고,여기 가 바로 return retval 입 니 다.
    결론.
    그래서 우 리 는 왜 return 코드 를 실 행 했 는 지,왜 finally 코드 가 먼저 실 행 했 는 지 알 수 있 을 것 입 니 다.return 의 본질 은 why 와 retval 을 설정 한 다음 에 goto 에서 큰 판단 을 한 다음 에 why 의 값 에 따라 대응 하 는 작업 을 수행 하 는 것 이기 때 문 입 니 다!그래서 정말 실질 적 인 귀환 은 아니 라 고 할 수 있 습 니 다.우리 가 나중에 다시 사용 할 때 더 이상 구덩이 에 빠 지지 않 았 으 면 좋 겠 습 니 다!
    자,이상 이 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기