[Java vs Python] 클래스/static 한 장 정리

NANO·4일 전

알고리즘 Cheat Sheet 시리즈는 코딩 테스트를 풀다가 "이거 자바에선 뭐였지?", "파이썬은 어떻게 했더라?" 싶을 때 바로 펼쳐 보려고 만든 개인 참고용 정리입니다.
Java와 Python을 나란히 놓고, 문법 차이 때문에 실수하기 쉬운 부분만 짧게 정리합니다.

이번 주제는 클래스와 static입니다.
자바로 코테를 풀다 보면 static을 일단 붙이고 보자는 식으로 쓰게 되는데, 왜 붙여야 하는지와 파이썬에서는 무엇에 해당하는지를 정리해 봅시다.


1. 클래스 기본

항목자바파이썬
생성자클래스 이름과 같은 메서드__init__(self, ...)
자기 자신 참조this (생략 가능)self (생략 불가)
객체 생성new Point(1, 2)Point(1, 2)
필드 선언클래스 본문에 타입과 함께__init__ 안에서 self.x = ...
메서드 첫 번째 매개변수없음self를 직접 적는다
접근 제어private, public 등없음 (_이름은 관례일 뿐)
class Point {
    int x, y;                    // 필드 선언

    Point(int x, int y) {        // 생성자
        this.x = x;
        this.y = y;
    }

    int sum() {                  // 인스턴스 메서드
        return x + y;            // this.x에서 this 생략 가능
    }
}

Point p = new Point(1, 2);
p.sum();   // 3
class Point:
    def __init__(self, x, y):    # 생성자
        self.x = x               # 여기서 필드가 만들어진다
        self.y = y

    def sum(self):               # 인스턴스 메서드
        return self.x + self.y   # self 생략 불가

p = Point(1, 2)
p.sum()   # 3

코테에서 좌표나 노드처럼 값 몇 개를 묶기만 할 때는 자바는 int[]나 클래스, 파이썬은 튜플 (x, y)이 가장 간단하다.


2. static: 객체가 아니라 클래스에 속한 것

구분인스턴스 멤버static 멤버
소속객체마다 따로클래스에 하나 (모든 객체가 공유)
사용 조건객체를 만들어야 쓸 수 있다객체 없이 쓸 수 있다
호출p.sum()Math.max(a, b)

자바와 파이썬 대응표

개념자바파이썬
공유 변수static int count;클래스 본문에 count = 0 (클래스 변수)
객체 없이 부르는 메서드static int add(int a, int b)@staticmethod
클래스 자체를 받는 메서드-@classmethod (cls)
상수static final int MOD = 1_000_000_007;MOD = 10**9 + 7 (대문자는 관례)
접근클래스이름.count클래스이름.count
class Counter {
    static int count = 0;        // 모든 객체가 공유
    int id;                      // 객체마다 따로

    Counter() {
        count++;
        id = count;
    }

    static int total() {         // 객체 없이 호출 가능
        return count;
    }
}

new Counter(); new Counter();
Counter.total();   // 2
class Counter:
    count = 0                    # 클래스 변수 (모든 객체가 공유)

    def __init__(self):
        Counter.count += 1       # 클래스 이름으로 접근
        self.id = Counter.count  # 인스턴스 변수 (객체마다 따로)

    @staticmethod
    def total():                 # self 없음, 객체 없이 호출 가능
        return Counter.count

Counter(); Counter()
Counter.total()   # 2

3. 코테에서 static이 필요한 이유

main이 static이기 때문!

자바 프로그램은 객체를 만들기 전에 main부터 실행된다. 그래서 main은 static이고, static 메서드 안에서는 static 멤버만 바로 쓸 수 있다.

public class Main {
    int n;                                  // 인스턴스 변수

    void dfs(int x) { }                     // 인스턴스 메서드

    public static void main(String[] args) {
        n = 5;        // ❌ non-static variable n cannot be referenced from a static context
        dfs(0);       // ❌ non-static method dfs(int) cannot be referenced from a static context
    }
}

해결 방법은 두 가지다.

// 방법 1: 전부 static으로 (백준 스타일, 가장 흔함)
static int n;
static void dfs(int x) { }

// 방법 2: 객체를 만들어서 호출
Main m = new Main();
m.dfs(0);
플랫폼시작점static 필요 여부
백준public static void main보조 메서드와 공유 변수에 static 필요
프로그래머스class Solution의 solution() (인스턴스 메서드)필요 없음

DFS · BFS용 공유 변수

재귀 함수 여러 번의 호출이 같은 배열과 답을 공유해야 할 때, 자바는 static 필드, 파이썬은 전역 변수를 쓴다.

public class Main {
    static int n;
    static boolean[] visited;
    static List<List<Integer>> graph = new ArrayList<>();
    static int answer = 0;

    static void dfs(int cur) {
        visited[cur] = true;
        answer++;
        for (int next : graph.get(cur)) {
            if (!visited[next]) dfs(next);
        }
    }
}
import sys
sys.setrecursionlimit(10**6)      # 파이썬 재귀 깊이 제한(기본 1000) 늘리기, 분할정복 시 필수다...

n = 5
visited = [False] * (n + 1)
graph = [[] for _ in range(n + 1)]
answer = 0

def dfs(cur):
    global answer                 # 전역 변수에 "대입"하려면 global 선언
    visited[cur] = True           # 리스트 원소 수정은 global 없이 가능
    answer += 1
    for nxt in graph[cur]:
        if not visited[nxt]:
            dfs(nxt)

파이썬 global vs nonlocal

상황필요한 선언
전역 변수 읽기없음
전역 리스트 · 딕셔너리의 내용 수정 (visited[i] = True, d[k] = v)없음
전역 변수에 새 값 대입 (answer += 1, answer = 0)global answer
함수 안의 함수에서 바깥 함수 변수에 대입nonlocal answer
def solution(n):
    answer = 0

    def dfs(x):
        nonlocal answer           # solution의 answer를 바꾸겠다는 뜻
        answer += 1

    dfs(0)
    return answer

4. 체크포인트

자바

  • static 메서드 안에서는 this와 인스턴스 멤버를 쓸 수 없다. 객체가 없을 수도 있기 때문이다.
  • 프로그래머스에서 static 필드는 초기화를 직접 하자. 테스트 케이스가 같은 클래스로 여러 번 실행되면 이전 값이 남아 있을 수 있다. solution() 시작 부분에서 answer = 0;처럼 다시 초기화한다.
  • static final 상수는 대문자로! static final int MOD = 1_000_000_007;

파이썬

  • self를 빼먹으면 에러가 난다...

    class A:
        def hello():          # self 누락
            print("hi")
    
    A().hello()               # TypeError: A.hello() takes 0 positional arguments but 1 was given
  • self.count += 1은 클래스 변수를 바꾸지 않는다. 그 객체에 새 인스턴스 변수를 만들 뿐이다. 클래스 변수는 클래스이름.count += 1로 바꾼다.

    class Counter:
        count = 0
        def inc(self):
            self.count += 1   # 인스턴스 변수 count가 새로 생긴다
    
    a = Counter(); a.inc()
    print(a.count, Counter.count)   # 1 0
  • 클래스 변수에 리스트를 두면 모든 객체가 공유한다. 객체마다 따로 가져야 하면 __init__ 안에서 만든다.

    class Bag:
        items = []                    # ❌ 모든 Bag이 같은 리스트를 쓴다
        def __init__(self):
            self.items = []           # ⭕ 객체마다 새 리스트
  • global 없이 대입하면 지역 변수가 새로 생긴다. answer += 1은 UnboundLocalError가 난다.


💡 이것만 기억하자

  1. static은 "객체 없이 쓸 수 있고, 모두가 공유하는 것" 파이썬에서는 클래스 변수와 @staticmethod
  2. main이 static이라서 보조 메서드와 공유 변수도 static, 프로그래머스 solution()은 인스턴스 메서드라 필요 없다.
  3. 파이썬 메서드는 첫 매개변수로 self를 적고, 필드는 항상 self.을 붙인다.
  4. 파이썬 전역 변수: 읽기와 내용 수정은 그냥, 대입은 global (함수 안의 함수는 nonlocal)
  5. 파이썬 클래스 변수는 클래스이름.변수로 바꾼다. self.변수 += 1은 다른 변수를 만든다.
  6. 파이썬 DFS는 sys.setrecursionlimit을 잊지 말자.
profile
즐거운 토마토

0개의 댓글