
정보 은닉(Information Hiding)은 객체지향 프로그래밍(OOP)의 중요한 개념 중 하나로 클래스 내부의 데이터(속성)와 구현 세부 사항을 외부에서 직접 접근하지 못하도록 숨기고 필요한 경우에만 제한된 방식으로 접근을 허용하는 것이다.
파이썬에서는 주로 변수 앞에 언더바( _ )를 사용해서 접근 수준을 표시한다.
1) public(공개)
:일반적으로 주로 사용하는 변수나 메서드처럼 이름 앞에 아무것도 붙이지 않은 경우
: 외부에서 자유롭게 접근 가능하다.
2) Protected (보호)
: 변수나 메서드 이름 앞에 단일 언더스코어( _ )를 붙이는 경우
: 외부에서 직접 접근은 가능하지만 내부적으로만 사용하라는 암묵적인 규칙이 있다.
3) Private (비공개)
: 변수나 메서드 이름 앞에 이중 언더스코어( __ )를 붙이는 경우
: Name Mangling(이름 변환)으로 외부에서 접근을 어렵게 만든다.
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
# if 문의 해당 함수에서 0이 들어 가지 않도록 막음
def add_age(self,age):
if age < 0:
print('나이는 0보다 커야 합니다. 나이 정보 오류')
else:
self.__age += age
def __str__(self):
return f'이름은 {self.__name}, 나이는 {self.__age}'
p = Person('홍길동', 20)
p.__age = 30
__age는 Private 변수로 Name Mangling에 의해 _Person__age로 변환되어 외부에서 직접 접근할 수 없다.
1) Setter 메서드를 사용하는 방법
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
# 나이를 설정하는 메서드
def set_age(self, age):
if age < 0:
print("나이는 0보다 커야 합니다.")
else:
self.__age = age
def add_age(self, age):
if age < 0:
print('나이는 0보다 커야 합니다. 나이 정보 오류')
else:
self.__age += age
def __str__(self):
return f'이름은 {self.__name}, 나이는 {self.__age}'
p = Person('홍길동', 20)
p.set_age(50) # 나이를 직접 설정
print(p) # 출력: 이름은 홍길동, 나이는 50
2) @classmethod 로 변경하는 방법
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
@classmethod
def set_person_age(cls, person_instance, age):
if age < 0:
print("나이는 0보다 커야 합니다.")
else:
person_instance.__age = age
def __str__(self):
return f'이름은 {self.__name}, 나이는 {self.__age}'
p = Person('홍길동', 20)
Person.set_person_age(p, 50) # 클래스 메서드를 통해 나이 변경
print(p) # 출력: 이름은 홍길동, 나이는 50
3) __dict__ 직접 접근 방식
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
# if 문의 해당 함수에서 0이 들어 가지 않도록 막음
def add_age(self,age):
if age < 0:
print('나이는 0보다 커야 합니다. 나이 정보 오류')
else:
self.__age += age
def __str__(self):
return f'이름은 {self.__name}, 나이는 {self.__age}'
p = Person('홍길동', 20)
# p.__age = 30 # 이중 언더스코어를 사용
p.__dict__['_Person__age'] = 50
print(p.__dict__) # 출력 : {'_Person__name': '홍길동', '_Person__age': 50}
데코레이터(Decorator)는 코드의 기능을 확장하고 반복적인 작업을 제거하며 코드 가독성을 높인다.
다른 함수를 감싸는 함수 또는 클래스이고 함수를 인자로 받아 새로운 함수를 반환한다.
'@데코레이터_이름' 형태이고 특정 함수나 클래스에 추가적인 동작을 적용할 수 있다.
def smile():
print('^_^')
def confused():
print('@_@')
def deco(fun):
def wrapper():
print('emotion!')
fun()
print('emotion!')
return wrapper
================
# emotion!
# ^_^
# emotion!
1) @deco를 사용한 데코레이터
def deco(fun):
def wrapper():
print('emotion!')
fun()
print('emotion!')
return wrapper
@deco
def smile():
print('^_^')
smile()
2) 직접 함수 호출
def smile():
print('^_^')
def confused():
print('@_@')
def deco(fun):
def wrapper():
print('emotion!')
fun()
print('emotion!')
return wrapper
smile_deco = deco(smile)
smile_deco()
@adder_deco
def adder1(n1,n2):
return n1 + n2
@adder_deco
def adder2(n1,n2 ,n3):
return n1 + n2 + n3
@adder_deco
def adder3(n1,n2 ,n3,n4):
return n1 + n2 + n3 + n4
======
8
6
10
adder1(3,5) # 8
adder2(1,2,3) # 6
adder3(1,2,3,4) # 6
def adder_deco(func) : # 함수를 인자로 받는다.
def add(*n) :
return func(*n)
return add
class Example:
class_var = "클래스 변수" # 클래스 변수
def __init__(self, instance_var):
self.instance_var = instance_var # 인스턴스 변수
# 클래스 변수 접근
print(Example.class_var) # 출력: 클래스 변수
# 인스턴스 생성 후 클래스 변수와 인스턴스 변수 접근
obj = Example("인스턴스 변수")
print(obj.class_var) # 출력: 클래스 변수
print(obj.instance_var) # 출력: 인스턴스 변수
# 클래스 변수 변경
Example.class_var = "변경된 클래스 변수"
print(obj.class_var) # 출력: 변경된 클래스 변수
class Example:
class_var = "클래스 변수"
@classmethod
def class_method(cls):
return f"클래스 변수: {cls.class_var}"
# 클래스 함수 호출
print(Example.class_method()) # 출력: 클래스 변수: 클래스 변수
# 인스턴스를 통해서도 호출 가능
obj = Example()
print(obj.class_method()) # 출력: 클래스 변수: 클래스 변수
class Example:
@staticmethod
def static_method(x, y):
return x + y
# 클래스 이름으로 호출
print(Example.static_method(3, 5)) # 출력: 8
# 인스턴스를 통해 호출
obj = Example()
print(obj.static_method(10, 20)) # 출력: 30
class Calculator:
@staticmethod # 스태틱 메소드 = 정적 메소드 = 간단한 함수들 = 객체와 상관없는 함수들
def add(n1, n2):
return n1 + n2
def add2(self, n1,n2): #인스턴스 메소드
return n1 + n2
@staticmethod
def mul(n1, n2):
return n1 * n2
cal = Calculator()
print(Calculator.add2(10,20))
인스턴스를 통해 호출
: cal.add2(10, 20)
명시적으로 인스턴스를 전달
: Calculator.add2(cal, 10, 20)
def decorator1(func):
def wrapper():
print('decorator1')
func()
return wrapper
def decorator2(func):
def wrapper():
print('decorator2')
func()
return wrapper
데코레이터를 여러 개 지정
@decorator1
@decorator2
def hello():
print('hello')
hello()
hello 함수를 decorator1과 decorator2로 감쌌기 때문에
decorator1 / decorator2 / hello
이렇게 출력될 것이다.
class Cirle2:
PI = 3.12419
def get_area(cls, radius):
return cls.PI * radius * radius
=============================
print(Cirle2.PI)
result = Cirle2.get_area(5)
print(result)
프로퍼티 함수(Property Function)는 클래스 속성에 접근하는 방식을 더 안전하고 직관적으로 만들기 위한 기능이다.
Getter/Setter를 연결해 속성처럼 접근하면서 내부적으로는 메서드를 실행한다.
'property( fget, fset, fdel, doc )' 형태로 사용한다.
class Person:
def __init__(self, name, age):
self.__name = name # 비공개 변수
self.__age = age
def get_age(self): # Getter
return self.__age
def set_age(self, age): # Setter
if age < 0:
print("나이는 0 이상이어야 합니다.")
else:
self.__age = age
age = property(get_age, set_age) # Getter와 Setter 연결
p = Person("Alice", 25)
print(p.age) # Getter 호출: 25
p.age = 30 # Setter 호출
print(p.age) # Getter 호출: 30
p.age = -5 # Setter 호출: "나이는 0 이상이어야 합니다."
뭔가 예제만 보면 setter와 getter 중 (property 괄호 안에 있는 것들 중) 제일 맞는 거 같은 거 하나씩 뽑아서 쓸 수 있는 느낌인데 맞는지 모르겠다.
print문 안에 있으니까 setter는 아니겠지? -> getter로 씀
값을 변경하려는 거 같네? -> getter는 아닌듯 -> setter로 씀
이런 느낌이다.
3줄 요약:
1.정보은닉 이란 변수를 최대한 노출 시키지 않도록 하는것이다.
2.파이썬에서 객체의 변수와 값은는 __dict__ 딕셔너리 객체로 따로 관리 한다.
3. __변수 이름은 __dict__['_클래스명__변수이름'] 으로 접근 가능하다.
3줄요약 두번째:
4. 데코레이터는 함수호출이며 함수의 파라미터로 콜백함수를 넘긴다.
5. 클래스 변수는 공용변수 이며, 각 객체들이 공유 하는 변수이다.
6. 프로퍼티는 쪼매 어렵다.
@deco~~할 때 선언해야만 한다.
함수 정의 시점에만 동작하기 때문에 미리 정의했던 함수를 가져오면 안된다.
def deco(fun):
def wrapper():
print("emotion!")
fun()
print("emotion!")
return wrapper
def smile():
print("^_^")
# 여기서 함수 호출
smile() # ^_^ 출력 (데코레이터 적용 안 됨)
# 나중에 데코레이터 작성
@deco
smile() # SyntaxError: invalid syntax
: @deco~~를 사용하지 않고 deco(smile)로 직접 감쌌기 때문이다.
4-1번의 경우 편리하지만 smile이 .wrapper로 덮어져서 원본함수로 접근하기가 어렵다.
4-2번의 경우 원본 smile 함수는 유지되고 wrapper의 결과는 smile_deco를 통해 접근 가능하다.
함수 하나마다 각각 데코레이터를 붙여야 한다.
상속과 데코레이터는 비슷하지만 목적과 사용사례가 다르다.
상속
: 클래스 구조를 확장하거나 공통 동작을 가진 여러 클래스에서 코드 중복을 제거할 때 적합하다.
: 부모 클래스의 모든 기능을 재사용 가능하지만 클래스 간 강한 결합이 발생하고 상속의 깊이가 깊어지면 유지 보수가 어려워진다.
데코레이터
: 기능을 추가하거나 수정하고 싶지만 기존 함수나 클래스의 구조를 변경하지 않으려는 경우에 적합하다.
: 재사용성이 높지만 함수에 한정된 동작을 추가하는데 적합하고 클래스 구조 확장이 어렵다.
: 둘 다 클래스 내부에 정의된 메서드지만 사용하는 방식, 목적, 접근 범위가 다르다.
클래스 함수 (@classmethod)
: '@classmethod' 데코레이터로 정의되며 첫 번째 매개변수로 클래스 자체를 나타내는 cls를 받는다.
: 인스턴스 상태(인스턴스 변수와 그 값들)에 접근할 수 없습니다.
: 클래스 이름 또는 인스턴스를 통해 호출 가능
인스턴스 함수
: 인스턴스 메서드는 일반적으로 정의되며 첫 번째 매개변수로 인스턴스 자신을 나타내는 self를 받는다.
: 인스턴스 변수에 접근하거나 조작할 수 있다.
: 반드시 인스턴스를 통해 호출해야 하며 클래스 이름으로 직접 호출하면 self를 전달하지 않아 오류가 발생한다.
사실 아직 클래스 함수 / 스태틱 함수 이런 걸로 물어보면 헷갈리긴 한다....
별찍기 할 때가 그립다...
