CS 지식
[Python] Mangling / Args인자 / Decorator 에 대해
도리컴
2024. 1. 25. 02:15
반응형
목표
- mangling, 가변인자, decorator의 정의와 왜 필요한 지를 알고, 어디에 어떻게 쓰는지를 알아보자!
- python 코드를 보다 위 3가지가 나왔을 때, 당황하지 않고 읽어 넘기기
목차
- 1.mangling
- 2.가변인자, 키워드 가변인자
- 3.decorator
1. Mangling
정의
- 뭉개다 -> 특정 변수/함수를 뭉갬 -> 외부 코드에서 발견할 수 없도록 하기 위함
- 클래스 내부의 변수/함수명 앞에 언더스코어(_)를 두 개 붙여 적용
- 맹글링된 변수/함수는 본연의 이름으로 접근 불가(_클래스명__속성명 으로 접근 가능)
- 외부의 접근을 조금 어렵게 할 뿐, 완벽히 private이 되는 건 아님
필요한 이유
- 외부에서의 접근을 어느정도 막기 위해
- 오버라이딩을 차단하기 위해(종종 하위클래스가 상위클래스의 속성을 오버라이딩 하려는 경우를 방지하려는 목적도 있음)
In [ ]:
#사용 예(외부에서의 접근 차단)
class TestClass:
def __init__(self): self.name = "왕춘삼"
self.age = 30
self.__hobby = "인형놀이" #hobby를 숨김 - private의 특성을 어느정도 띄게 됨 / _클래스명__속성명 으로 접근 가능
man = TestClass() print(man.name, man.age, man.__hobby) #에러 : hobby를 못찾음
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-29-4b6ad34ef023> in <cell line: 10>()
8
9 man = TestClass()
---> 10 print(man.name, man.age, man.__hobby) #- 에러 : hobby를 못찾음
AttributeError: 'TestClass' object has no attribute '__hobby'
In [ ]:
print(dir(man)) # man의 모든 속성을 출력(dir함수 : 객체의 모든 속성 리스트 반환)
['_TestClass__hobby', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
In [ ]:
man._TestClass__hobby #이렇게 접근할 수 있음 / 형식 : _클래스명__속성명
Out[ ]:
'인형놀이'
In [ ]:
#사용 예 2(의도치 않은 오버라이딩 방지) - 자식이 의도치않게 부모의 속성을 오버라이딩 하는 것 방지
class Parent: def __init__(self):
self._protected_attribute = 10
class Child(Parent):
def __init__(self):
super().__init__()
self._protected_attribute = 20 # 상위 클래스의 속성을 오버라이딩(서로 이름이 같아버려서)
obj = Child()
print(obj._protected_attribute) # - 난 부모껄 참조하고 싶은뎁...
20
In [ ]:
class Parent:
def __init__(self):
self.__protected_attribute = 10
class Child(Parent):
def __init__(self):
super().__init__()
self.__protected_attribute = 20 # 상위 클래스의 속성을 오버라이딩(서로 이름이 같아버려서)
obj = Child()
print(obj._Parent__protected_attribute) # - 부모꺼 참조 가능
10
2. 가변인자, 키워드 가변인자
정의
- ""(가변인자), "*"(키워드 가변인자) - 함수에 유동적으로 파라미터를 전달할 때 사용
- 일반적으로 args, kwargs라고 부름
- 가변인자는 튜플 / 키워드 가변인자는 '키워드=값'(딕셔너리) 형태로 값 전달
- args가 kwargs보다 항상 먼저 -> 함수호출의 가독성과 명확성을 높이기 위한 파이썬의 문법 규칙
필요한 이유
- 함수 호출 시 인자의 개수를 동적으로 조절할 수 있어 편리하고, 코드의 유연성과 확장성을 높여줌
- 대표적인 예 : print문
주의
- 가변인자 남발 시 코드의 가독성이 오히려 떨어지고 함수의 동작 파악이 어려울 수 있음
In [ ]:
#가변인자 : *
def print_numbers(*args):
print(args)
for arg in args:
print(arg)
#파라미터의 수가 유동적 - 너무 꿀
print_numbers(10)
print_numbers(1, 2, 3, 4)
(10,)
10
(1, 2, 3, 4)
1
2
3
4
- 가변 인자 : 튜플
- 키워드 가변인자 : '키워드=값'(딕셔너리)
In [2]:
#키워드 가변인자 : **
def personal_info(age, name, address):
print('이름: ', name)
print('나이: ', age)
print('주소: ', address)
x = {'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'}
personal_info(**x) #정석은 이렇게 - 각 value값들이 key값에 맞게끔 전달됨
이름: 홍길동
나이: 30
주소: 서울시 용산구 이촌동
3. Decorator
사전지식 : 클로저(closure)
- 외부 함수의 지역 변수를 참조하는 내부 함수를 클로저 함수라고 함.
- 내부 함수는 자신이 생성될 때의 환경, 즉 상위 함수의 지역 변수를 '기억'하고 있는 상태가 됨
- 내부 함수가 호출될 때마다, 내부 함수는 외부 함수의 지역 변수를 '참조'하여 동작을 수행
- 참고 : https://sungmin93.tistory.com/32
In [ ]:
#print1과 print2함수는 각각 자기가 어떤 값을 출력해야 하는지를 기억하고 있다.
def outer(x):
def inner():
print(x)
return inner
print1 = outer(1)
print2 = outer(2)
print(type(print1))
print1()
print2()
#출처: https://engineer-mole.tistory.com/181 [매일 꾸준히, 더 깊이:티스토리]
<class 'function'>
1
2
Decorator 정의
- 소스코드를 변경하지 않고 다른 함수/메서드의 동작을 수정할 수 있는 고차함수
- 적용할 함수 바로 위에 '@데코레이터_이름'을 붙여준다.
- 모든 Callable Object가 데코레이터가 될 수 있음(함수, 메서드, 변수 모두 가능)
필요한 이유
- 기존의 코드를 수정하지 않고도 여러가지 기능을 추가할 수 있음
- DRY(Don't Repeat Yourself) - 깔끔한 코드를 만들며 코드의 반복 줄임 -> 효율 굳
- 외부에서 특정 함수나 클래스에 개입하는 경우 사용
주의
- 함수의 서명(반환값, 매개변수, 함수 동작)을 바꿀 수 있기 때문에 신중한 사용 필요
- 서명이 변경되면 기존에 해당 함수를 사용하던 코드들이 동작하지 않을 수 있음
In [ ]:
#맛보기
def decorator_function(func):
def wrapper(*args, **kwargs):
print("이게 먼데")
return wrapper
class Mainclass:
@decorator_function # main_function은 decorator_function(main_function)으로 대체됨 -> wrapper함수로 대체
def main_function(self):
print("하이")
def main_function2(self):
print("하이하이")
print("MAIN FUNCTION START")
my = Mainclass()
my.main_function()
my.main_function2()
MAIN FUNCTION START
이게 먼데
하이하이
- "@decorator_function"이 위에 있으면, 그 함수는 '데코레이터 함수의 반환값'으로 대체
- 원래 함수와 파라미터는 데코레이터 함수로 넘겨짐
- 강의에서 나온 예제
In [ ]:
# 메시지 출력 위아래로 별찍기 - 꾸미기
def star(func):
def inner(*args):
print(args[1] * 30)
func(*args)
print(args[1] * 30)
return inner
@star # printer = star(printer) 이후 printer 실행
def printer(msg, mark):
print(msg)
printer("Hello", "*")
******************************
Hello
******************************
In [ ]:
#2의 49제곱
def generate_power(exponent): # exponent 값은 2 -> @generate_power(2) 이므로
def wrapper(f): # f는 raise_two함수
def inner(*args): # args는 raise_two의 파라미터인 7 -> 형태가 (7,)임
# unpacking을 위해 *을 붙여 *args
result = f(*args) # 7을 넣은 raise_two 결과값이 result에 저장
return exponent**result # 2^49 return
return inner
return wrapper
# @~(데코레이터)가 붙는 순간, raise_two호출마다
# generate_power 함수를 호출하고, 파라미터로 raise_two함수가 들어감
@generate_power(2) # raise_two = generate_power(2)(raise_two) 를 실질적으로 수행
def raise_two(n):
return n**2
print("2^49 = ", raise_two(7))
2**49
2^49 = 562949953421312
Out[ ]:
562949953421312
In [ ]:
# 함수를 추적하는 기능을 추가할 때 -> decorator 사용 예
def trace(func): # 호출할 함수를 매개변수로 받음
def wrapper():
print(func.__name__, '함수 시작') # __name__으로 함수 이름 출력 func() # 매개변수로 받은 함수를 호출
func()
print(func.__name__, '함수 끝') return wrapper # wrapper 함수 반환
@trace # 데코레이터를 사용해 hello에 추적 기능 추가
def hello():
print('hello')
@trace # 데코레이터를 사용해 world에 추적 기능 추가
def world():
print('world')
hello() # 함수를 그대로 호출
world() # 함수를 그대로 호출
hello 함수 시작
hello
hello 함수 끝
world 함수 시작
world
world 함수 끝
In [1]:
#데코레이터를 이용한 타이머 함수 -> 해당 함수의 실행시간을 기록하려고 함!
import time
def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time() # 시작 시간 측정
result = func(*args, **kwargs) # 원래 함수 실행
end_time = time.time() # 종료 시간 측정
print(f"{func.__name__} 함수의 실행 시간: {end_time - start_time}초")
return result
return wrapper
@timer_decorator # 데코레이터를 사용해 my_function에 타이머 기능 추가
def my_function(n):
total = 0
for i in range(n):
total += i
return total
result = my_function(1000000) # 함수 실행 및 실행 시간 출력
print(result)
# 출력
#my_function 함수의 실행 시간: 0.12572193145751953초
my_function 함수의 실행 시간: 0.09530282020568848초
499999500000
Decorator 참고:
https://m.blog.naver.com/devinfo_today/220759606738
https://dojang.io/mod/page/view.php?id=2427
https://bluese05.tistory.com/30
https://schoolofweb.net/blog/posts/%ed%8c%8c%ec%9d%b4%ec%8d%ac-%eb%8d%b0%ec%bd%94%eb%a0%88%ec%9d%b4%ed%84%b0-decorator/
https://m.blog.naver.com/devinfo_today/220762435465
https://ksrae.github.io/javascript/decorator/
https://whatisthenext.tistory.com/113
결론
- Mangling은 변수/함수를 뭉개서 다른이름으로 사용하게끔 하는 기법이다!
- 가변인자, 키워드 가변인자는 유동적인 개수의 값을 입력받을 수 있는 파라미터이다!
- 데코레이터는 원본함수의 수정 없이 특정 함수/변수/클래스의 부가적인 기능을 구현할 때 사용된다!
- 이해가 완벽히 되지 않았더라도, '줄건 준다는 마인드'를 장착하고 필요할 때 다시 찾아보기로 한다!
반응형