객체지향 프로그래밍
1. 객체
현실이든 가상이든 속성과 행동으로 떠올릴 수 있다면 모든 것이 객체입니다.
1-1. 객체 지향 프로그래밍
프로그램을 여러개의 독립된 객체들과 그 객체들간의 상호작용으로 파악하는 프로그래밍 접근법을 말합니다.
프로그램을 객체들과 객체들의 소통으로 바라보는 것
게임 캐릭터 객체
- 속성 : 체력, 목숨, 소유중인 총
- 행동 : 총을 장전한다, 총을 쏜다, 이동, 체력 0되면 죽음
총 객체
- 속성 : 모델명, 무게, 장전 총알 갯수
- 행동 : 총알 발사
총알 객체
- 속성 : 공격력
- 행동 : 캐릭터의 체력을 공격력만큼 - 하기
우리가 보는 거의 모든 것들을 객체라고 볼 수 있고 해당 객체들의 속성과 행동
그리고 이것들을 통해 상호작용 하는 프로그래밍을 객체 지향 프로그래밍이라 합니다.
2. 클래스와 인스턴스
인스타그램에서 유저를 나타내는 객체를 만들때 필요한 것은 무엇일까?
객체는 속성과 행동이 필요합니다.
유저 속성
- 이름
- 이메일
- 비밀번호
- 팔로우 유저
- 팔로우 목록
유저 행동
- 팔로우 하기
- 피드 올리기
- 좋아요 누르기
위에 있는 정리를 통해 User 객체의 틀을 잡았다고 말할 수 있습니다.
이것이 바로 클래스를 정의하는 것입니다.
인스턴스란 객체의 틀을 이용해 클래스를 만들었다면
이를 통해 여러가지 객체를 만들어 낼때 A, B, C 유저 객체가 생긴다면 이 A, B, C를 인스턴스라고 말합니다.
class User:
def __init__(self):
pass
def __str__(self):
return
user_a = User() # 인스턴스
user_b = User()
user_c = User()
2-1. 인스턴스 변수 정의
인스턴스명.속성명 = 값
class User:
def __init__(self):
pass
def __str__(self):
return
user_a = User() # 인스턴스
user_b = User()
user_c = User()
user_a.name = "토마스"
user_a.email = "python@codeit.kr"
user_a.password = "qqqq"
user_b.name = "제임스"
user_b.email = "java@codeit.kr"
user_b.password = "9999"
user_c.name = "매튜"
user_c.email = "go@codeit.kr"
user_c.password = "1111"
# 인스턴스 변수 접근
print(user_a.email)
print(user_b.password)
# 존재하지 않는 인스턴스 변수에 접근
print(user_c.age) # object has no attribute 'age' error
2-2. 인스턴스 메소드
객체에서 속성은 변수, 행동은 함수로 나타냅니다.
인스턴스 메소드는 인스턴스 변수를 사용하거나, 인스턴스 변수에 값 설정할 때 사용합니다.
class User:
def __init__(self):
pass
def __str__(self):
return
def say_hello(user):
print(f"안녕하세요. 저는 {user.name}입니다.")
user_a = User()
user_a.name = "토마스"
user_a.email = "python@codeit.kr"
user_a.password = "qqqq"
User.say_hello(user_a) # == user_a.say_hello() 동일
user_a.say_hello()
User.say_hello(user_a)
와 user_a.say_hello()
를 동일하게 실행이 가능한 이유는
인스턴스 메서드의 특별한 규칙이 있기 때문입니다.
왼쪽은 클래스 메소드 오른쪽은 인스턴스 메소드를 호출했습니다.
def say_hello(user):
로 인자를 넘겨 받지만 인스턴스 메소드의 경우
이때 user_a
가 첫번째 파라미터로 자동 전달되게 됩니다. (인자가 없는 함수를 호출하지만 1개가 전달되는 의미)
class User:
def __init__(self):
pass
def __str__(self):
return
def say_hello(user):
print(f"안녕하세요. 저는 {some_user.name}입니다.")
def login(user, my_email, my_password):
if user.email == my_email and user.password == my_password:
print("로그인 성공, 환영합니다.")
else:
print("로그인 실패, 없는 아이디이거나 잘못된 비밀번호입니다.")
위 코드에 login(some_user, my_email, my_password)
함수를 보면 3개의 인자를 받지만
실제로 인스턴스.함수명
으로 호출할때는 아래처럼 인자를 2개만 넘겨주는게 맞습니다.
user_a = User()
user_a.name = "토마스"
user_a.email = "python@codeit.kr"
user_a.password = "qqqq"
user_a.login("python@codeit.kr", "qqqq")
2-3. self 사용
파이썬에서는 인스턴스 메소드의 첫번째 파라미터 이름을 self
로 사용하도록 합니다.
class User:
def __init__(self):
pass
def __str__(self):
return
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}입니다.")
def login(self, my_email, my_password):
if self.email == my_email and self.password == my_password:
print("로그인 성공, 환영합니다.")
else:
print("로그인 실패, 없는 아이디이거나 잘못된 비밀번호입니다.")
2-4. init 사용
init 함수는 인스턴스를 초기화 하는 함수입니다. 특수메소드라고 불리며
첫번째 인자로는 self
로 지정해야 하고 인스턴스 생성시 코드는 아래와 같습니다.
class User:
"""magic method __{name}__ 특수메소드 (특정 상황에서 자동 호출 메소드) """
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")
print(user1.name, user1.email, user1.password)
print(user2.name, user2.email, user2.password)
2-5. dunder method
특수메소드중 하나로 __str__
메소드는 print 함수를 호출시 자동으로 출력됩니다.
class User:
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
""" double underscore (던더 메소드) """
def __str__(self):
return f"사용자: {self.name}, 이메일: {self.email}, 패스워드: ******"
2-6. 클래스 변수
같은 클래스의 인스턴스끼리 공유하는 변수를 말합니다.
class User:
count = 0
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
User.count += 1 # 유저 생성시 유저 수 1씩 증가
user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")
print(User.count)
만약 같은 이름의 인스턴스 변수값을 초기화하거나 할당할때는 어떻게 될까요?
우선 순위는 인스턴스 변수값을 새로 할당하게 됩니다.
class User:
count = 0
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
User.count += 1 # 유저 생성시 유저 수 1씩 증가
user1 = User("Young", "young@codeit.kr", "123456")
user1.count = 10
print(User.count) # 1
print(user1.count) # 10 -> 인스턴스 변수 우선 순위
2-7. 데코레이터
어떤 함수를 꾸며 새로운 함수로 리턴하는 것을 데코레이터라고 합니다.
def add_print_to(original):
"""
add_print_to는 wrapper함수를 통해 앞뒤 print로 인자로 받은
original 함수를 꾸며 새로운 함수로 return
그렇기에 add_print_to 함수는 데코레이터 함수라 부른다
"""
def wrapper():
print("wrapper start")
original()
print("wrapper end")
return wrapper
def print_hello():
print("hello it's me")
print_func = add_print_to(print_hello)
print_func()
@add_print_to
def print_hello_2():
""" 데코레이터 표기 """
print("hello it's me")
print_hello_2()
데코레이터를 사용할 때 이점은 다음과 같습니다.
함수를 추가할때마다 꾸며지는 내용이 중복 발생
꾸밀 내용을 담은 함수를 작성하고
해당 함수에 새로운 함수를 인자로 보내주기 위해 @ decorator 표기
2-8. 클래스 메소드
클래스 메소드는 첫번째 파라미터로 클래스가 자동 전달되며 그 변수의 표기는 cls
입니다.
또한 인스턴스 메소드와 구분하기 위해 @classmethod
데코레이터를 표기합니다.
class User:
count = 0 # 클래스 변수
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
User.count += 1 # 유저 생성시 유저 수 1씩 증가
def __str__(self):
return f"사용자: {self.name}, 이메일: {self.email}, 패스워드: ******"
@classmethod
def number_of_users(cls):
print(f"총 유저수는 : {cls.count}입니다.")
user1 = User("Young", "young@codeit.kr", "123456")
user1.number_of_users() # 1
User.number_of_users() # 1
인스턴스변수와 다르게 클래스나 인스턴스로 접근시 모두 첫번째 인자로 클래스 자동 전달
class User:
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
@classmethod
def from_string(cls, string_value):
user_values = string_value.split(",")
return cls(user_values[0], user_values[1], user_values[2])
@classmethod
def from_list(cls, list_value):
return cls(list_value[0], list_value[1], list_value[2])
info_string = "mount,chelsea@nike.kr,123456"
info_list = ["foden", "mancity@puma.kr", "abcdef"]
mount = User.from_string(info_string)
foden = User.from_list(info_list)
print(mount.name, mount.email, mount.password)
print(foden.name, foden.email, foden.password)
2-9. 정적 메소드
정적 메소드를 구분하기 위해 @classmethod
데코레이터를 표기합니다.
정적메소드는 인스턴스 메소드와 클래스 메소드와 같이 자동 전달되는 파라미터가 없습니다.
또한 인스턴스 변수와 클래스 변수의 사용이 모두 가능합니다.
정적메소드는 그럼 언제 사용할까?
쉽게 생각해서 인스턴스 변수나 클래스 변수 중 아무것도 사용하지 않으면 입니다.
단지 기능적인 역할만 하는 메소드를 정의할 때 스태틱 메소드로 정의하면 됩니다.
# 인스턴스 메소드
def __str__(self):
return "사용자: {}, 이메일: {}, 비밀번호: ******".format(self.name, self.email)
# 클래스 메소드
@classmethod
def number_of_users(cls):
print("총 유저 수는: {}입니다".format(cls.count))
# 정적 메소드
@staticmethod
def is_valid_email(email_address):
return "@" in email_address # True or False
3. 가변 vs 불변 타입
파이썬에선 대표적으로 가변 타입과 불변 타입으로 구분지을 수 있는 것은 내장 함수인 list와 tuple이 있습니다.
튜플은 한번 초기화 한 후 인덱스로 접근하여 속성값을 변경할 수 없습니다.
하지만 파이썬의 불변타입은 변수가 가리키는 객체 자체는 변경할 수 있습니다.
즉, 접근하여 값을 할당하는 것은 불가하지만 객체 메모리 주소에 새로 할당하는 것은 가능합니다.
가변 타입은 직접 작성하는 클래스로 인스턴스 변경시 속성을 변경할 수 있습니다.
4. 절차 지향 vs 객체 지향
객체지향
- 객체 지향 프로그래밍에서는 관련된 동작들을 관련된 데이터와도 함께 묶어서 관리
- 프로그램을 생성시 데이터 + 함수 가능
- 객체들이 상호작용하며 소통하는 과정
절차지향
- 절차 지향 프로그래밍에서는 프로그램 안에서 서로 관련된 동작들만을 묶어서 관리
- 프로그램을 생성시 데이터 + 함수 불가능
- 명령들이 순서대로 실행하는 과정
용도에 따라 적합한 방식이 다르기에 어느 부분이 무조건 옳다라고 볼 수 없습니다.
하지만 복잡한 프로그램일수록 객체 지향 프로그래밍으로 하는 경우가 더 나을때가 있습니다.
Reference
'Posts > Python' 카테고리의 다른 글
Python | Django Interview Q&A List (0) | 2021.11.10 |
---|---|
파이썬을 이용한 클린 코드를 위한 TDD - 3장 단위 테스트 적용 (0) | 2021.11.08 |
파이썬을 이용한 클린 코드를 위한 TDD - 2장 unittest 모듈 (0) | 2021.10.30 |
파이썬을 이용한 클린 코드를 위한 TDD - 1장 기능테스트를 이용한 장고 프로젝트 설정 (0) | 2021.10.30 |