5편. Python 클래스와 객체지향 – Java보다 유연한 설계 방식 이해하기
📚 목차
1. Python 클래스 선언 방식 - 간결함과 동적 설계
2. __init__() 생성자 - 오버로딩 없이 유연한 초기화
3. self 키워드 - 인스턴스와 메서드 연결의 핵심
4. 상속과 오버로딩 - Java와 Pytho의 설계 차이
5. 접근 제한자 - Python의 관례 기반 제어 방식
✔ 마무리 - 객체지향 철학의 차이를 이해하며
Java는 정적 타입과 엄격한 클래스 구조를 기반으로 하는 전통적인 객체지향 언어입니다. 반면 Python은 함수형, 절차적, 객체지향 프로그래밍을 모두 지원하는 다중 패러다임 언어로, 특히 객체지향 구현 방식에 있어 Java와는 매우 다른 철학과 문법 구조를 지닙니다.
Java 개발자가 Python 객체지향을 이해하면, 더 Pythonic한 코드를 작성할 수 있을 뿐 아니라, Django, FastAPI, Pandas 등 다양한 프레임워크와 라이브러리 사용에 있어 개발 효율과 협업 능력이 크게 향상됩니다.
이제 본격적으로 Python의 클래스 설계 방식을 살펴보겠습니다.

1. Python 클래스 선언 방식 – 간결함과 동적 설계
Java는 정적 타입 언어로, 클래스를 선언할 때 각 변수의 자료형을 명시하고 public, private 등과 같은 접근 제어자를 사용합니다.
이는 코드의 안정성을 높이고 컴파일 시점에 오류를 발견하는 데 도움을 줍니다. 반면 Python은 동적 타입 언어이며, 간결함과 유연성을 중시하여 클래스 선언 문법이 훨씬 단순합니다.
✔️Java 예시: 자료형과 제어자가 있는 클래스
Java에서 Person 클래스를 선언하고 name이라는 String 타입의 public 변수와 생성자, 그리고 간단한 greet 메서드를 포함하는 예시입니다.
public class Person {
public String name; // public 접근 제어자와 String 자료형 명시
private int age; // private 접근 제어자와 int 자료형 명시
// 생성자: 클래스명과 동일
public Person(String name, int age) {
this.name = name; // this 키워드로 인스턴스 변수 접근
this.age = age;
}
// public 메서드
public void greet() {
System.out.println("안녕하세요, 제 이름은 " + this.name + "입니다. " + this.age + "살입니다.");
}
// private 변수에 접근하는 public getter 메서드
public int getAge() {
return this.age;
}
}
// 사용 예시
// Person p = new Person("김철수", 30);
// p.greet(); // 출력: 안녕하세요, 제 이름은 김철수입니다. 30살입니다.
// System.out.println(p.name); // 출력: 김철수
// System.out.println(p.getAge()); // 출력: 30
✔️ Python 예시: 간결하고 유연한 클래스
Python에서 동일한 Person 클래스를 선언하는 예시입니다.
여기서는 자료형 명시나 명시적인 접근 제어자 키워드가 필요 없습니다.
__init__ 메서드가 생성자 역할을 하며, 모든 인스턴스 메서드는 첫 번째 인자로 self를 가집니다.
class Person:
# __init__ 메서드는 클래스의 '생성자' 역할을 합니다.
# self는 인스턴스 자신을 가리키는 파라미터이며, 모든 인스턴스 메서드의 첫 번째 파라미터로 명시되어야 합니다.
def __init__(self, name, age):
# 인스턴스 변수 선언: 자료형을 명시하지 않고 바로 할당합니다.
self.name = name
self.age = age
# 인스턴스 메서드: 첫 번째 파라미터로 self를 가집니다.
def greet(self):
print(f"안녕하세요, 제 이름은 {self.name}입니다. {self.age}살입니다.")
# 일반 메서드: age를 반환합니다. 파이썬에서는 별도의 getter/setter를 강제하지 않습니다.
def get_age(self):
return self.age
# 클래스 사용 예시
p = Person("김철수", 30)
p.greet() # 출력: 안녕하세요, 제 이름은 김철수입니다. 30살입니다.
print(p.name) # 출력: 김철수
print(p.get_age()) # 출력: 30
✔️ 비교 요약
| 항목 | Java | Python |
| 클래스 선언 | public class MyClass { ... }처럼 public class 키워드 필요 | class MyClass: 키워드만 사용 |
| 생성자 | 클래스명과 동일한 메서드 사용 public MyClass(...) { ... } | __init__() 메서드 사용def __init__(self, ...) |
| 변수 선언 | 자료형 명시 필수String name;, int age; | 자료형 없이 선언 및 할당self.name = name |
| 접근 제어자 | public, private, protected, default 키워드 사용 | _, __로 접근 수준 표현 (관례 기반) |
| 인스턴스 참조 | this.name처럼 this 키워드 사용 | self.name처럼 self 키워드 사용 (메서드 첫 번째 인자에 명시) |
2. __init__() 생성자 – 오버로딩 없이 유연한 초기화
Python에서 __init__() 메서드는 클래스의 인스턴스가 생성될 때 자동으로 호출되는 특별한 메서드입니다. Java의 생성자와 유사하게 객체의 초기 상태를 설정하는 역할을 하지만, 몇 가지 중요한 차이가 있습니다.
가장 큰 차이는 메서드 오버로딩의 부재입니다. Java에서는 파라미터의 타입이나 개수가 다른 여러 개의 생성자를 정의하여 객체 생성 시 유연성을 제공할 수 있습니다.
✔️ Java 예시: 생성자 오버로딩
예를 들어, Person(String name)과 Person(String name, int age) 두 개의 생성자를 가질 수 있습니다.
public class Product {
String name;
double price;
int quantity;
// 생성자 1: 이름과 가격만으로 초기화
public Product(String name, double price) {
this.name = name;
this.price = price;
this.quantity = 0; // 기본값 설정
}
// 생성자 2: 모든 속성으로 초기화
public Product(String name, double price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
}
✔️ Python 예시: 유연한 초기화
Python에서는 __init__() 메서드를 하나만 가질 수 있습니다.
대신, 기본값 파라미터, 가변 인자(*args), 키워드 인자(**kwargs)를 활용하여 Java의 오버로딩과 유사한 유연성을 구현할 수 있습니다.
아래 User 클래스는 name과 age를 선택적으로 받을 수 있으며, email과 같은 추가 정보도 유연하게 처리할 수 있도록 설계되었습니다.
class User:
# name과 age에 기본값을 None으로 설정하여 선택적 인자로 만듭니다.
# **kwargs를 사용하여 정의되지 않은 추가 키워드 인자를 받을 수 있습니다.
def __init__(self, name=None, age=None, **kwargs):
self.name = name
# age가 None이거나 0보다 작으면 기본값 0으로 설정
self.age = age if age is not None and age >= 0 else 0
self.user_id = self._generate_id() # 내부 메서드를 호출하여 ID 생성
# **kwargs를 통해 전달된 추가 속성을 인스턴스 변수로 설정합니다.
# 예를 들어, User(name="Alice", email="alice@example.com")처럼 호출 가능
for key, value in kwargs.items():
setattr(self, key, value) # setattr 함수로 동적으로 속성 추가
# private에 가까운 내부 메서드 (관례적으로 _로 시작)
def _generate_id(self):
import uuid # UUID 모듈 임포트
return str(uuid.uuid4()) # 고유 ID 생성
def display_info(self):
info = f"사용자 ID: {self.user_id}\n이름: {self.name if self.name else '미지정'}\n나이: {self.age}"
# 동적으로 추가된 속성들을 출력
for key, value in self.__dict__.items():
if key not in ['name', 'age', 'user_id']:
info += f"\n{key.replace('_', ' ').capitalize()}: {value}"
print(info)
# 사용 예시
user1 = User("홍길동") # 이름만 제공
user1.display_info()
print("-" * 20)
user2 = User("김영희", 25) # 이름과 나이 제공
user2.display_info()
print("-" * 20)
user3 = User(age=30, email="park@example.com", department="IT") # 키워드 인자와 추가 속성 제공
user3.display_info()
print("-" * 20)
user4 = User() # 아무 인자 없이 생성
user4.display_info()
이처럼 Python의 __init__()은 기본값, *args, **kwargs를 통해 Java의 생성자 오버로딩 이상의 유연성을 제공합니다.
개발자는 파라미터의 존재 여부나 값에 따라 조건 분기를 처리하여 다양한 초기화 시나리오를 지원할 수 있습니다.
3. self 키워드 – 인스턴스와 메서드 연결의 핵심
Java에서 this 키워드가 현재 인스턴스 자신을 가리키는 것처럼, Python에서는 self 키워드가 동일한 역할을 수행합니다.
하지만 self는 Java의 this와 달리 모든 인스턴스 메서드의 첫 번째 파라미터로 명시적으로 선언되어야 한다는 점에서 큰 차이가 있습니다.

✔️ Java: this의 암묵적 사용:
Java에서는 컴파일러가 자동으로 this를 처리해 주므로, 개발자는 필요할 때만 명시적으로 this를 사용합니다.
주로 인스턴스 변수와 메서드 파라미터의 이름이 같을 때 충돌을 피하거나, 현재 객체의 다른 생성자를 호출할 때(this(...)) 사용됩니다.
public class Calculator {
private int result; // 인스턴스 변수
public Calculator(int result) {
this.result = result; // 파라미터 result와 인스턴스 변수 result를 구분
}
public void add(int value) {
this.result += value; // this를 통해 인스턴스 변수 result에 접근
}
public int getResult() {
return this.result;
}
}
✔️ Python: self의 명시적 선언
Python에서 self는 인스턴스 메서드가 호출될 때 해당 인스턴스 자체가 첫 번째 인자로 전달된다는 것을 의미합니다.
이는 파이썬이 메서드를 함수와 동일하게 취급하며, 인스턴스에 속한 변수나 다른 메서드에 접근하기 위해 '어떤 인스턴스'에 대한 작업인지를 명시적으로 알려주기 위함입니다.
class Car:
def __init__(self, brand, model):
# self.brand와 self.model은 이 인스턴스(객체)에 속한 변수입니다.
self.brand = brand
self.model = model
self.speed = 0
# drive 메서드는 self를 통해 Car 인스턴스의 속성(speed)에 접근합니다.
def drive(self, increase_by):
self.speed += increase_by
print(f"{self.brand} {self.model}의 현재 속도는 {self.speed}km/h 입니다.")
# stop 메서드 역시 self를 통해 인스턴스의 speed를 0으로 설정합니다.
def stop(self):
self.speed = 0
print(f"{self.brand} {self.model}가 정지했습니다.")
# 객체 생성
my_car = Car("Tesla", "Model 3")
# 메서드 호출 시 self는 자동으로 전달됩니다.
my_car.drive(50) # 출력: Tesla Model 3의 현재 속도는 50km/h 입니다.
my_car.drive(30) # 출력: Tesla Model 3의 현재 속도는 80km/h 입니다.
my_car.stop() # 출력: Tesla Model 3가 정지했습니다.
✔️ self 사용에 대한 중요한 주의사항:
▪️생략 불가능: 인스턴스 메서드를 정의할 때는 self를 첫 번째 인자로 반드시 포함해야 합니다. 이를 생략하면 TypeError가 발생합니다.
▪️다른 이름 가능: 기술적으로 self 대신 다른 이름을 사용할 수 있지만(예: this나 my_instance), 이는 파이썬 커뮤니티의 강력한 관례를 깨는 것이므로 self를 사용하는 것이 강력히 권장됩니다.
▪️클래스 메서드와 정적 메서드: self는 인스턴스 메서드에만 적용됩니다.
클래스 메서드 (@classmethod): 첫 번째 인자로 클래스 자체를 의미하는 cls를 받습니다.
정적 메서드 (@staticmethod): self나 cls와 같은 특별한 첫 번째 인자를 받지 않습니다.
self는 파이썬 객체지향에서 인스턴스의 상태와 행동을 연결하는 핵심적인 요소이므로 그 역할을 정확히 이해하는 것이 중요합니다.
4. 상속과 오버라이딩 – Java와 Python의 설계 차이
상속은 객체지향 프로그래밍의 핵심 개념 중 하나로, 기존 클래스(부모/기반 클래스)의 특성과 행동을 새로운 클래스(자식/파생 클래스)가 물려받아 재사용하거나 확장하는 메커니즘입니다.
Python과 Java 모두 상속과 오버라이딩(부모 클래스의 메서드를 자식 클래스에서 재정의)을 지원하지만, 그 방식에는 차이가 있습니다.
✔️ Java 상속 예시: extends 키워드와 @override 어노테이션
Java는 단일 상속만 지원합니다. extends 키워드를 사용하여 상속 관계를 명시하고, interface나 abstract class를 통해 추상화를 구현합니다.
메서드 오버라이딩 시에는 @Override 어노테이션을 사용하여 의도를 명확히 하고 컴파일러의 검증을 받습니다.
// 부모 클래스
public class Animal {
protected String species; // protected 접근 제어자
public Animal(String species) {
this.species = species;
}
public void speak() {
System.out.println(this.species + "는 소리를 냅니다.");
}
public void move() {
System.out.println(this.species + "는 움직입니다.");
}
}
// 자식 클래스: Animal을 상속받음
public class Dog extends Animal {
public Dog(String name) {
// super()를 사용하여 부모 클래스의 생성자 호출
super("개"); // 부모 생성자에 "개"라는 종(species)을 전달
}
// @Override 어노테이션을 사용하여 speak 메서드를 오버라이드
@Override
public void speak() {
System.out.println("멍멍!");
}
// 부모 클래스의 move 메서드를 그대로 사용
}
// 자식 클래스: Animal을 상속받음
public class Cat extends Animal {
public Cat() {
super("고양이");
}
@Override
public void speak() {
System.out.println("야옹!");
}
}
// 사용 예시
// Animal myAnimal = new Animal("동물");
// myAnimal.speak(); // 출력: 동물는 소리를 냅니다.
//
// Dog myDog = new Dog("바둑이");
// myDog.speak(); // 출력: 멍멍! (오버라이딩된 메서드 호출)
// myDog.move(); // 출력: 개는 움직입니다. (부모 메서드 호출)
//
// Cat myCat = new Cat();
// myCat.speak(); // 출력: 야옹!
✔️ Python 상속 예시: 괄호 안에 부모 클래스 명시
Python은 클래스 선언 시 괄호 () 안에 상속받을 부모 클래스를 명시합니다.
@override와 같은 명시적인 어노테이션은 필요 없지만, super() 함수를 통해 부모 클래스의 메서드를 호출할 수 있습니다.
Python은 다중 상속을 지원한다는 점이 Java와의 큰 차이점입니다.
# 부모 클래스
class Animal:
def __init__(self, species):
self.species = species
def speak(self):
print(f"{self.species}는 소리를 냅니다.")
def move(self):
print(f"{self.species}는 움직입니다.")
# 자식 클래스: Dog는 Animal을 상속받습니다.
class Dog(Animal):
def __init__(self, name):
# super().__init__()을 사용하여 부모 클래스의 생성자를 호출합니다.
# super()는 부모 클래스를 참조하는 프록시 객체를 반환합니다.
super().__init__("개") # Animal의 __init__에 "개"를 전달
self.name = name
# speak 메서드를 오버라이딩합니다. Java의 @Override와 같은 명시적인 키워드는 없습니다.
def speak(self):
print("멍멍!")
# 부모 클래스의 move 메서드를 그대로 사용합니다.
# 자식 클래스: Cat은 Animal을 상속받습니다.
class Cat(Animal):
def __init__(self):
super().__init__("고양이") # Animal의 __init__에 "고양이"를 전달
def speak(self):
print("야옹!")
# super()를 활용한 오버라이딩 예시
class Poodle(Dog):
def __init__(self, name):
super().__init__(name) # Dog의 __init__ 호출
def speak(self):
super().speak() # 부모(Dog)의 speak 메서드 호출
print(f"{self.name}는 앙앙! 하고 짖어요.") # 추가적인 행동
# 사용 예시
my_animal = Animal("동물")
my_animal.speak() # 출력: 동물는 소리를 냅니다.
my_dog = Dog("바둑이")
my_dog.speak() # 출력: 멍멍!
my_dog.move() # 출력: 개는 움직입니다.
my_cat = Cat()
my_cat.speak() # 출력: 야옹!
my_poodle = Poodle("초코")
my_poodle.speak() # 출력: 멍멍! (Dog의 speak), 초코는 앙앙! 하고 짖어요.
✔️ Python 다중 상속 (Multiple Inheritance):
Python은 여러 부모 클래스로부터 상속받는 다중 상속을 지원합니다.
이 경우 메서드 호출 순서(Method Resolution Order, MRO)가 중요해집니다.
ClassName.mro() 메서드를 통해 MRO를 확인할 수 있습니다.
class Flying:
def fly(self):
print("하늘을 날 수 있습니다.")
class Swimming:
def swim(self):
print("물을 헤엄칠 수 있습니다.")
# Duck은 Flying과 Swimming 모두 상속받습니다.
class Duck(Flying, Swimming):
def quack(self):
print("꽥꽥!")
my_duck = Duck()
my_duck.fly() # 출력: 하늘을 날 수 있습니다.
my_duck.swim() # 출력: 물을 헤엄칠 수 있습니다.
my_duck.quack() # 출력: 꽥꽥!
# MRO 확인
print(Duck.__mro__)
# (<class '__main__.Duck'>, <class '__main__.Flying'>, <class '__main__.Swimming'>, <class '__main__.object'>)
✔️ Python 추상 클래스
Java의 abstract class나 interface처럼 완전한 추상화를 제공하지 않지만, Python은 abc 모듈을 사용하여 추상 클래스와 추상 메서드를 정의할 수 있습니다. 이를 통해 자식 클래스가 특정 메서드를 반드시 구현하도록 강제할 수 있습니다.
from abc import ABC, abstractmethod
class Shape(ABC): # ABC를 상속받아 추상 클래스임을 명시
@abstractmethod # 추상 메서드임을 명시
def area(self):
pass # 구현부가 없습니다.
@abstractmethod
def perimeter(self):
pass
def description(self): # 일반 메서드도 포함 가능
return "도형입니다."
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
def perimeter(self):
return 2 * 3.14 * self.radius
# circle = Shape() # TypeError: Can't instantiate abstract class Shape with abstract methods area, perimeter
circle = Circle(5)
print(f"원의 넓이: {circle.area()}")
print(f"원의 둘레: {circle.perimeter()}")
print(circle.description())
Python의 상속은 Java보다 유연하며, 다중 상속과 abc 모듈을 통해 다양한 디자인 패턴을 구현할 수 있습니다.
5. 접근 제한자 – Python의 관례 기반 제어 방식
Java는 public, protected, default(패키지), private과 같은 명시적인 키워드를 사용하여 클래스 멤버(변수 및 메서드)의 접근 범위를 제어합니다. 이는 컴파일러 수준에서 강력하게 강제됩니다.
반면 Python은 이러한 명시적인 접근 제어자 키워드를 제공하지 않습니다.
대신 이름 관례(Naming Conventions)와 이름 맹글링(Name Mangling)이라는 메커니즘을 통해 접근 제한을 '권고'하거나 '부분적으로 제한'합니다. 이는 "우리는 모두 성인이고 동의하에 코드를 수정한다"는 Python의 철학을 반영합니다.
✔️ Python의 네이밍 관례 및 이름 맹그링(Name Mangling)
Python에서 변수나 메서드 이름 앞에 붙는 언더스코어(_)의 개수에 따라 암묵적인 접근 제한 의미가 달라집니다.
| 접근수준 | Java 키워드 | Python 표현 | 의미 |
| public | public | name | 어디서든 접근 가능 |
| protected | protected | _name | 내부 또는 상속 클래스에서 사용 권장 |
| private | private | __name | 내부 전용 (Name Mangling 발생) |
✔️ Python 예시: 이름 맹글링
아래 Account 클래스는 Python의 다양한 접근 제한 관례를 보여줍니다.
class Account:
def __init__(self):
self.balance = 1000 # public: 외부에서 자유롭게 접근 가능
self._account_type = "개인" # protected: 내부용으로 사용 권고, 외부 접근 가능
self.__pin = "1234" # private: 이름 맹글링 적용, 직접 접근 어려움
# public 메서드
def get_balance(self):
return self.balance
# protected 수준의 메서드 (내부용)
def _display_account_type(self):
print(f"계좌 유형: {self._account_type}")
# private 수준의 메서드 (내부용)
def __verify_pin(self, pin_attempt):
return self.__pin == pin_attempt
# private 속성 __pin에 접근하는 public getter 메서드
def get_pin(self, pin_attempt):
if self.__verify_pin(pin_attempt):
return self.__pin
else:
return "핀 번호가 일치하지 않습니다."
# 객체 생성
a = Account()
print(f"잔액 (public): {a.balance}") # OK: public 속성에 접근
a._display_account_type() # OK: protected 속성을 다루는 메서드 호출. 가능하지만 외부에서 직접 호출은 지양.
print(f"계좌 유형 (protected): {a._account_type}") # OK: protected 속성에 직접 접근. 가능하지만 지양.
# print(a.__pin) # AttributeError: 'Account' object has no attribute '__pin'
# 직접적으로 __pin에 접근하려 하면 오류 발생. 이름 맹글링 때문입니다.
# 이름 맹글링된 이름으로 접근 (가능하지만 권장되지 않음!)
# Python은 __pin을 _Account__pin으로 변환합니다.
print(f"핀 번호 (name mangled): {a._Account__pin}") # OK: 비공식적인 방법으로 접근 가능.
# public getter를 통해 private 속성에 안전하게 접근
print(f"get_pin(1234): {a.get_pin('1234')}") # OK: public 메서드를 통해 접근
print(f"get_pin(9999): {a.get_pin('9999')}") # 출력: 핀 번호가 일치하지 않습니다.
✔️ 이름 맹글링 (Name Mangling) 원리:
Python에서 __변수명처럼 이중 언더스코어로 시작하는 변수나 메서드는 인터프리터에 의해 이름 맹글링(Name Mangling) 과정을 거칩니다.
이 기능은 클래스 내부에서만 사용해야 하는 변수나 메서드를 외부에서 우연히 접근하지 않도록 보호하는 역할을 합니다.
class Account:
def __init__(self):
self.__pin = "1234"
위 코드에서 __pin이라는 변수는 실제로는 _Account__pin 이라는 이름으로 저장됩니다.
즉, 변수 앞에 _클래스명이 자동으로 붙는 방식입니다.
a = Account()
print(a.__pin) # ❌ AttributeError 발생
print(a._Account__pin) # ✅ "1234" 출력됨 (우회 접근)
- a.__pin은 Python이 내부적으로 바꿔놓은 이름과 일치하지 않기 때문에 오류가 발생합니다.
- 하지만 a._Account__pin처럼 바뀐 이름으로는 접근이 가능합니다.
✔️ 속성(Properties)을 이용한 접근 제어 : @property 사용 예시
Java에서 getter/setter 메서드를 통해 속성 접근을 제어하는 것처럼, Python에서는 @property 데코레이터를 사용하여 유사한 기능을 구현할 수 있습니다.
이는 클래스 외부에서는 일반 속성처럼 보이지만 내부적으로는 메서드를 통해 접근을 제어하는 Pythonic한 방식입니다.
class Wallet:
def __init__(self, initial_balance):
self._balance = initial_balance # 내부적으로 _balance로 저장 (protected 관례)
@property # getter 메서드 정의
def balance(self):
print("잔액에 접근합니다.")
return self._balance
@balance.setter # setter 메서드 정의 (읽기 전용으로 만들려면 setter를 정의하지 않음)
def balance(self, new_balance):
if not isinstance(new_balance, (int, float)):
raise ValueError("잔액은 숫자여야 합니다.")
if new_balance < 0:
raise ValueError("잔액은 음수가 될 수 없습니다.")
print(f"잔액을 {self._balance}에서 {new_balance}로 변경합니다.")
self._balance = new_balance
@balance.deleter # deleter 메서드 정의
def balance(self):
print("잔액을 삭제합니다.")
del self._balance
# 사용 예시
my_wallet = Wallet(1000)
print(f"현재 잔액: {my_wallet.balance}") # getter 호출
my_wallet.balance = 1500 # setter 호출
print(f"변경된 잔액: {my_wallet.balance}")
try:
my_wallet.balance = -100 # setter에서 예외 발생
except ValueError as e:
print(f"오류: {e}")
# del my_wallet.balance # deleter 호출
# print(my_wallet.balance) # 삭제 후 접근 시 AttributeError 발생
Python의 접근 제한은 Java와 달리 개발자의 '책임'과 '관례'에 더 크게 의존합니다.
_나 __를 통해 개발자의 의도를 표현하고, @property와 같은 기능을 통해 안전하고 Pythonic한 방식으로 속성에 접근을 제어할 수 있습니다.
✔ 마무리 - 객체지향 철학의 차이를 이해하며
Java와 Python은 모두 객체지향 언어지만, 그 설계 철학은 다릅니다.
Java는 정적 타입과 엄격한 문법으로 안정성을 중시하고, Python은 간결함과 유연성을 중시하며 관례 기반의 구조를 따릅니다.
Python 클래스는 __init__(), self, @property, super() 같은 요소를 통해 간단하지만 유연한 객체 설계를 가능하게 합니다.
특히 오버로딩 없는 생성자, 명시적인 self 사용, 접근 제한을 위한 관례 등은 Java와의 큰 차이입니다.
📌 요약
🔸 Python 클래스는 간결하면서도 유연한 구조를 지닌다.
🔸 생성자는 *args, **kwargs 등으로 다양하게 초기화 가능하다.
🔸 self는 인스턴스와 메서드를 연결하는 필수 키워드다.
🔸 접근 제어는 키워드가 아닌 관례와 데코레이터로 처리된다.
🔸 상속과 추상 클래스도 더 유연한 방식으로 구현된다.
Python 객체지향의 핵심은 “강제보다 명시, 구조보다 자유”에 있습니다.
이 철학을 이해하면 더 Pythonic하고 생산적인 코드를 작성할 수 있게 됩니다.
※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.
'3.SW개발 > Python' 카테고리의 다른 글
| [Java관점]7편. Python 컬렉션 정리 – Java List/Map/Set과의 대응 구조 (0) | 2025.11.12 |
|---|---|
| [Java관점]6편. Python 예외 처리 문법 – Java와 Python의 처리 구조 비교 (0) | 2025.11.12 |
| [Java관점]4편. Python 함수와 메서드 – 호출 방식과 선언 구조 비교 (0) | 2025.11.11 |
| [Java관점]3편. Python 조건문과 반복문 – Java와 다른 제어 구조 한눈에 보기 (1) | 2025.11.11 |
| [Java관점]2편. Python 변수와 자료형 – Java보다 유연한 선언 방식 비교 (0) | 2025.11.10 |