: 프로그램을 여러 개의 독립된 객체들과 그 객체들 간의 상호작용으로 파악하는 프로그래밍 접근법이다.
: 프로그램을 객체들 간의 소통으로 바라보는 것.
class User:
pass
user1.name = "김"
user1.email = "kim@gmail.com"
user2.name = "이"
user2.email = "lee@gmail.com"
예시
class User:
def say_hello(some_user):
print(f"안녕, 나는 {some_user.name}이야!")
pass
user1.name = "김"
user1.email = "kim@gmail.com"
user2.name = "이"
user2.email = "lee@gmail.com"
User.say_hello(user1) # 클래스에서 메서드 호출
user1.say_hello() # 인스턴스에서 메서드 호출
결과:
안녕, 나는 김이야!
안녕, 나는 김이야!
user1.say_hello()에 user1을 인자로 넘기지 않아도 호출이 된다.
넘기는 순간 에러가 발생한다.
인스턴스가 메서드를 호출할 때는 자기 자신이 첫 번째 인자로 넘어간다!
인스턴스가 메서드를 호출할 때는 자기 자신이 첫 번째 인자로 넘어간다!
이를 명시해주기 위해 파이썬에서는 인스턴스 메서드의 첫 파라미터로 "self"를 사용한다.
이렇게 안해도 에러가 나는건 아니지만, 이건 파이썬 쓰는 사람들끼리의 규칙이다.
__init__
__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 정의 부분 바로 아래 변수를 정의해주면 된다.
예시를 보자.
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
__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
: 함수를 인자로 받아 꾸며진 함수를 리턴하는 함수
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()
결과값
함수 시작
안녕
함수 끝
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입니다.
User.say_hello(user1)
또는 user1.say_hello()
User.number_of_users()
또는 user1.number_of_users()
...생략...
@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
print(type(1))
print(type("a"))
print(type([]))
print(type({}))
print(type(()))
결과값
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'tuple'>
파이썬은 모든 것이 클래스이다.
help(class_name)
을 사용하면 해당 클래스의 모든 docstring이 출력된다.1). Google docstring
"""유저에게 추천할 영상을 찾아준다
Parameters:
number_of_suggestions (int): 추천하고 싶은 영상 수
(기본값은 5)
Returns:
list: 추천할 영상 주소가 담긴 리스트
"""
2). ReStructuredText(파이썬 공식 문서화 기준)
"""유저에게 추천할 영상을 찾아준다
:param number_of_suggestions: 추천하고 싶은 영상 수
(기본값은 5)
:type number_of_suggestions: int
:returns: 추천할 영상 주소가 담긴 리스트
:rtype: list
"""
3). NumPy/SciPy (통계, 과학 분야에서 쓰이는 Python 라이브러리)
"""유저에게 추천할 영상을 찾아준다
Parameters
----------
number_of_suggestions: int
추천하고 싶은 영상 수 (기본값은 5)
Returns
-------
list
추천할 영상 주소가 담긴 리스트
"""
변수명: type명
이다.class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.set_age(age)
self.__resident_id = resident_id
def authenticate(self, id_field):
"""본인이 맞는지 확인하는 메소드"""
return self.__resident_id == id_field
def can_drink(self):
"""음주 가능 나이인지 확인하는 메소드"""
return self.__age >= Citizen.drinking_age
def __str__(self):
"""주민 정보를 문자열로 리턴하는 메소드"""
return self.name + "씨는 " + str(self.__age) + "살입니다!"
def get_age(self):
"""숨겨 놓은 인스턴스 변수 __age의 값을 받아오는 메소드"""
return self.__age
def set_age(self, value):
"""숨겨 놓은 인스턴스 변수 __age의 값을 설정하는 메소드"""
if value < 0:
print("나이는 0보다 작을 수 없습니다. 기본 값 0으로 나이를 설정하겠습니다")
self.__age = 0
else:
self.__age = value
# 주민 인스턴스 생성
young = Citizen("younghoon kang", 18, "87654321")
print(young.__str__()) # 출력: younghoon kang씨는 18살입니다!
print(young.__authenticate("87654321")) # 에러가 난다!!!
__
가 붙은 변수에 접근하는 메서드캡술화된 변수의 값을 읽어주는 메서드는 getter, 할당해 주는 메서드를 setter라고 한다.
print(young.age)
이 실행되면 데코레이터에 덕에 @property가 달려있는 age 메서드(getter)가 실행된다.young.age = 30
이 실행되면 데코레이터 덕에 @age.setter가 자동으로 실행된다.class Mom:
pass
class Son(Mom):
pass
print(Son.mro())
결과값:
[<class '__main__.Son'>, <class '__main__.Mom'>, <class 'object'>]
class Mom:
pass
class Son(Mom):
pass
son = Son()
print(isinstance(son, Mom))
print(isinstance(son, object))
print(isinstance(son, list))
결과값:
True
True
False
class Mom:
pass
class Son(Mom):
pass
son = Son()
print(issubclass(Son, Mom))
print(issubclass(Son, object))
print(issubclass(Son, list))
결과값:
True
True
False
__init__
(똑같은 인자 념겨주기)로 상속을 명시할 수 있다. (이렇게 안해도 자동으로 상속은 된다.)class Employee:
"""직원 클래스"""
company_name = "코드잇 버거"
raise_percentage = 1.03
def __init__(self, name, wage):
"""인스턴스 변수 설정"""
self.name = name
self.wage = wage
class DeliveryMan(Employee):
"""배달원 클래스"""
raise_percentage = 1.1
def __init__(self, name, wage, on_standby):
super().__init__(name, wage)
self.on_standby = on_standby
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, b):
self.b = b
class C(A, B):
super().__init__
을 실행하면 A, B 중 어떤 클래스의 메서드가 실행되는 걸까?class C(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b)
class C(A, B):
def __init__(self, a, b):
self.a = a
self.b = b
class Ship:
"""배 클래스"""
def __init__(self, fuel, fuel_per_hour, supplies, num_crew):
"""연료량, 시간당 연료 소비량, 물자량, 선원 수를 인스턴스 변수로 갖는다"""
self.fuel = fuel
self.fuel_per_hour = fuel_per_hour
self.supplies = supplies
self.num_crew = num_crew
def report_fuel(self):
"""연료량 보고 메소드"""
print("현재 연료는 {}l 남아 있습니다".format(self.fuel))
def load_fuel(self, amount):
"""연료 충전 메소드"""
self.fuel += amount
def report_supplies(self):
"""물자량 보고 메소드"""
print("현재 물자는 {}명분이 남아 있습니다".format(self.supplies))
def load_supplies(self, amount):
"""물자 보급 메소드"""
self.supplies += amount
def distribute_supplies_to_crew(self):
"""물자 배분 메소드"""
if self.supplies >= self.num_crew:
self.supplies -= self.num_crew
return True
print("물자가 부족하기 때문에 배분할 수 없습니다")
return False
def report_crew(self):
"""선원 수 보고 메소드"""
print("현재 선원 {}명이 있습니다".format(self.num_crew))
def load_crew(self, number):
"""선원 승선 메소드"""
self.num_crew += number
def run_engine_for_hours(self, hours):
"""엔진 작동 메소드"""
if self.fuel > self.fuel_per_hour * hours:
self.fuel -= self.fuel_per_hour * hours
print("엔진을 {}시간 동안 돌립니다!".format(hours))
else:
print("연료가 부족하기 때문에 엔진 작동을 시작할 수 없습니다")
class Ship:
"""배 클래스"""
def __init__(self, fuel, fuel_per_hour, supplies, num_crew):
self.fuel_tank = FuelTank(fuel)
self.crew_manager = CrewManager(num_crew)
self.supply_hold = SupplyHold(supplies, self.crew_manager)
self.engine = Engine(self.fuel_tank, fuel_per_hour)
class FuelTank:
"""연료 탱크 클래스"""
def __init__(self, fuel):
"""연료 탱크에 저장된 연료량을 인스턴스 변수로 갖는다"""
self.fuel = fuel
def load_fuel(self, amount):
"""연료 충전 메소드"""
self.fuel += amount
def use_fuel(self, amount):
"""연료 사용 메소드"""
if self.fuel - amount >= 0:
self.fuel -= amount
return True
print("연료가 부족합니다!")
return False
def report_fuel(self):
"""연료량 보고 메소드"""
print("현재 연료는 {}l 남아 있습니다".format(self.fuel))
class Engine:
"""엔진 클래스"""
def __init__(self, fuel_tank, fuel_per_hour):
"""연료 탱크 인스턴스와 시간당 연료 소비량을 인스턴스 변수로 갖는다"""
self.fuel_tank = fuel_tank
self.fuel_per_hour = fuel_per_hour
def run_for_hours(self, hours):
"""엔진 작동 메소드, 연료 탱크 인스턴스를 사용한다"""
if self.fuel_tank.use_fuel(self.fuel_per_hour * hours):
print("엔진을 {}시간 동안 돌립니다!".format(hours))
return True
print("연료가 부족하기 때문에 엔진 작동을 시작할 수 없습니다")
return False
class CrewManager:
"""선원 관리 클래스"""
def __init__(self, num_crew):
"""승선한 선원 수를 인스턴스 변수로 갖는다"""
self.num_crew = num_crew
def load_crew(self, number):
"""선원 승선 메소드"""
self.num_crew += number
def report_crew(self):
"""선원 수 보고 메소드"""
print("현재 선원 {}명이 있습니다".format(self.num_crew))
class SupplyHold:
"""물자 창고 클래스"""
def __init__(self, supplies, crew_manager):
"""물자량과 선원 관리 인스턴스를 인스턴스 변수로 갖는다"""
self.supplies = supplies
self.crew_manager = crew_manager
def load_supplies(self, amount):
"""물자 충전 메소드"""
self.supplies += amount
def distribute_supplies_to_crew(self):
"""물자 배분 메소드, 각 선원들에게 동일한 양의 물자를 배분한다"""
if self.supplies >= self.crew_manager.num_crew:
self.supplies -= self.crew_manager.num_crew
return True
print("물자가 부족하기 때문에 배분할 수 없습니다")
return False
def report_supplies(self):
"""물자량 보고 메소드"""
print("현재 물자는 {}명분이 남아 있습니다".format(self.supplies))
class Employee:
"""직원 클래스"""
company_name = "코드잇 버거"
raise_percentage = 1.03
def __init__(self, name, wage):
self.name = name
self._wage = wage
def raise_pay(self):
"""직원 시급을 인상하는 메소드"""
self._wage *= self.raise_percentage
@property
def wage(self):
return self._wage
def __str__(self):
"""직원 정보를 문자열로 리턴하는 메소드"""
return Employee.company_name + " 직원: " + self.name
class Cashier(Employee):
"""리스코프 치환 원칙을 지키지 않는 계산대 직원 클래스"""
burger_price = 4000
def __init__(self, name, wage, number_sold=0):
super().__init__(name, wage)
self.number_sold = number_sold
def raise_pay(self, raise_amount):
"""직원 시급을 인상하는 메소드"""
self.wage += self.raise_amount
@property
def wage(self):
return "시급 정보를 알려줄 수 없습니다"
class Rectangle:
"""직사각형 클래스"""
def __init__(self, width, height):
"""세로와 가로"""
self.width = width
self.height = height
def area(self):
"""넓이 계산 메소드"""
return self.width * self.height
@property
def width(self):
"""가로 변수 getter 메소드"""
return self._width
@width.setter
def width(self, value):
"""가로 변수 setter 메소드"""
self._width = value if value > 0 else 1
@property
def height(self):
"""세로 변수 getter 메소드"""
return self._height
@height.setter
def height(self, value):
"""세로 변수 setter 메소드"""
self._height = value if value > 0 else 1
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
@property
def width(self):
"""가로 변수 getter 메소드"""
return self._width
@width.setter
def width(self, value):
"""가로 변수 setter 메소드"""
self._width = value if value > 0 else 1
self._height = value if value > 0 else 1
@property
def height(self):
"""세로 변수 getter 메소드"""
return self._height
@height.setter
def height(self, value):
"""세로 변수 setter 메소드"""
self._width = value if value > 0 else 1
self._height = value if value > 0 else 1
from abc import ABC, abstractmethod
class IMessage(ABC):
@property
@abstractmethod
def content(self):
"""추상 getter 메소드"""
pass
@abstractmethod
def edit_content(self, new_content: str) -> None:
"""작성한 메시지를 수정하는 메소드"""
pass
@abstractmethod
def send(self, destination: str) -> bool:
"""작성한 메시지를 전송하는 메소드"""
pass
class Email(IMessage):
def __init__(self, content, owner_email):
"""이메일은 그 내용과 보낸 사람의 이메일 주소를 인스턴스 변수로 가짐"""
self._content = content
self.owner_email = owner_email
@property
def content(self):
"""_content 변수 getter 메소드"""
return self._content
def edit_content(self, new_content):
"""이메일 내용 수정 메소드"""
self._content = self.owner_email + "님의 메일\n" + new_content
def send(self, destination):
"""이메일 전송 메소드"""
print("{}에서 {}로 이메일 전송!\n내용: {}").format(self.owner_email, destination, self._content)
return True
class TextMessage(IMessage):
def __init__(self, content):
"""문자 메시지는 그 내용을 인스턴스 변수로 가짐"""
self._content = content
@property
def content(self):
"""_content 변수 getter 메소드"""
return self._content
def edit_content(self, new_content):
"""문자 메시지 내용 수정 메소드"""
self._content = new_content
def send(self, destination):
"""문자 메시지 전송 메소드"""
print("{}로 문자 메시지 전송!\n내용: {}").format(destination, self._content)
class TextReader:
"""인스턴스의 텍스트 내용을 읽어주는 클래스"""
def __init__(self):
self.texts = []
def add_text(self, text: IMessage):
"""인스턴스 추가 메소드, 파라미터는 IMessage 인터페이스를 상속받을 것"""
self.texts.append(text)
def read_all_texts(self):
"""인스턴스 안에 있는 모든 텍스트 내용 출력"""
for text in self.texts:
print(text.content)
이에 대한 해결책은 추상 클래스로 상위 모듈과 하위 모듈 사이에 추상화 레이어를 만드는 것입니다. 이렇게 되면
1. 상위 모듈에는 추상 클래스의 자식 클래스의 인스턴스를 사용한다는 가정 하에 그 하위 모듈을 사용하는 코드를 작성해두면 되고,
2. 하위 모듈은 추상 클래스의 추상 메소드들을 구현(오버라이딩)만 하면 됩니다.
class Sword:
"""검 클래스"""
def __init__(self, damage):
self.damage = damage
def slash(self, other_character):
"""검 사용 메소드"""
other_character.get_damage(self.damage)
class GameCharacter:
"""게임 캐릭터 클래스"""
def __init__(self, name, hp, sword: Sword):
self.name = name
self.hp = hp
self.sword = sword
def attack(self, other_character):
"""다른 유저를 공격하는 메소드"""
if self.hp > 0:
self.sword.slash(other_character)
else:
print(self.name + "님은 사망해서 공격할 수 없습니다.")
def change_sword(self, new_sword):
"""검을 바꾸는 메소드"""
self.sword = new_sword
def get_damage(self, damage):
"""캐릭터가 공격받았을 때 자신의 체력을 깎는 메소드"""
if self.hp <= damage:
self.hp = 0
print(self.name + "님은 사망했습니다.")
else:
self.hp -= damage
def __str__(self):
"""남은 체력을 문자열로 리턴하는 메소드"""
return self.name + "님은 hp: {}이(가) 남았습니다.".format(self.hp)
와...정말 많은 도움이 되었습니다. 감사합니다.