Backend/Python + FastAPI

Python Basics (1)

elevne 2022. 11. 22. 22:36

Python 변수

Python에서느 변수의 유효 범위를 게산할 때 Namespace를 기반으로 계산한다. Python의 Namespace는 built-in, global, enclosed, local 4 개로 나눌 수 있다.


built-in 은 Python에 내장되어 있는 Namespace로 Pythonㅇ로 작성된 코드 어디에서나 이용이 가능하다.
global 은 전역적으로 사용 가능한 변수로 파일 단위의 모듈 안에서 유효하다.
enclosed 는 함수 안에 함수가 있는 구조에서 내부 함수가 외부 함수의 변수에 접근할 수 있는 것을 의미한다.
local 은 지역적인 Namespace로 Class, function의 내부로 한정된다.



Python 의 Class, function 내에서 외부의 변수를 사용하기 위해서 'global'을 사용할 수 있다. 아래 코드처럼 사용할 수 있다.


txt = "Hello"

def testGlobal():
    global txt
    txt += " World"
    print(txt)

if __name__ == "__main__":
    testGlobal()




또 위에서 enclosed, 함수 내의 함수가 외부 함수의 변수에 접근하고자 할 때는 'nonlocal'을 사용할 수 있다.


def testNonlocal(a):
    txt = ""

    def addTxt():
        nonlocal txt
        return txt + a
    
    txt = addTxt()
    print(txt)

if __name__ == "__main__":
    testNonlocal("Hello World")





First Class Citizen

First Class Citizen이란 한국어로는 '일급 객체'라고 표현되며 first-class citizen 속성을 가진다는 것은 어떤 객체를 다른 개체의 매개변수로 전달하거나, 함수의 반환값으로 사용하거나, 변수에 값으로 하당할 수 있다는 것을 의미한다. 어떠한 함수가 first-class citizen 속성을 가지면 이는 first-class 함수라고 부른다. Python에서 지원하는 속성이 바로 이 first-class 함수라고 한다. Javascript에서 callback 함수를 사용하는 것을 떠올려보았다. 아래와 같은 코드로 함수를 불러와서 사용할 수 있는 것이다.


def square(x):
    return x*x

def main():
    f = square
    print(f(2))

def main2(func):
    print(func(3))

if __name__ == "__main__":
    main()
    main2(square)




위 코드의 결과값으로 4, 9가 차례대로 print 될 것이다.



Nested Function

Nested Function은 위의 nonlocal 키워드를 사용하였을 때 작성해본 중첩된 형태의 함수를 뜻한다. 좀 더 자세히는 Nested Function은 함수로 감싸인 함수를 뜻하는데 함수 안에 정의된 함수는 Inner Function, 그리고 이 함수를 감싸고 있는 외부 함수를 Outer Function이라고 한다. Inner Function을 사용하면 Outer Function의 자원을 이용할 수 있다는 장점이 있다. 그래서 같은 매개변수를 사용하고 연관된 기능을 하나의 Outer Function에 묶어서 사용하는 경우가 있다. 하지만, Outer Function에서 Inner Function 내부에 접근할 수는 없다.



조금 더 나아가서 Closure 이라는 것에 대해 알아볼 수 있다. Closure은 중첩 함수의 일종으로 함수와 함수가 사용하는 환경(nonlocal)을 저장하는 것이다. Closure은 함수의 반환값으로 내부 함수를 사용하는 함수라고도 정의할 수 있다.



*참고: Python의 dir() 함수를 사용하면 개체의 속성을 조회할 수 있다.


def square(x):
    return x*x

if __name__ == "__main__":
    print(dir(square))


dir()




그렇다면 Closure은 언제 사용하는 것일까? 다음과 같은 상황에서 사용될 수 있다고 한다.


1. global 변수를 사용하고 싶지 않을 때 (nonlocal 변수가 대체)
2. Class를 사용하지 않기 위해서
3. Decorator을 사용하기 위해서 (간단하게는 어떤 함수를 실행하기 전이나 실행하고 난 뒤에 특정 기능을 수행하기 위한 기능)



Partial Application

Partial Application이란 가변적인 매개변수의 일부를 미리 전달해서 Wrapping 함수를 만들고, Wrapping 된 함수를 사용하여 가변적인 매개변수만 매개벼수로 사용하는 기법을 뜻한다.



*args, **kwargs

가변 매개변수를 사용할 때 위 매개변수들을 사용하게 되는데, 사실 중요한 부분은 '*'이고 args, kwargs는 다른 말로 대체되어도 상관없다. *args는 non-keyword 가변 인자를 다루고 **kwargs는 keyworded 가변 인자를 다룬다. 쉽게 설명하자면 *args에는 리스트와 같은 것이 들어가는 것이고, **kwargs에는 Dictionary와 같은 것이 들어가는 것이다.



Python의 내장함수 partial(functools 라이브러리)을 사용하여 Partial Application을 사용할 수 있다. 아래 코드와 같이 작성할 수 있는 것이다.


from functools import partial

def printTxt(year, month, day, title, content):
    print("{}-{}-{} {}:{}".format(year, month, day, title, content))

def main():
    f = partial(printTxt, '2022', '11', '22')
    f("Python", "Basics")
    f("AI", "NLP")

if __name__ == "__main__":
    main()


partial