본문 바로가기

Posts/Python

[python 객체지향 프로그래밍] 객체

반응형

객체지향 프로그래밍


1. 객체

현실이든 가상이든 속성행동으로 떠올릴 수 있다면 모든 것이 객체입니다.

image


1-1. 객체 지향 프로그래밍

프로그램을 여러개의 독립된 객체들과 그 객체들간의 상호작용으로 파악하는 프로그래밍 접근법을 말합니다.

프로그램을 객체들과 객체들의 소통으로 바라보는 것

image

게임 캐릭터 객체
  • 속성 : 체력, 목숨, 소유중인 총
  • 행동 : 총을 장전한다, 총을 쏜다, 이동, 체력 0되면 죽음
총 객체
  • 속성 : 모델명, 무게, 장전 총알 갯수
  • 행동 : 총알 발사
총알 객체
  • 속성 : 공격력
  • 행동 : 캐릭터의 체력을 공격력만큼 - 하기

image

우리가 보는 거의 모든 것들을 객체라고 볼 수 있고 해당 객체들의 속성행동

그리고 이것들을 통해 상호작용 하는 프로그래밍을 객체 지향 프로그래밍이라 합니다.


2. 클래스와 인스턴스

인스타그램에서 유저를 나타내는 객체를 만들때 필요한 것은 무엇일까?

객체는 속성행동이 필요합니다.

image

유저 속성

  • 이름
  • 이메일
  • 비밀번호
  • 팔로우 유저
  • 팔로우 목록

유저 행동

  • 팔로우 하기
  • 피드 올리기
  • 좋아요 누르기

위에 있는 정리를 통해 User 객체의 틀을 잡았다고 말할 수 있습니다.

이것이 바로 클래스를 정의하는 것입니다.

image

인스턴스란 객체의 틀을 이용해 클래스를 만들었다면

이를 통해 여러가지 객체를 만들어 낼때 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. 인스턴스 메소드

image

객체에서 속성은 변수, 행동은 함수로 나타냅니다.

인스턴스 메소드는 인스턴스 변수를 사용하거나, 인스턴스 변수에 값 설정할 때 사용합니다.

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)

만약 같은 이름의 인스턴스 변수값을 초기화하거나 할당할때는 어떻게 될까요?

image

우선 순위는 인스턴스 변수값을 새로 할당하게 됩니다.

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()

데코레이터를 사용할 때 이점은 다음과 같습니다.

image

함수를 추가할때마다 꾸며지는 내용이 중복 발생

image

꾸밀 내용을 담은 함수를 작성하고
해당 함수에 새로운 함수를 인자로 보내주기 위해 @ 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

image

인스턴스변수와 다르게 클래스나 인스턴스로 접근시 모두 첫번째 인자로 클래스 자동 전달

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 불변 타입

image

파이썬에선 대표적으로 가변 타입과 불변 타입으로 구분지을 수 있는 것은 내장 함수인 listtuple이 있습니다.

image

튜플은 한번 초기화 한 후 인덱스로 접근하여 속성값을 변경할 수 없습니다.

하지만 파이썬의 불변타입은 변수가 가리키는 객체 자체는 변경할 수 있습니다.

즉, 접근하여 값을 할당하는 것은 불가하지만 객체 메모리 주소에 새로 할당하는 것은 가능합니다.

image

가변 타입은 직접 작성하는 클래스로 인스턴스 변경시 속성을 변경할 수 있습니다.


4. 절차 지향 vs 객체 지향

객체지향

  • 객체 지향 프로그래밍에서는 관련된 동작들을 관련된 데이터와도 함께 묶어서 관리
  • 프로그램을 생성시 데이터 + 함수 가능
  • 객체들이 상호작용하며 소통하는 과정

절차지향

  • 절차 지향 프로그래밍에서는 프로그램 안에서 서로 관련된 동작들만을 묶어서 관리
  • 프로그램을 생성시 데이터 + 함수 불가능
  • 명령들이 순서대로 실행하는 과정

용도에 따라 적합한 방식이 다르기에 어느 부분이 무조건 옳다라고 볼 수 없습니다.

하지만 복잡한 프로그램일수록 객체 지향 프로그래밍으로 하는 경우가 더 나을때가 있습니다.


Reference



반응형