파이썬(python) 함수 및 스코핑룰(scoping)
함수에 대한 내용이 제어문 다음에 왔어야 했는데, 뜬금없이 파일 입출력에 대한 글을 써버렸네요. 아무튼 이 강의는 애초에 최소 C언어에 대한 기초가 있는 분들을 위한 강의지만, 처음 프로그래밍을 접하는 분들에게도 도움되는 내용이 있을 거라 생각됩니다. 그냥 몰라도 한번 따라 하시면 됩니다. 프로그래밍은 처음에는 머리굴려 생각하는 것 보다 몸으로 체득하는 것이 중요합니다. 다시 본론으로 들어가서 파이썬의 함수나 C/C++에서의 함수나 거기서 거기입니다. C/C++과 약간 차이가 나는 부분만 정리하고 넘어가면 되는 정도, 파이썬이 첫 프로그래밍 언어라면 그냥 그대로 익히시면 됩니다. 그리고 스코핑 룰이라 주제를 첨가했는데, C++의 이름공간과 전역 변수 지역변수의 비슷한 개념이라고 생각하시면 됩니다. 함수와 관련된 내용이라 첨가했습니다.
파이썬으로 간단한 함수 만들어보기
C/C++언어와 차이나는 점은 파이썬은 동적 타입이기 때문에 인수에 타입명이 없다는 것 입니다. 또한 함수선언을 한 후 콜론(:)으로 함수선언의 끝임을 나타냅니다.
C/C++언어의 경우
파이썬의 경우는 a 값에 모든 타입이 들어갈 수 있습니다. 심지어 함수명도 들어갈 수 있습니다.
함수이름은 그대로 함수포인터가 됩니다. C/C++의 경우는 함수포인터에 관한 문법이 쉽지는 않은데, 파이썬은 이것을 그냥 씹어 먹고 있습니다. 매우 간단하네요.
파이썬은 언어적 차원에서 포인터가 없습니다. 따라서 함수명은 함수 레퍼런스가 되어야 겠죠. 함수의 실체(함수객체)는 메모리의 어딘가에 있고 함수 명으로 실체에 접근 하는 겁니다.
이런 식으로 대입하여 temp 또한 함수 레퍼런스가 되어 func 함수명 대신 temp로 사용이 가능합니다. 아래 그림처럼 globals()를 실행하면 현재 전역지역에 생성되어 있는 객체들을 볼 수 있습니다.
레퍼런스 temp와 func 가 가리키는 함수객체의 주소가 동일함을 알 수 있습니다.
내장함수
위에서 잠깐 나온 globals()는 내장함수 입니다. 위에 globals()를 실행시키면 위와 같이 사전 형식으로 전역 변수들이 사전형식으로 표시가 됩니다.
'__builtins__' : <module '__builtin__' (built-in)> 이라고 된 부분이 보일 겁니다. __builtins__는 내장함수 리스트를 가진 변수인데
>>>dir(__builtins__)
위와 같이 하면 내장함수를 리스트형으로 확인할 수 있습니다.
>>>dir()이라고 하면 현재 지역의 객체를 리스트 형식으로 보여줍니다.
dir()을 전역에서 그리고 함수 내에서 실행해 보면
전역에서 실행한 경우 : 전역변수들이 보이는데 내장객체들을 저장한 '__builtins__'도 전역 지역에 있습니다. dir()이라고 치면 나오는 객체들은 일종의 폴더라고 생각하면 쉽게 이해할 수 있습니다. 이 폴더 내부에 객체들(함수나 변수)가 있다고 생각하 보길 바랍니다. 이 폴더의 내부를 보고 싶다면 dir(폴더)라고 하면 되겠죠. 비유해서 설명한 것이니 진짜 폴더라고 하지는 말아주세요.(리스트입니다)
아무튼 globals()나 locals()와 dir()은 사실 근본적으로는 같은 정보를 보여주는데, 단지 보여주는 형식만 사전형식과, 리스트형식으로 틀린 것입니다.
스코핑(scope) 룰
스코핑 룰은 간단합니다. "LGB" 순서라고 외워두세요. 변수를 찾는 우선순위를 지역->전역->내장 으로 한다는 뜻입니다. C/C++의 경우와 비슷합니다만 ...
예들 들면
위 경우는 전역으로 a=3을 선택했고 test함수 내부에서 a를 리턴 합니다. test 함수 내부 지역 객체 a가 없으므로 전역으로 넘어가서 찾게 됩니다. 전역에서 찾았으므로 3이 출력됩니다. 여기서 test함수를 재선언한 후 내부에 a=5를 선언합니다. 내부 지역 객체에 a가 있으므로 결과는 5를 출력합니다. 전역에 있는 a=3과 전혀 다릅니다.
만약 함수 내에서 외부 객체를 사용하고 싶다면
global a
a=5
이런식으로 사용하면 외부 객체를 사용하게 됩니다. (C/C++에서는 외부 변수를 사용하고 싶다면 포인터를 사용하는데 말이죠.)
다음은 또 다른 예입니다.
위에서 globals() 가 내장함수임을 알았습니다. 이 또한 함수이므로 globals는 하나의 레퍼런스 객체입니다. 실수로 globals라는 이름의 변수로 4를 대입했다고 치고, 내장 함수 globals()를 사용하려고 하면 에러가 납니다. 현 지역(전역) 객체로 globals=4가 정의 되었으므로 내장함수를 체크하기전에 전역에 globals가 있음을 발견합니다. 전역에 정의된 globals는 함수가 아니므로 에러를 냅니다. del로 현 지역(전역)의 globals 객체를 삭제한 후 다시 함수를 호출 했습니다. 제대로 호출 되는군요. 한가지 더 예를 들어보죠.
이번에는 함수를 정의했습니다. 내장함수와 동일한 함수명이 전역에 있다면 내장 함수는 사용이 안됩니다.