객체 지향 프로그래밍, 클래스

정재욱·2022년 7월 12일
0

Development_common_sense

목록 보기
2/4

참고글

📕객체 지향 프로그래밍

객체란?

  • '속성''행동'으로 이루어진 것.

    • 자동차는 바퀴 4개, 색깔, 의자, 핸들 등의 속성과 전진, 후진 등의 행동을 가진 객체
  • '속성'과 '행동'을 떠올릴 수 있는 것이라면, 그것이 현실에 존재하던 가상에 존재하던 모두 객가 될 수 있다.

  • 파이썬에서 속성은 변수로, 행동은 함수로 나타낸다.

객체지향 프로그래밍이란?

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

즉, 프로그램을 객체들 간의 소통으로 바라보는 것.

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

  • 프로그램에 어떤 객체들이 필요할지 정한다.
  • 객체들의 속성과 행동을 정한다.
  • 객체들이 서로 어떻게 소통할지 정한다.

객체지향

인스턴스 변수 정의하기
인스턴스 변수 : 클래스 정의에서 메서드 안에서 사용되면서 self.변수명 처럼 사용되는 변수

  • 각 객체별로 서로 다른 값을 가짐
  • 클래스 내부에서는 self.인스턴스 변수명을 사용하여 엑세스, 클래스 밖에서는 객체명.인스턴스변수명 으로 엑세스
class User:
    pass

user1 = User()
user1.name = "김"
user1.email = "kim@gmail.com"

user2 = User()
user2.name = "이"
user2.email = "lee@gmail.com"

인스턴스 메서드 정의하기
인스턴스 메서드 : 해당 객체 안에서 호출(self.메서드명)

  • 해당 메서드를 호출한 객체에만 영향을 미침
  • 객체 속성에 접근 가능
class User:
	def say_hello(some_user):
    	print(f"안녕, 나는 {some_user.name}이야!")
    pass

user1 = User()
user1.name = "김"

user2 = User()
user2.name = "이"

User.say_hello(user1) # 클래스에서 메서드 호출
print(user1)
user2.say_hello()  # 인스턴스에서 메서드 호출
print(user2)

>>>
안녕, 나는 김이야!
<class '__main__.User'>
안녕, 나는 이이야!
<__main__.User object at 0x0000023127E686A0>

user2.say_hello()에 user2을 인자로 넘기지 않아도 호출이 된다.

인스턴스가 메서드를 호출할 때는 자기 자신이 첫 번째 인자로 넘어간다!

self를 사용하자

이를 명시해주기 위해 파이썬에서는 인스턴스 메서드의 첫 파라미터로 "self"를 사용한다.

이렇게 안해도 에러가 나는건 아니지만, 이건 파이썬 쓰는 사람들끼리의 규칙이다.

class User:
	def say_hello(self):  # self 사용
    	print(f"안녕, 나는 {self.name}이야!")
    pass

user1 = User()
user1.name = "김"

user2 = User()
user2.name = "이"

User.say_hello(user1) # 클래스에서 메서드 호출
user2.say_hello()  # 인스턴스에서 메서드 호출

>>>
안녕, 나는 김이야!
안녕, 나는 이이야!

user2.say_hello() 에서 user2가 def say_hello(self):의 self로 자동으로 들어간다.

init

  • 언더바 두개에 쌓여있는 메서드를 magic method , 특수 메소드라고 부른다.
  • magic method는 특정 상황에서 자동으로 호출되는 메소드다.
  • __init__메서드는 클래스 생성시 자동으로 호출된다.
class User:
    def initialize(self, name, email):
        self.name = name
        self.email = email

user1 = User()
user1.initialize("김코딩", "hi@gmail.com")

__init__메서드 없이는 객체 속성을 지정하기 위해 위처럼 해야한다.

__init__메서드는 객체를 생성할 때 인자를 넘겨주면 되므로 훨씬 간단해진다.

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

user1 = User("김코딩", "kim@gmail.com")

user1 = User("김코딩", "kim@gmail.com") 요 코드 한 줄이 실행되면 1) User의 인스턴스 하나가 생성됨, 2) init메서드가 호출됨 한 번에 객체를 생성하고 속성을 지정해줄 수 있다는 장점 때문에 보통 클래스를 만들땐 항상 init 메서드가 같이 호출된다.

str
위 코드에서 print(user1)을 해보면 <__main__.User object at 0x7fc9ab5c9670>이런 값이 나온다.
인스턴스를 출력 했을 때 원하는 값이 나오게 하려면 __str__을 사용하면 된다.

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

user1 = User("김코딩", "kim@gmail.com")

print(user1)

>>>
김코딩

클래스 변수
클래스 변수 : 클래스 정의에서 메서드 밖에 존재하는 변수

  • 해당 클래스를 사용하는 모두에게 공용으로 사용되는 변수
  • 클래스 변수는 클래스 내외부에서 클래스명.변수명으로 엑세스 가능
class User:
    count = 0

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

    def __str__(self):
        return self.name

User.count = 1
print(User.count)
>>>
1
  • 그렇다면 count변수가 User인스턴스의 총 개수를 나타나게 하려면 어떻게 해야할까?
  • User인스턴스가 생성될 때 마다 count +=1을 해주면 된다.
  • User인스턴스가 생성될 때마다 __init__메서드가 호출되므로 거기에 더해주면 된다.
class User:
    count = 0

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

        User.count += 1

    def __str__(self):
        return self.name


user1 = User("김", "kim@gmail.com")
user2 = User("이", "lee@gmail.com")
user3 = User("박", "park@gmail.com")

print(User.count)
>>>
3

클래스 변수를 출력하고 할당하는 방법에 대해 좀 더 살펴보자.

...생략....
user1 = User("김", "kim@gmail.com")
user2 = User("이", "lee@gmail.com")
user3 = User("박", "park@gmail.com")

user2.count = 10
print(User.count)
print(user1.count)
print(user2.count)
print(user3.count)

>>>
3
3
10
3
  • user2.count를 새로 정의한 부분때문에 그것만 10값이 나왔다.
  • 그런데 user2.count=10은 인스턴스 변수를 할당해준 것이지, 클래스 변수를 할당한게 아니다.
  • 즉 같은 이름을 가진 클래스 변수와 인스턴스 변수가 존재하는 것이고, 이때는 인스턴스 변수가 선행한다.

데코레이터
함수를 인자로 받아 꾸며진 함수를 리턴하는 함수

def print_hello():
    print("안녕")


def add_print_to(original):
    def wrapper():
        print("함수 시작")
        original()
        print("함수 끝")
    return wrapper

add_print_to(print_hello)()

>>>
함수 시작
안녕
함수 끝
  • 어떤 함수를 데코레이터 함수에 넣으려면 @를 사용하면 된다.
def add_print_to(original):
    def wrapper():
        print("함수 시작")
        original()
        print("함수 끝")
    return wrapper

@add_print_to
def print_hello():
    print("안녕")

print_hello()

>>>
함수 시작
안녕
함수 끝

클래스 메소드

  • 클래스 메소드는 메소드 위에 @classmethod 데코레이터를 달아주면 된다.
  • 클래스 메소드 첫 인자는 self가 아닌 cls를 사용한다.
  • 클래스 변수에 접근이 가능하여 cls.클래스변수명으로 엑세스 가능하다. 단, 객체 속성/메소드는 접근 불가
  • 클래스 메소드는 클래스를 통해 호출할 수도 있고, 인스턴스를 통해 할 수도 있다.
class User:
    count = 0

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

        User.count += 1

    def __str__(self):
        return self.name

    @classmethod
    def number_of_users(cls):
        print(f"총 유저수는 {cls.count}입니다.")


user1 = User("김", "kim@gmail.com")
user2 = User("이", "lee@gmail.com")
user3 = User("박", "park@gmail.com")

User.number_of_users()  # 클래스를 통해 클래스 메서드 호출
user1.number_of_users()  # 인스턴스를 통해 클래스 메서드 호출

>>>
총 유저수는 3입니다.
총 유저수는 3입니다.

인스턴스 메소드 vs 클래스 메소드

  • 인스턴스 메소드: User.say_hello(user1) 또는 user1.say_hello()
  • 클래스 메소드: User.number_of_users() 또는 user1.number_of_users()
  • 클래스 메소드는 클래스가 첫 인자로 자동 전달된다.
  • 이렇게 될 수 있는 이유는 @classmethod 데코레이터 떄문이다.

언제 각 메소드를 사용해야 할까?

  • 위에서 정의한 classmethod는 사실 instance method 만들어도 문제는 없다.
  • 언제 어떤 메소드를 쓰냐의 기준은 어떤 변수를 쓰냐에 달렸다.
  • 클래스 변수를 사용한다면 클래스 메서드, 인스턴스 변수를 사용한다면 인스턴스 메서드를 사용하자.
  • 만약 클래스 변수, 인스턴스 변수 둘 다 사용한다면 -> 인스턴스 메소드를 사용해라!
  • 인스턴스 메서드는 인스턴스, 클래스 변수 둘 다 사용 가능하지만, 클래스 메서드는 인스턴스 변수를 사용 못하기 때문.
  • 인스턴스가 하나도 없을 때에도 사용될 가능성이 있는 메서드라면 클래스 메서드를 사용하자!

정적 메소드(static method)

  • 인스턴스 변수나 클래스 변수 중 아무것도 사용하지 않을 때 static method사용.
  • 객체 속성에 접근 불가
  • static method는 첫 인자로 self, cls 중 어느것도 자동으로 넘어가지 않는다.
...생략...
    @staticmethod
    def is_valid_email(email_address):
        return "@" in email_address


user1 = User("김", "kim@gmail.com")
user2 = User("이", "lee@gmail.com")
user3 = User("박", "park@gmail.com")

print(User.is_valid_email("email"))  # 클래스를 통해 클래스 메서드 호출
print(user1.is_valid_email("email@email.com")) 

>>>
False
True

private, protected, public

  • 정보 은닉 방식
    • class의 attribute, method에 대해 접근을 제어할 수 있는 기능
  • private -> protected -> public
    • private : private로 선언된 attribute, method는 해당 클래스에서만 접근 가능
      • 해당 속성의 앞에 __(double underscore)를 붙이면 접근이 허용되지 않음
    • protected : protected로 선언된 attribute, method는 해당 클래스 또는 해당 클래스를 상속받은 클래스에서만 접근 가능
      • 해당 속성의 앞에 _(single underscore)를 붙여서 표기
    • public : public으로 선언된 attribute, method는 어떤 클래스라도 접근 가능

파이썬에서의 private, protected, public

  • java, C++언어 등의 객체지향 언어와 달리, 파이썬에서의 모든 attribute, methodsms 기본적으로 public
  • 클래스 외부에서 attribute, method 접근 가능

Public

class Quadrangle:
	def __init__(self, width, height, color):
    	self.width = width
        self.height = height
        self.color = color
        
    def get_area(self):
    	return self.width * self.height
        
    def set_area(self, width, height):
    	self.width = width
        self.height = height

square = Quadrangle(5, 5, 'black')
print(square.get_area())
print(square.width)
square.width = 10
print(square.get_area())

>>>
25
5
50

Private

class Quadrangle:
	def __init__(self, width, height, color):
    	self.__width = width
        self.__height = height
        self.__color = color
        
    def get_area(self):
    	return self.__width * self.__height
        
    def __set_area(self, width, height):
    	self.__width = width
        self.__height = height
        
square = Quadrangle(5, 5, 'black')
print(square.__set_area(10, 10))

>>>
AttributeError : 'Quadrangle' object has no attribute '__set_area'

미리 알아야 할 것들

1) 파이썬은 순수 객체 지향 언어다 = 모든 것이 객체다

print(type(1))
print(type("a"))
print(type([]))
print(type({}))
print(type(()))

>>>
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'tuple'>

2) 파이썬 가변 타입 vs 불변타입

  • 직접 작성하는 클래스는 가변타입이다.
  • 내장 클래스 가변/불변 여부

3) 절차지향 vs 객체지향

  • 객체지향 개념이 등장하기 이전에 절차지향이있었다.
  • 절차지향에는 객체 개념이 없는 대신 '함수'개념이 쓰인다

4) 파이썬은 동적 타입 언어이다.

  • type 지정을 해주지 않아도 된다.
  • 반대 말인 정적 타입 언어는 변수를 선언할 때 꼭 타입을 지정해주어야 한다.

5) type hinting

  • 무슨 타입인지 설명해주는 코드를 작성할 수 있다.
  • 방법은 변수명: type명이다.
  • 예시: name: str = "kim"
  • type hinting대로 작성하지 않아도 에러가 나진 않는다.
  • python 3.5 이후부터만 사용 가능.
profile
AI 서비스 엔지니어를 목표로 공부하고 있습니다.

0개의 댓글