from math import pi
class Rectangle:
"""직사각형 클래스"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
"""직사각형의 넓이를 리턴한다"""
return self.width * self.height
def perimeter(self):
"""직사각형의 둘레를 리턴한다"""
return 2 * self.width + 2 * self.height
class Circle:
"""원 클래스"""
def __init__(self, radius):
self.radius = radius
def area(self):
"""원의 넓이를 리턴한다"""
return pi * self.radius ** 2
def perimeter(self):
"""원의 둘레를 리턴한다"""
return 2 * pi * self.radius
class Paint:
"""그림판 프로그램 클래스"""
def __init__(self):
self.shapes = []
def add_shape(self, shape):
"""그림판에 도형을 추가한다"""
self.shapes.append(shape)
def total_area_of_shapes(self):
"""그림판에 있는 모든 도형의 넓이의 합을 구한다"""
return sum([shape.area() for shape in self.shapes])
def total_perimeter_of_shapes(self):
"""그림판에 있는 모든 도형의 둘레의 합을 구한다"""
return sum([shape.perimeter() for shape in self.shapes])
rectangle = Rectangle(3, 7)
circle = Circle(4)
paint_program = Paint()
paint_program.add_shape(rectangle)
paint_program.add_shape(circle)
print(paint_program.total_perimeter_of_shapes()) # 45.132741228718345
print(paint_program.total_area_of_shapes()) # 71.26548245743669
shape
인스턴스가 없음에도 shape.area
, shape.perimeter
가 정상 작동area
, perimeter
메소드를 갖고 있기 때문에 가능perimeter
, area
가 없기 때문class Cylinder:
"""원통 클래스"""
def __init__(self, radius, height):
self.radius = radius
self.height = height
paint_program = Paint()
cylinder = Cylinder(7, 4)
rectangle = Rectangle(3, 7)
circle = Circle(4)
paint_program.add_shape(cylinder)
paint_program.add_shape(circle)
paint_program.add_shape(rectangle)
print(paint_program.total_perimeter_of_shapes())
print(paint_program.total_area_of_shapes())
# AttributeError: 'Cylinder' object has no attribute 'perimeter'
Paint
클래스에 아래와 같이 조건문을 추가해주면 정상 작동class Paint:
def __init__(self):
self.shapes = []
def add_shape(self, shape):
# 조건문 추가
if isinstance(shape, Circle) or isinstance(shape, Rectangle):
self.shapes.append(shape)
else:
print("넓이 둘레를 구하는 메소드가 없는 도형은 추가할 수 없습니다!")
def total_area_of_shapes(self):
return sum([shape.area() for shape in self.shapes])
def total_perimeter_of_shapes(self):
return sum([shape.perimeter() for shape in self.shapes])
isinstance()
를 한개만 써줘도 됨from math import pi
class Shape:
"""공통 부모 클래스"""
def area(self):
"""도형의 넓이를 리턴한다: 자식 클래스가 오버라이딩할 것"""
pass
def perimeter(self):
"""도형의 둘레를 리턴한다: 자식 클래스가 오버라이딩할 것"""
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * self.width + 2 * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return pi * self.radius ** 2
def perimeter(self):
return 2 * pi * self.radius
class Cylinder:
def __init__(self, radius, height):
self.radius = radius
self.height = height
class Paint:
def __init__(self):
self.shapes = []
def add_shape(self, shape):
if isinstance(shape, Shape):
self.shapes.append(shape)
else:
print("넓이, 둘레를 구하는 메소드가 없는 도형은 추가할 수 없습니다.")
def total_area_of_shapes(self):
return sum([shape.area() for shape in self.shapes])
def total_perimeter_of_shapes(self):
return sum([shape.perimeter() for shape in self.shapes])
cylinder = Cylinder(7, 4)
rectangle = Rectangle(3, 7)
circle = Circle(4)
paint_program = Paint()
paint_program.add_shape(cylinder)
paint_program.add_shape(circle)
paint_program.add_shape(rectangle)
print(paint_program.total_perimeter_of_shapes())
print(paint_program.total_area_of_shapes())
# 넓이, 둘레를 구하는 메소드가 없는 도형은 추가할 수 없습니다.
# 45.132741228718345
# 71.26548245743669
area
, perimeter
메소드가 자식 클래스에 무조건 오버라이딩 되어야 함Shape
클래스에는 두 메소드가 pass
기 때문에 아무것도 실행되지 않음area
, perimeter
메소드를 무조건 오버라이딩하도록 강제해보자from math import pi
class Shape:
def area(self):
pass
def perimeter(self):
pass
class EquilateralTriangle(Shape):
"""정삼각형 클래스"""
def __init__(self, side):
self.side = side # Shape 클래스를 상속받지만 area, perimeter 메소드가 없음
class Paint:
def __init__(self):
self.shapes = []
def add_shape(self, shape):
if isinstance(shape, Shape):
self.shapes.append(shape)
else:
print("넓이, 둘레를 구하는 메소드가 없는 도형은 추가할 수 없습니다.")
def total_area_of_shapes(self):
return sum([shape.area() for shape in self.shapes])
def total_perimeter_of_shapes(self):
return sum([shape.perimeter() for shape in self.shapes])
triangle = EquilateralTriangle(4)
paint_program = Paint()
paint_program.add_shape(triangle)
print(paint_program.total_perimeter_of_shapes())
print(paint_program.total_area_of_shapes())
# TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
decorator
로 abstractmethod
를 갖는 메소드가 추상 메소드abc
로부터 클래스 ABC
와 메소드 abstractmethod
를 import
한다ABC
클래스를 상속받는다decorator
로 abstractmethod
를 써준다from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
shape = Shape()
# TypeError: Can't instantiate abstract class Shape with abstract methods area, perimeter
Shape
의 자식 클래스는 반드시 두 메소드를 오버라이딩 해야 함from math import pi, sqrt # sqrt : 제곱근 구하는 함수
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class EquilateralTriangle(Shape):
"""정삼각형 클래스"""
def __init__(self, side):
self.side = side
triangle = EquilateralTriangle(4)
paint_program = Paint()
paint_program.add_shape(triangle)
print(paint_program.total_perimeter_of_shapes())
print(paint_program.total_area_of_shapes())
# TypeError: Can't instantiate abstract class EquilateralTriangle with abstract methods area, perimeter
ABC
를 상속받고 추상 메소드도 오버라이딩 없이 그대로 상속받은 상태from math import pi, sqrt # 제곱근 구하는 함수
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float:
pass
@abstractmethod
def perimeter(self) -> float:
pass
class EquilateralTriangle(Shape):
"""정삼각형 클래스"""
def __init__(self, side):
self.side = side
def area(self):
"""정삼각형의 넓이를 리턴한다"""
return sqrt(3) * self.side * self.side / 4
def perimeter(self):
"""정삼각형의 둘레를 리턴한다"""
return 3 * self.side
triangle = EquilateralTriangle(4)
paint_program = Paint()
paint_program.add_shape(triangle)
print(paint_program.total_perimeter_of_shapes())
print(paint_program.total_area_of_shapes())
# 12
# 6.928203230275509
type hinting
을 해주는 게 좋음from math import pi, sqrt
from abc import ABC, abstractmethod
class Shape(ABC):
def area(self):
pass
def perimeter(self):
pass
@property
@abstractmethod
def x(self): # getter 메소드이자 추상 메소드
"""도형의 x 좌표 getter 메소드"""
pass
@property # 순서가 뒤바뀌면 안됨
@abstractmethod
def y(self): # getter 메소드이자 추상 메소드
"""도형의 y 좌표 getter 메소드"""
pass
class EquilateralTriangle(Shape):
def __init__(self, x, y, side):
self._x = x
self._y = y
self.side = side
def area(self):
return sqrt(3) * self.side * self.side / 4
def perimeter(self):
return 3 * self.side
@property
def x(self):
"""_x getter 메소드"""
return self._x
@x.setter
def x(self, value):
"""_x setter 메소드"""
self._x = value
@property
def y(self):
"""_y getter 메소드"""
return self._y
@y.setter
def y(self, value):
"""_y setter 메소드"""
self._y = value
equilateral_triangle = EquilateralTriangle(5, 6, 4)
equilateral_triangle.x = 10
print(equilateral_triangle.x) # 10
하지만 추상 클래스 속 일반 메소드가 겹친다면 문제 발생
from abc import ABC, abstractmethod
class Message(ABC):
@abstractmethod
def print_message(self) -> None:
pass
class Sendable(ABC):
@abstractmethod
def send(self, destination: str) -> None:
pass
class Email(Message, Sendable):
def __init__(self, content, user_email):
self.content = content
self.user_email = user_email
def print_message(self):
print("이메일 내용입니다:\n{}".format(self.content))
def send(self, destination):
print("이메일 주소 {}에서 {}로 보냅니다!".format(self.user_email, destination))
email = Email("안녕? 오랜만이야 잘 지내니?", "jahyeon@gmail.com")
email.print_message()
email.send("goldstlme@gmail.com")
# 이메일 내용입니다:
# 안녕? 오랜만이야 잘 지내니?
# 이메일 주소 jahyeon@gmail.com에서 goldstlme@gmail.com로 보냅니다!
def new_print(value_1, value_2 = None, value_3 = None):
if value_3 is None:
if value_2 is None:
print(value_1)
else:
print("{} {}".format(value_1,value_2))
else:
print("{} {} {}".format(value_1, value_2, value_3))
new_print("this") # this
new_print(0.5678) # 0.5678
new_print("this", "that") # this that
new_print("this", "that", 3) # this that 3
def print_name(first_name, last_name, email=""):
print("{}{} {}".format(last_name, first_name, email))
print_name("자현", "구", "jahyeongu@gmail.com")
print_name(first_name="자현", last_name="구",email="jahyeongu@gmail.com")
print_name(last_name="구",email="jahyeongu@gmail.com", first_name="자현")
print_name(last_name="구", first_name="자현")
# 구자현 jahyeongu@gmail.com
# 구자현 jahyeongu@gmail.com
# 구자현 jahyeongu@gmail.com
# 구자현
def print_message_and_add_numbers(message, *numbers):
print(message)
return sum(numbers)
print(print_message_and_add_numbers("test1", 7, 3, 5))
print(print_message_and_add_numbers("test2", 1, 2, 3, 4))
print(print_message_and_add_numbers("test3", 2, 4, 6, 8, 10))
# test1
# 15
# test2
# 10
# test3
# 30
class Paint:
def add_shape(self, shape):
if isinstance(shape, Shape):
self.shapes.append(shape)
else:
print("넓이, 둘레를 구하는 메소드가 없는 도형은 추가할 수 없습니다.")
def total_area_of_shapes(self):
return sum([shape.area() for shape in self.shapes])
def add_shape(self, shape):
self.shapes.append(shape)
def total_area_of_shapes(self):
total_area = 0
for shape in self.shapes:
try:
total_area += shape.area()
except (AttributeError, TypeError):
print("그림판에 area 메소드가 없거나 잘못 정의되어 있는 인스턴스 {}가 있습니다.".format(shape))