def hello():
print("hello") #기존 함수 선언
def deco(fn):
def deco_hello():
print("*" * 20) # 기능 추가
fn() # 기존 함수 호출
print("*" * 20) # 기능 추가
return deco_hello
deco_hello = deco(hello) # 데코레이터에 호출할 함수를 넣음
hello() # 반환된 함수 호출
>>>********************
hello
********************
@데코레이터함수
와 같이 적어주는 방법으로 데코레이터를 사용하는 것도 가능함.@deco
def hello():
print("hello")
hello()
>>>********************
hello
********************
getter
, 값을 저장하는 메서드를 setter
라고 부름.class Person:
def __init__(self):
self.__age = 0
def get_age(self): # getter
return self.__age
def set_age(self, value): # setter
self.__age = value
james = Person()
james.set_age(20)
print(james.get_age())
>>>20
class Person:
def __init__(self):
self.__age = 0
@property
def age(self): # getter
return self.__age
@age.setter
def age(self, value): # setter
self.__age = value
james = Person()
james.age = 20 # 인스턴스.속성 형식으로 접근하여 값 저장
print(james.age) # 인스턴스.속성 형식으로 값을 가져옴
>>>20
getter, setter 메서드의 이름은 둘다 동일하게 age
getter에는 데코레이터로 @property를, setter에는 데코레이터로 @age.setter가 붙음. 즉, 값을 가져오는 메서드에는 @property 데코레이터를 붙이고, 값을 저장하는 메서드에는 @메서드이름.setter 데코레이터를 붙이는 방식임
데코레이터를 활용하는 위 방식의 경우 james.age 처럼 메서드를 속성처럼 사용 가능(()를 붙이지 않고도 호출이 가능)
값을 저장할 때는 james.age = 20처럼 메서드에 바로 할당하면 되고, 값을 가져올 때도 james.age처럼 메서드에 바로 접근 가능
class Book:
def __init__(self, raw_price):
if raw_price < 0 :
raise ValueError('price must be positive')
self.raw_price = raw_price
self._discounts = 0
@property
def discounts(self):
return self._discounts
@discounts.setter
def discounts(self, value):
if value < 0 or 100 < value:
raise ValueError('discounts must be between 0 and 100')
self._discounts = value
@property
def price(self):
multi = 100 - self._discounts
return int(self.raw_price * multi / 100)
book = Book(2000)
book.discounts
>>>0
book.price
>>>2000
book.discounts = 20
book.price
>>>1600
book.price = 100
>>>AttributeError: can't set attribute
언더스코어(_)의 사용 용도로 4가지가 있음
1. 인터프리터에서 사용
2. 무시하는 값
3. 반복문에서 사용
3. 숫자값의 분리(자릿수 구분, 구분자)
4. 변수명에서 사용
파이썬 인터프리터에서 가장 마지막 표현식의 결과값을 자동으로 _
라는 변수에 저장
여기에 저장된 값을 다른 변수에 저장 가능
일반적인 값처럼 사용 가능
>>> 5 + 4
9
>>> _
9
>>> _ + 6
15
>>> a = _
>>> a
15
_
에 지정# 하나의 값 무시
a, _, b = (1, 2, 3)
print(a, b) # 1,3
# 여러 개의 값 무시
# *을 이용하여 여러 개의 값을 하나의 변수에 저장할 때 쓰임
# "Extended Unpacking"이며, Python 3.x에서 사용 가능
a, *_, b = (7, 6, 5, 4, 3, 2, 1)
print(a, b) # 7,1
a = []
for _ in range(5):
a.append(_)
a
>>>[0, 1, 2, 3, 4]
_ = 5
while _ < 10:
print(_, end = ' ')
_ += 1
# end의 기본값은 \n 이나, 띄어쓰기로 바꿔줌
>>> 5 6 7 8 9
숫자가 길다면 자릿수 구분을 위해 _
를 사용
2진수, 8진수, 16진수 사용 가능
파이썬은 기본 10진수 -> 나머지 진수의 경우 _
앞에 접두어가 붙음
million = 1_000_000
binary = 0b_0010 # 2진수
octa = 0o_64 # 8진수
hexa = 0x_23_ab # 16진수
print(million) # 1000000
print(binary) # 2
print(octa) # 52
print(hexa) # 9131
class Test:
def__init__(self):
self.name = "test"
self._num = 7
obj = Test()
print(obj.name) # test
print(obj._num) # 7
# my_functions.py
def func():
return "test"
def _private_func():
return 7
# 다른 파일에서 my_function.py를 import
# 하지만 파이썬은 언더바 하나로 시작한 이름들은 import하지 않는다!
>>> from my_functions import *
>>> func()
"test"
>>> _private_func()
Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name '_private_func' is not defined
# 모듈 자체를 import하기!
>>> import my_functions
>>> my_functions.func()
"test"
>>> my_functions._private_func()
7
파이썬 키워드(예약어)를 변수, 함수, 클래스 명 등으로 사용하려는 경우
이름 끝에 _
를 사용하여 파이썬 키워드와의 충돌 방지 가능
a = [1,2,3,4]
sum = sum(a)
print(sum)
b = [3,4,5]
sum = sum(b)
print(sum)
sum은 리스트안의 값들을 모두 더하는 파이썬의 예약어
위 a 구문에선 sum을 변수로, b 구문에선 sum의 예약어 기능을 사용하기에 오류 발생 'int' object is not callable
이런 오류를 피하기 위해 sum 대신 sum_을 사용
a = [1,2,3,4]
sum_ = sum(a)
print(sum_)
b = [3,4,5]
sum_ = sum(b)
print(sum_)
name mangling: 파이썬이 해당 변수/함수의 이름을 짓이겨 바꿔버린다는 의미
맹글링을 당한 변수/함수는 원래의 이름을 접근할 수 없음
언더바 2개로 맹글링을 적용한 변수/함수는 _클래스명__변수/함수명
으로 이름이 변경됨
class TestClass():
def __init__(self):
self.name = "Peter"
self.gender = "male"
self.__age = 32
man = TestClass()
print(man.name) # Peter
print(man.gender) # male
print(man.__age) # AttributeError 발생
# dir함수는 클래스 객체의 모든 attribute들을 리턴
# gender, name는 보이지만 __age는 _TestClass__age라는 이름이 보인다
print(dir(man))
['_TestClass__age', '__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__', 'gender', 'name']
dir 함수를 통해 보면 man안에 함수와 변수에 __age가 없는 걸 확인 가능
언더스코어 두개로 시작하는 것은 private access modifier(접근 지정자)의 역할을 함. 이는 사용자가 만든 클래스의 변수를 변환하는데, 서브 클래스에서의 이름이 충돌나는 것을 방지하기 위해 사용함. 변수 뿐 아니라 메소드에서도 사용 가능
print(man._TestClass__age)
or
class TestClass():
def __init__(self):
self.name = "Peter"
self.gender = "male"
self.__age = 3
def get_age(self):
return self.__age
print(man.get_age())
class TestClass():
def __init__(self):
self.name = "Peter"
self.gender = "male"
self.__list = [1]
def get_list(self):
return self.__list
@property
def chk_list(self):
return self.__list
man = TestClass()
print(man.name)
print(man.get_list())
man.chk_list.append(10) #이 구문을 man.get_list.append(10) 또는 man.chk_list().append(10)으로 쓰면 error 발생
print(man.get_list())
#Peter
#[1]
#[1, 10]
특수한 예약 함수나 변수, 함수명으로 매직 메소드(magic method) 또는 dunder 메소드라고 함
대표적으로 __init__, __add__, __len__, __main__
클래스 안에 정의가능한 스페셜 메서드이며, 클래스를 int(), str(), list()등 파이썬의 빌트인 타입과 같은 작동을 하게 해줌
+, -, >, < 등의 오퍼레이터에 대해 각각의 데이터 타입에 맞는 메소드로 오버로딩해 백그라운드 연산
class 클래스 명:
@classmethod
def 메서드 명(cls, 매개변수1, 매개변수2):
코드
기본 구조는 위와 같음
클래스 메서드는 클래스에 속한 메서드로, 첫 번째 인수(cls)에 클래스 객체를 전달
클래스 메서드는 @classmethod를 붙이는 점 이외에는 인스턴스 메서드와 동일한 형태로 정의
첫 번째 인수가 클래스 객체이므로 일반적으로 self가 아닌 cls로 기술
class Person:
count = 0 # 클래스 속성
def __init__(self):
Person.count += 1 # 인스턴스가 만들어질 때
# 클래스 속성 count에 1을 더함
@classmethod
def print_count(cls):
print('{0}명 생성되었습니다.'.format(cls.count)) # cls로 클래스 속성에 접근
james = Person()
maria = Person()
Person.print_count()
# 2명 생성되었습니다.
인스턴스가 생성될 때마다 +1씩 count되므로 __init__ 메서드에서 클래스 속성에 count += 1
첫 번째 매개변수 cls에는 현재 클래스가 반환. 즉 cls.count처럼 cls로 클래스 속성 count에 접근이 가능
클래스 메서드는 인스턴스 없이 호출 가능, 하지만 메서드 안에서 클래스 속성, 클래스 메서드에 접근 시 사용
cls 사용 시 메서드 안에서 현재 클래스의 인스턴스 생성 가능
class 클래스 명:
@staticmethod
def 메서드 명(매개변수1, 매개변수2):
코드
기본 구조는 위와 같음
함수처럼 동작하는 메서드
클래스 메서드와 거의 같은 구문, @staticmethod 사용
인수에는 인스턴스나 클래스 객체는 전달X, 호출 시 전달한 값이 그대로 전달
#classmethod
class hello:
num = 10
@classmethod
def calc(cls, x):
return x + 10 + cls.num #cls.num을 통해 hello 클래스의 num 속성에 접근
print(hello.calc(10))
#결과
30
#staticmethod
class hello:
num = 10
@staticmethod
def calc(x):
return x + 10 + hello.num
print(hello.calc(10))
#결과
30
class hello:
t = '내가 상속해 줬어'
@classmethod
def calc(cls):
return cls.t
class hello_2(hello):
t = '나는 상속 받았어'
print(hello_2.calc())
#결과
내가 상속해 줬어
상속관계가 있는 클래스에서 클래스 메서드를 사용한 경우
상속받은 hello_2 클래스가 t 속성을 업데이트
위의 클래스 메서드에서 cls.t가 상속시켜준 클래스에 존재하더라도 상속받은 클래스의 t 속성임을 명시함 -> 따라서 t는 hello 클래스에서 찾아옴
class 기반클래스이름1:
코드
class 기반클래스이름2:
코드
class 파생클래스이름(기반클래스이름1, 기반클래스이름2):
코드
class Person:
def greeting(self):
print('안녕하세요.')
class University:
def manage_credit(self):
print('학점 관리')
class Undergraduate(Person, University):
def study(self):
print('공부하기')
james = Undergraduate()
james.greeting() # 안녕하세요.: 기반 클래스 Person의 메서드 호출
james.manage_credit() # 학점 관리: 기반 클래스 University의 메서드 호출
james.study() # 공부하기: 파생 클래스 Undergraduate에 추가한 study 메서드
class A:
def greeting(self):
print('안녕하세요. A입니다.')
class B(A):
def greeting(self):
print('안녕하세요. B입니다.')
class C(A):
def greeting(self):
print('안녕하세요. C입니다.')
class D(B, C):
pass
x = D()
x.greeting() # 안녕하세요. B입니다.
이런 클래스 간의 관계를 다이아몬드 상속이라 부름
클래스 A를 상속받아서 B, C를 만들고, 클래스 B와 C를 상속받아서 D를 만듦
여기서 D는 ABC중 어떤 클래스의 메서드를 호출해야하는지는 메서드 탐색 순서로 확인 가능
>>> D.mro()
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
MRO에 따르면 D의 메서드 호출 순서는 D, B, C, A 순(보통 다중 상속에서 클래스의 목록 중 왼쪽 -> 오른쪽 순이므로 D에서는 B -> C)
따라서 D에는 greeting 메서드가 없으므로 B의 greeting이 호출됨
여기서 나오는 object클래스는 모든 클래스의 조상. 즉, 어떤 클래스든 MRO를 출력하면 object가 출력됨.
Reference
- https://velog.io/@sugenius77/Python-%EC%96%B8%EB%8D%94%EB%B0%94-%EC%96%B8%EB%8D%94%EC%8A%A4%EC%BD%94%EC%96%B4-%EB%B0%91%EC%A4%84-%EB%AC%B8%EC%9E%90
- https://hwanii-with.tistory.com/54
- https://wikidocs.net/21054
- https://dojang.io/mod/page/view.php?id=2388
- https://dojang.io/mod/page/view.php?id=2476
- https://dojang.io/mod/page/view.php?id=2427