Java 개발자를 위한 Python OOP

발행: (2026년 2월 16일 오전 04:56 GMT+9)
12 분 소요
원문: Dev.to

I’m happy to translate the article for you, but I need the full text of the post in order to do so. Could you please paste the content you’d like translated (excluding any code blocks or URLs you want to keep unchanged)? Once I have the text, I’ll provide a Korean translation while preserving the original formatting and markdown.

Java 버전

Square Shape 이다. 위치(x, y)와 변의 길이로 정의됩니다.

Square은 Shape을 상속합니다

abstract class Shape {
    abstract int area();
    abstract int perimeter();
}

public class Square extends Shape {
    private int x;
    private int y;
    private int side;

    public Square(int x, int y, int side) {
        this.x = x;
        this.y = y;
        this.side = side;
    }

    public Square(int x1, int y1, int x2, int y2) {
        if (Math.abs(x2 - x1) != Math.abs(y2 - y1)) {
            throw new IllegalArgumentException("Square must have equal sides");
        }
        x = x1;
        y = y1;
        side = Math.abs(x2 - x1);
    }

    @Override
    public int area() {
        return side * side;
    }

    @Override
    public int perimeter() {
        return 4 * side;
    }

    @Override
    public String toString() {
        return String.format("Square[x=%d,y=%d,side=%d]", x, y, side);
    }
}

이 글은 Java 개발자를 대상으로 하며, Java 코드는 자세히 설명하지 않았습니다. 댓글로 질문을 자유롭게 남겨 주세요.

Source:

Python으로 번역하기

클래스 정의

class Square(Shape):
    pass   # empty blocks are not allowed in Python, so we’ll fill it later

생성자 (Python은 __init__ 메서드가 하나만 있음)

Python에서는 선언과 초기화가 같은 위치에서 이루어지므로, 필드는 __init__ 안에 선언합니다.

def __init__(self, x, y, side):
    self.x = x
    self.y = y
    self.side = side

인스턴스를 만들 때 new 키워드를 사용하지 않습니다:

s1 = Square(1, 3, 10)
s2 = Square(x=1, y=3, side=10)

Python은 메서드 오버로드를 지원하지 않으므로, 두 번째 Java 생성자(네 개의 매개변수)는 또 다른 __init__으로 표현할 수 없습니다. 나중에 @classmethod로 구현할 것입니다.

캡슐화

Python에는 public / protected / private 키워드가 없습니다. 관례적으로 앞에 언더스코어(_)를 붙이면 “private”라는 의미가 됩니다.

명시적인 self 인자

모든 인스턴스 메서드는 첫 번째 매개변수로 인스턴스를 받으며, 전통적으로 self라고 명명합니다. 이는 Java의 this와 유사하지만, self키워드가 아닙니다.

def area(self):
    return self.side * self.side

def perimeter(self):
    return 4 * self.side

문자열 표현

Python은 Java의 toString 대신 __str____repr__를 사용합니다.

  • __str__ – 사용자 친화적인 표현 (printstr()이 사용)
  • __repr__ – 명확한 표현, 가능하면 객체를 재생성할 수 있는 형태
def __str__(self):
    return f"Square [side={self.side}]"

def __repr__(self):
    return f"Square(side={self.side})"
sq = Square(5, 0, 5)
print(sq)               # Square [side=5]
print(str(sq) + ".")    # Square [side=5].
# print(sq + ".") would raise a TypeError because concatenation with a non‑string is not allowed.
메서드목적호출 위치
__repr__생성자와 같은 표현 반환repr() 또는 인터랙티브 인터프리터
__str__읽기 쉬운 문자열 반환str(), print()

정적 메서드와 클래스 메서드

Java에서 static 메서드는 클래스에 속합니다. Python은 정적 메서드(@staticmethod)와 클래스 메서드(@classmethod)를 구분합니다.

정적 메서드 예시

public static double calcSideLength(double area) {
    return Math.sqrt(area);
}
import math

@staticmethod
def calc_side_length(area):
    return math.sqrt(area)   # `math`은 클래스가 아니라 모듈입니다

정적 메서드는 인스턴스(self)나 클래스(cls) 인자를 받지 않으며, 인스턴스나 클래스에 의존하지 않을 때 사용합니다.

두 번째 생성자를 위한 클래스 메서드

클래스 메서드는 클래스 자체(cls)를 첫 번째 인자로 받아 팩터리 메서드에 적합합니다.

@classmethod
def from_points(cls, x1, y1, x2, y2):
    """두 개의 대각선 꼭짓점으로부터 Square를 생성합니다."""
    if abs(x2 - x1) != abs(y2 - y1):
        raise ValueError("The points do not form a square")
    side = abs(x2 - x1)
    # 왼쪽 아래 꼭짓점을 원점 (x1, y1)으로 사용
    return cls(x1, y1, side)
sq = Square.from_points(0, 0, 3, 3)   # 3×3 정사각형을 생성

예외 처리

Java의 throw는 Python에서는 raise가 됩니다. 위의 from_points 메서드가 이미 정사각형이 아닌 경우 ValueErrorraise하는 예를 보여주고 있습니다.

전체 Python 구현

import math
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Square(Shape):
    def __init__(self, x, y, side):
        self.x = x
        self.y = y
        self.side = side

    @classmethod
    def from_points(cls, x1, y1, x2, y2):
        """Factory method that builds a Square from two opposite corners."""
        if abs(x2 - x1) != abs(y2 - y1):
            raise ValueError("The points do not form a square")
        side = abs(x2 - x1)
        return cls(x1, y1, side)

    @staticmethod
    def calc_side_length(area):
        """Return the side length for a given area."""
        return math.sqrt(area)

    def area(self):
        return self.side * self.side

    def perimeter(self):
        return 4 * self.side

    def __str__(self):
        return f"Square [side={self.side}]"

    def __repr__(self):
        return f"Square(side={self.side})"

이제 Java 버전과 마찬가지로 클래스를 사용할 수 있지만, 파이썬식 관용구를 사용합니다:

sq1 = Square(1, 2, 5)
sq2 = Square.from_points(0, 0, 5, 5)

print(sq1)                     # Square [side=5]
print(repr(sq2))               # Square(side=5)
print(Square.calc_side_length(25))  # 5.0

코딩 즐겁게! 내용이 명확하지 않다면, 댓글을 남겨 주세요. 설명해 드리겠습니다.

Square.make_square_from_points 사용하기

try:
    sq = Square.make_square_from_points(4, 5, 7, 11)
except ValueError as ve:
    print(ve)

출력

ValueError: (4, 5, 7, 11)

Error hierarchy

사용자 정의 예외 만들기

우리는 ValueError를 상속받아 자체 오류 MalformedSquareException을 만들 것입니다.

class MalformedSquareException(ValueError):
    def __init__(self, x1, y1, x2, y2):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

    # __str__와 관련된 차이점을 확인하세요
    def __repr__(self):
        return "MalformedSquareException(%d, %d, %d, %d)" % (
            self.x1, self.y1, self.x2, self.y2
        )

    def __str__(self):
        return (
            "The points (%d, %d) and (%d, %d) are not the opposite corners of a square"
            % (self.x1, self.y1, self.x2, self.y2)
        )

try:
    sq = Square.make_square_from_points(4, 7, 5, 11)
except MalformedSquareException as ve:
    print(ve)

출력

The points (4, 5) and (7, 11) are not the opposite corners of a square

추상 클래스

Shape 추상 기본 클래스는 파이썬에서 다음과 같이 작성할 수 있습니다:

import abc

# alternative: class Shape(abc.ABC)
class Shape(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def area(self):
        ...  # Python abstract methods require a body

    @abc.abstractmethod
    def perimeter(self):
        ...

파이썬 추상 클래스는 다음 중 하나로 생성됩니다:

  • ABCMeta를 메타클래스로 선언하거나, 또는
  • ABC를 서브클래스화하기 (ABC의 메타클래스가 ABCMeta임).

자바와 마찬가지로 파이썬 추상 클래스는 생성자와 비추상 메서드를 가질 수 있습니다. 또한 정적클래스 추상 메서드를 정의할 수도 있습니다.

예시: 정적 추상 메서드

class Shape(metaclass=abc.ABCMeta):
    @staticmethod
    @abc.abstractmethod
    def nb_sides():
        ...

class Square(Shape):
    @staticmethod
    def nb_sides():
        return 4

Note: @staticmethod must precede @abc.abstractmethod.

연산자 오버로드

Python은 __add__, __mul__, __lt__, __gt__ 등과 같은 특수 메서드를 통해 연산자 오버로드를 지원합니다. 반면에 Java는 문자열 연결을 위해 + 연산자만 오버로드합니다.

import abc
import math

class Square(metaclass=abc.ABCMeta):
    # sq2 = sq1 * 3 creates sq2 that's √3 times bigger than sq1
    def __mul__(self, n):
        return Square(self.x, self.y, self.side * math.sqrt(n))

    def __lt__(self, other):
        return self.side < other.side

s1 = Square(0, 0, 10)
s2 = s1 * 3          # calls __mul__
print(s1 < s2)       # calls __lt__

Source:

다중 상속

Python은 Java와 달리 단일 상속만 허용하는 것이 아니라 다중 상속을 지원합니다. 클래스가 여러 조상을 가질 때 해결 순서는 다음과 같습니다:

  1. 바로 직전 조상이 더 높은 우선순위를 갖고, 더 위에 있는 조상보다 우선합니다.
  2. 같은 계층 수준에 있는 조상들 사이에서는 클래스 정의에서의 순서가 우선순위를 결정합니다 (왼쪽 → 높은 우선순위, 오른쪽 → 낮은 우선순위).

예시 다이어그램

Updated Shape, Rectangle, Rhombus & updated Square classes

코드

class Rectangle(Shape):
    def __init__(self, x, y, length, breadth):
        self.x, self.y = x, y
        self.length, self.breadth = length, breadth

    def area(self):
        return self.length * self.breadth

    def perimeter(self):
        return 2 * (self.length + self.breadth)

    @staticmethod
    def nb_sides():
        return 4

class Rhombus(Shape):
    def __init__(self, x, y, side, angle):
        self.x, self.y = x, y
        self.side, self.angle = side, angle

    def area(self):
        return self.side * self.side * math.sin(self.angle * math.pi / 180)

    def perimeter(self):
        return 4 * self.side

    @staticmethod
    @abc.abstractmethod
    def nb_sides():
        return 4

class Square(Rhombus, Rectangle):
    def __init__(self, side):
        # Rhombus expects (x, y, side, angle); we set x=y=0 and angle=90°
        super().__init__(0, 0, side, 90)

    def area(self):
        return super().area()

    @staticmethod
    def nb_sides():
        return 4

주요 포인트:

  • SquareRhombusRectangle 두 클래스를 모두 상속받습니다.
  • 메서드 해석 순서(MRO)는 위에서 설명한 규칙을 따릅니다.
  • Square.__init__에서의 super() 호출은 Rhombus.__init__(MRO에서 첫 번째 부모)에게 인자를 올바르게 전달합니다.

Python에서 super()와 생성자 호출 이해

  • super() 호출은 자동으로 부모 생성자를 호출하지 않으며, 단지 부모 객체에 대한 참조만 제공합니다. 따라서 부모의 초기화 로직이 필요하면 __init__을 명시적으로 호출해야 합니다.
  • Square 선언에서 RhombusRectangle보다 먼저 나열되어 있기 때문에, super()는 먼저 Rhombus를 해결합니다.
0 조회
Back to Blog

관련 글

더 보기 »

왜 0.1 + 0.2가 코드에서 0.3이 되지 않을까

markdown Floating‑Point Surprise python print 0.1 + 0.2 당신은 0.3이 나오길 기대합니다. 하지만 실제로는 0.30000000000000004가 나옵니다. 당신의 계산기는 0.3이라고 말하고, Excel도 0.3이라고 말합니다. Yo...

나만의 함수

사용자 정의 함수: 특정 작업을 수행하기 위해 작성된 코드 블록. 함수의 선언·정의 - 인자 (argument) = 매개변수 (parameter) - 수행문 - 반환값 (return) python def 함수_이름(인자1, 인자2, …): 수행문 1 수행문 2 return 인자의 종류…