Python 에서 return 과 finally 가 공동으로 파 낸 구 덩이 를 분석 합 니 다.
본 고 는 주로 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
결 과 는 모두 첫 번 째 문법 과 같다!이렇게 쓰 는 동 화 를 처음 봤 을 때 받 아들 이기 어 려 울 수도 있 고 가 독성 이 떨어진다 고 생각 할 수도 있 지만 사실은 이런 쓰 기 는 오히려 조금 나 을 것 같 습 니 다.왜냐하면:더 쉽게 이해 하기 위해 코드 예 를 들 어:
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 을 설정 합 니 다:이것 이 밝 혀 지면 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 의 값 에 따라 대응 하 는 작업 을 수행 하 는 것 이기 때 문 입 니 다!그래서 정말 실질 적 인 귀환 은 아니 라 고 할 수 있 습 니 다.우리 가 나중에 다시 사용 할 때 더 이상 구덩이 에 빠 지지 않 았 으 면 좋 겠 습 니 다!
자,이상 이 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
로마 숫자를 정수로 또는 그 반대로 변환그 중 하나는 로마 숫자를 정수로 변환하는 함수를 만드는 것이었고 두 번째는 그 반대를 수행하는 함수를 만드는 것이었습니다. 문자만 포함합니다'I', 'V', 'X', 'L', 'C', 'D', 'M' ; 문자열이 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.