차밍이

객체 지향 프로그래밍 : 객체와 메서드를 통해 알아보자 본문

파이썬/기본 문법 정리

객체 지향 프로그래밍 : 객체와 메서드를 통해 알아보자

2020. 1. 29. 19:42
반응형

본 내용은 codeit 코드 잇 사이트의 객체지향 프로그래밍 수업을 들으며 공부한 내용들을 바탕으로 정리하여 작성하였습니다.

객체 지향 프로그래밍이란

  • Object-Oriented Programming (OOP)

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

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

위키피디아에서의 정의

  • 프로그래밍 설계방법론이자 개념의 일종으로 프로그램을 수많은 '객체'라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식입니다.

  • 프로그래밍에서 필요한 데이터를 속성과 행동을 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 프로그래밍하는 방법입니다.

 

객체 지향 프로그래밍의 장점과 단점

장점

  1. 코드 재사용이 용이하다.
    • 클래스 상속을 사용해서 계속해서 확장해나갈 수 있습니다.
  2. 유지보수가 쉽다.
    • 클래스 내부의 메소드와 인스턴스를 사용해서 해당 부분을 수정해서 사용할 수 있습니다.
  3. 대형 프로젝트에 적합하다.
    • 클래스 단위로 모듈화시켜 개발할 수 있으므로 여러 명 혹은 여러 회 사에서 각자 맞은 부분을 개발하기 위해 업무 분담이 가능합니다.

단점

  1. 처리 속도가 느리며
  2. 객체가 많아질수록 용량이 커집니다.
  3. 설계 시 많은 노력과 시간이 필요합니다.

 

 

객체란 무엇인가?

객체는 속성과 행동으로 구성된 모든 것입니다.

 

객체 = 속성 + 행동

 

우리가 살아가는 모든 존재를 객체라고 할 수 있습니다.

 

=> 사람, 자동차, 인스타그램 유저 등 무엇이든 객체가 될 수 있다.

객체는 속성과 행동으로 구성되어 있습니다.

자동차 객체의 속성 : 색상, 높이, 의자 개수 등

자동차 객체의 행동 : 시동 걸리기, 앞으로 전진하기, 후진하기

인스타그램 유저 객체

속성 : 이메일 주소, 비밀번호, 친구 목록

행동 : 좋아요 누리기, 포스팅

모든 것이 객체인가요? : YES 무엇이든 객체가 될 수 있다.

 

 

객체 지향 프로그래밍의 예시

총 게임을 객체 지향 프로그래밍으로 한다면

  • 객체 : 게임 캐릭터, 총, 총알
    • 게임 캐릭터
      • 속성 : ID, 현재 사용 중인 총, 체력 목숨
      • 행동 : 총을 발사한다. 달린다. 체력이 다하면 죽는다.
      • 속성 : 모델명, 무게, 장전된 총알
      • 행동 : 총알을 발사한다.
    • 총알 객체
      • 속성 : 공격력
      • 행동 : 총알을 맞은 캐릭터의 체력을 공격력만큼 깎는다.

각 객체와 객체의 속성 및 행동이 정해져 있습니다. 이를 통해서 각 객체들 간의 상호 유기적인 동작을 바탕으로 프로그래밍하는 것을 객체지향 프로그래밍이라고 합니다.

예를 들어

 

 게임 캐릭터가 총을 쏜다.

​ 총객체가 총알을 발사한다.

​ 총객체의 장전된 총알이 줄어든다.

​ 총알 객체가 맞은 캐릭터의 체력을 깎는다.

​ 게임 캐릭터 객체의 체력이 줄어든다.

 

이런 식의 객체의 행동과 객체들 간의 상호 동작을 생각하시면 됩니다.

따라서 객체지향적으로 설계했다는 것은 모델링(modeling)을 했다는 말과 동일하게 볼 수 있습니다.

 

 

객체 지향 프로그래밍으로 프로그램을 만들려면

  1. 프로그램에 어떤 객체들이 필요할지 정한다.

  2. 객체들의 속성과 행동을 정한다.

  3. 객체들이 서로 어떻게 소통할지 정한다.

인스타그램 User 객체로 예시 및 실습

인스타그램 User 객체

속성

​ 이름, 이메일 주소, 비밀번호, 팔로우 목록, 팔로워 목록

행동

​ 자기소개하기, 팔로우하기, 좋아요 누르기

=> 모든 유저들이 객체는 User 객체를 기반으로, 모두 같은 틀

=> 이런 객체의 틀을 파이썬에서 클래스라고 합니다.

 

클래스로 객체를 만든다 == 클래스로 인스턴스를 만든다.

 

파이썬

파이썬에서 객체는 인스턴스라는 말로 쓰입니다.

객체 = 인스턴스

클래스로 여러 객체 or 인스턴스를 만들 수 있다.

객체 : 속성 + 행동

​ 속성 : 변수로 정의

​ 행동 : 함수로 정의 => 메소드를 정의했다고 표현함

 

메서드의 종류

메서드의 종류는 3가지가 있습니다.

  1. 인스턴스 메서드
  2. 클래스 메서드
  3. 정적 메서드

1. 인스턴스 메서드

  • 인스턴스 변수를 사용하거나, 인스턴스 변수 값을 설정하는 메서드

  • 이름 앞뒤로 __가 붙는 메서드 : magic method = special method = 특수 메서드라고 한다.

  • 특수 메서드는 특정 상황에서 자동으로 호출되는 메서드이다.

    • __init__ 메서드 : 인스턴스가 생성될 때 자동으로 호출

    • __str__ 메소드 : print 함수를 호출할 때 자동으로 실행되며 인스턴스를 출력하면 나오는 값

    • 그 외에도 다양한 특수 메서드가 존재한다.

## 인스턴스 변수는 항상 사용하기 전에 미리 설정해야 한다.
## User 인스턴스의 변수를 설정하는 메소드를 만들어 보자.
class User: # 클래스 이름의 첫글자는 대문자를 사용할 것 규칙
    # 인스턴스 메소드의 첫번째 파라미터는 꼭 self를 사용한다.
    def __init__(self, name, email, password): 
        self.name = name
        self.email = email
        self.password = password

# 1. User 인스턴스 생성        
# 2. __init__ 메소드 자동 호출
user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

print(user1.name, user1.email, user1.password)

인스턴스 메소드 활용

# 맞팔하기
class User:
    # 인스턴스 변수 설정
    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

        self.following_list = []    # 이 유저가 팔로우하는 유저 리스트
        self.followers_list = []    # 이 유저를 팔로우하는 유저 리스트

    # 팔로우
    def follow(self, another_user):
        # 코드를 입력하세요
        self.following_list.append(another_user.name)
        another_user.followers_list.append(self.name)
    # 내가 몇 명을 팔로우하는지 리턴
    def num_following(self):
        # 코드를 입력하세요
        return len(self.following_list)
    # 나를 몇 명이 팔로우하는지 리턴
    def num_followers(self):
        # 코드를 입력하세요
        return len(self.followers_list)

# 유저들 생성
user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")
user3 = User("Taeho", "taeho@codeit.kr", "123abc")
user4 = User("Lisa", "lisa@codeit.kr", "abc123")

# 유저마다 서로 관심 있는 유저를 팔로우
user1.follow(user2)
user1.follow(user3)
user2.follow(user1)
user2.follow(user3)
user2.follow(user4)
user4.follow(user1)

# 유저 이름, 자신의 팔로워 수, 자신이 팔로우하는 사람 수를 출력합니다
print(user1.name, user1.num_followers(), user1.num_following())
print(user2.name, user2.num_followers(), user2.num_following())
print(user3.name, user3.num_followers(), user3.num_following())
print(user4.name, user4.num_followers(), user4.num_following())

__str__ 활용

  • __str__메서드는 dunder method , dunder str이라고 부름
# 맞팔하기
class User:
    # 인스턴스 변수 설정
    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

        self.following_list = []    # 이 유저가 팔로우하는 유저 리스트
        self.followers_list = []    # 이 유저를 팔로우하는 유저 리스트

    # 팔로우
    def follow(self, another_user):
        # 코드를 입력하세요
        self.following_list.append(another_user.name)
        another_user.followers_list.append(self.name)
    # 내가 몇 명을 팔로우하는지 리턴
    def num_following(self):
        # 코드를 입력하세요
        return len(self.following_list)
    # 나를 몇 명이 팔로우하는지 리턴
    def num_followers(self):
        # 코드를 입력하세요
        return len(self.followers_list)
    def __str__(self):
        return "사용자: {}, 이메일: {}".format(self.name, self.email)

# 유저들 생성
user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")


# 유저 이름, 자신의 팔로워 수, 자신이 팔로우하는 사람 수를 출력합니다
print(user1)
print(user2)

클래스 메서드

클래스 변수

모든 인스턴스가 공유하는 값을 클래스 변수라고 합니다. 같은 클래스의 인스턴스들은 모두 같은 값을 가지게 됩니다.

어떤 인스타그램 User 객체에 접근하더라도 전체 User들의 수를 나타내는 count를 알고 싶다면 count는 클래스 변수로 선언해 주어야 될 것입니다.

count : 클래스 변수 count가 유저 인스턴스 개수를 정확히 나타내도록 하려면?

=> __init__ 메서드 안에 넣어 놓으면 class 선언될 때마다 실행되므로 전체 유저 수를 나타낼 수 있다.

class User:
    count = 0
    # 인스턴스 변수 설정
    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password
        User.count += 1

user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

print(User.count)
>> 2

클래스 변수와 인스턴스 변수 값이 동일하다면?

같은 이름의 클래스 변수 User.count

같은 이름의 인스턴스 변수 user1.count

출력하면 인스턴스 변숫값이 출력된다.

헷갈리지 않도록 클래스 변수 값을 부를 때는 꼭 클래스 명으로 출력해야 한다.

클래스 변수 : 한 클래스의 모든 인스턴스가 공유하는 속성

클래스 변수의 값 읽는 법?

  1. 클래스 이름.클래스 변수 이름
  2. 인스턴스 이름.클래스 변수 이름

클래스 변수의 값 설정하기?

  1. 클래스 이름.클래스 변수 이름

    • 인스턴스를 통해서는 클래스 변수값을 설정할 수 없습니다. 인스턴스에 같은 이름의 변수가 정의되는 것입니다.
class User:
    count = 0
    # 인스턴스 변수 설정
    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

        User.count += 1

user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

print(User.count)
>> 2
print(user1.count)
>> 2

user1.count = 5 # user1에 count라는 인스턴스 변수가 생성된 것
print(User.count)
>> 2
print(user1.count) 
>> 5 # 클래스 변수에 접근하지 못하고 같은 이름을 가지는 인스턴스 변수값이 출력된다.

데코레이터

데코레이터 : 어떤 함수를 꾸며서 새로운 함수를 만드는 것

def print_hello():
    print("안녕하세요")

# 데코레이팅 함수
def add_print_to(original): # 파라미터로 함수를 받는다.
    def wrapper(): # 함수 내에서 새로운 함수 선언
        print("함수 시작") # 부가기능 -> original을 꾸민다.
        original()
        print("함수 끝")   # 부가기능 -> original을 꾸민다.
    return wrapper   # 함수를 return 한다.

add_print_to(print_hello)()
>> "함수 시작"
"안녕하세요"
"함수 끝"

add_print_to(print_hello)
>> 
# 함수가 그대로 들어감. 실행시키려면 () 붙여줘야한다.

print_hello = add_print_to(print_hello)
print_hello()
>> "함수 시작"
"안녕하세요"
"함수 끝"
# 데코레이팅 함수
def add_print_to(original): # 파라미터로 함수를 받는다.
    def wrapper(): # 함수 내에서 새로운 함수 선언
        print("함수 시작") # 부가기능 -> original을 꾸민다.
        original()
        print("함수 끝")   # 부가기능 -> original을 꾸민다.
    return wrapper   # 함수를 return 한다.


@add_print_to  # add_print_to를 사용해서 print_hello()함수를 꾸며주도록 하는 명령어
def print_hello():
    print("안녕하세요")


print_hello()
>> "함수 시작"
"안녕하세요"
"함수 끝"

@classmethod # 데코레이터로 여러개의 함수를 꾸며줄 떄 편하게 사용할 수 있음
def fuction():
    return

2. 클래스 메서드

class User:
    count = 0
    # 인스턴스 변수 설정
    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

        User.count += 1

    def say_hello(self):
        print("안녕하세요! 저는 {}입니다.".format(self.name))

    def __str__(self):
        return "사용자: {}, 이메일: {}".format(self.name, self.email)

    # cls 클래스 메소드의 규칙
    # 첫 번째 파라미터의 이름은 꼭! cls로 쓰기
    @classmethod
    def number_of_users(cls): # cls.count == User.count
        print("총 유저 수는: {}입니다.".format(cls.count))
    # 인스턴스 메소드로 만들 수도 있다.
    # 그런데 왜 클래스 메소드로 만든걸까?
    # number_of_users에서는 인스턴스값이 전혀 들어가지 않는다. 
    # self 파라미터가 전혀 필요하지 않는다. self에 접근할 필요가 없다.
    # class 변수만 사용하므로 클래스 메소드를 사용하면 된다.


# 유저들 생성 # 인스턴스 생성
user1 = User("Young", "young@codeit.kr", "123456")
user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

# Class로 클래스 메소드 출력
User.number_of.users()
# 인스턴스로 클래스 메소드 출력
user1.number_of.users()
>> 총 유저 수는: 2입니다.
>> 총 유저 수는: 2입니다.

클래스 메서드를 사용할 때?

인스턴스 변수 사용 => 인스턴스 메소드

*클래스 변수 사용 * => 클래스 메소드

만약 클래스 변수와 인스턴스 변수를 모두 사용한다면??

클래스 메서드는 인스턴스 변수 사용 불가능하다.

반면 인스턴스 메소드는 인스턴스 변수와 클래스 변수 모두 사용할 수 있으므로 인스턴스 메서드를 사용해야 한다.

인스턴 없이도 필요한 정보가 있다면?

클래스 number_of_users에서

User.count -> 인스턴스가 하나도 없더라도 필요하다.

-> 0이라는 값을 출력해야 하므로

-> 따라서 클래스 변수로 사용하는 것이 옳다.


문자열이나 리스트 형태로 정보를 받아 인스턴스 만들기

class User:
    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

    @classmethod
    def from_string(cls, string_param):
        # 코드를 쓰세요
        info = string_param.split(',')
        return cls(info[0],info[1],info[2])
    @classmethod
    def from_list(cls, list_param):
        # 코드를 쓰세요
        return cls(list_param[0],list_param[1],list_param[2])

# 유저 생성 및 초기값 설정
younghoon = User.from_string("강영훈,younghoon@codeit.kr,123456")
yoonsoo = User.from_list(["이윤수", "yoonsoo@codeit.kr", "abcdef"])

print(younghoon.name, younghoon.email, younghoon.password)
print(yoonsoo.name, yoonsoo.email, yoonsoo.password)
>>> 강영훈 younghoon@codeit.kr 123456
>>> 이윤수 yoonsoo@codeit.kr abcdef

지금까지 클래스 메서드에 대해서 알아보았습니다.

메서드의 종류가 기억이 나시나요?

메소드의 종류는 3가지

  1. 인스턴스 메소드
  2. 클래스 메소드
  3. 정적 메소드

다음으로는 정적 메서드에 대해서 알아보겠습니다.

3. 정적 메소드 : static method

class User:
    count = 0

    def __init__(self, name, email, pw):
        self.name = name
        self.email = email
        self.pw = pw

        User.count += 1

    def say_hello(self):
        print("안녕하세요! 저는 {}입니다!".format(self.name))

    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


print(User.is_valid_email("taehosung"))
print(User.is_valid_email("taehosung@codeit.kr"))

print(user1.is_valid_email("taehosung"))
print(user1.is_valid_email("taehosung@codeit.kr"))
>>>
False
True
False
True

지금 User 클래스에서 is_valid_email 메서드가 정적 메서드입니다.

정적 메서드는 메소드 정의 위에 @staticmethod 데코레이터를 표시해야 합니다.

is_valid_email 메소드는 파라미터 email_address로 받은 문자열에 @가 들어있는지 체크합니다.

정적 메소드는

  • 인스턴스 메서드의 self
  • 클래스 메소드의 cls

같은 자동 전달되는 파라미터가 없습니다.

그리고 정적 메서드는 아래 코드처럼 인스턴스, 클래스 두 가지 모두를 통해 사용 가능합니다.

정적 메소드는 언제 사용할까요?

# 인스턴스 메소드
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

User 클래스에는 인스턴스 메서드, 클래스 메소드, 정적 메소드가 있습니다.

  1. 인스턴스 메소드 __str__는 인스턴스 변수인 self.name, self.email을 사용하고,
  2. 클래스 메소드 number_of_user는 클래스 변수인 cls.count를 사용합니다.
  3. 하지만 is_valid_email 메서드에선 아무 변수도 사용하고 있지 않네요.

인스턴스 변수나 클래스 변수 중 아무것도 사용하지 않을 메서드라면 정적 메서드로 만들면 됩니다. 그러니까 어떤 속성을 다루지 않고, 단지 기능(행동)적인 역할만 하는 메서드를 정의할 때 정적 메소드로 정의하면 됩니다. 이제 여러분은 메소드의 종류에는

  • 인스턴스 메소드
  • 클래스 메소드
  • 정적 메소드

 

 

객체지향 프로그래밍에 대한 정의와 관련 질문 및 내용들을 다른 블로거 분께서 잘 정리해 주신 것 같아 첨부합니다.

https://jeong-pro.tistory.com/95

 

객체 지향 프로그래밍이 뭔가요? (꼬리에 꼬리를 무는 질문 1순위, 그놈의 OOP)

객체 지향 프로그래밍(Object Oriented Programming) 여러 SW기업 신입사원 기술면접에서 워밍업느낌으로 면접자들 긴장을 풀어줄 겸 처음으로 자주 나오는 질문이다. "객체 지향 프로그래밍에 대해 설명 한번 해..

jeong-pro.tistory.com

 

반응형

관련된 글 보기

Comments