11편. Python 프로그래밍 사고 전환 – Java 스타일에서 Python 스타일로
📚 목차
1. 리스트 컴프리헨션 (List Comprehension) - for문을 한 줄로 줄이기
2. Unpacking과 Zip - 여러 값을 더 직관적으로 다루기
3. Context Manager (with 문) - 자원 관리를 자동으로 처리하기
4. 코드 스타일 가이드 (PEP8) - Python답게 쓰는 문법 습관
✔ 마무리 - Pythonic 사고의 시작점
“Java로는 이렇게 안 했는데…”
Python을 처음 접한 Java 개발자라면 한 번쯤 이렇게 생각해본 적 있을 것입니다.
Java는 정형화된 구조와 강한 타입 안정성을 중시하는 언어입니다. 반면, Python은 간결성과 유연함을 바탕으로 '무엇을 할지'에 더 집중할 수 있도록 설계되었습니다.
이러한 차이는 코드 스타일뿐 아니라 사고방식 전환을 요구합니다.
이번 마지막 글에서는 Java 개발자 입장에서 꼭 알아야 할 Pythonic한 코드 스타일의 핵심 요소를 4가지로 정리합니다.
1. 리스트 컴프리헨션 (List Comprehension) - for문을 한 줄로 줄이기
리스트 컴프리헨션은 Python에서 리스트를 생성하는 매우 간결하고 강력한 방법입니다.
기존 리스트나 다른 이터러블(iterable)을 기반으로 새로운 리스트를 만들 때 주로 사용되며, for 루프와 if 조건문을 한 줄에 결합할 수 있어 코드를 훨씬 깔끔하게 만듭니다.
Java의 스트림 API나 람다 표현식과 유사하게, 데이터를 변환하거나 필터링하여 새로운 컬렉션을 만드는 데 유용합니다.
✔️ Java 예시 – 스트림 API
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class JavaStreamExample {
public static void main(String[] args) {
List<Integer> numbers = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
List<Integer> evenSquares = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println("Squares (Stream): " + squares);
System.out.println("Even Squares (Stream): " + evenSquares);
}
}
✔️ Python 예시 – 리스트 컴프리헨션
Python에서는 리스트를 반복문 없이 한 줄로 생성할 수 있습니다.
[표현식 for 항목 in 반복가능객체] 형태가 기본 구조입니다.
# 1부터 10까지의 숫자를 포함하는 리스트 생성
numbers = list(range(1, 11))
print(f"Numbers: {numbers}")
# For 루프를 이용한 제곱 리스트 생성
squares_for_loop = []
for num in numbers:
squares_for_loop.append(num * num)
print(f"Squares (For Loop): {squares_for_loop}")
# 리스트 컴프리헨션을 이용한 제곱 리스트 생성
squares_comprehension = [num * num for num in numbers]
print(f"Squares (List Comprehension): {squares_comprehension}")
# For 루프와 조건문을 이용한 짝수의 제곱 리스트 생성
even_squares_for_loop = []
for num in numbers:
if num % 2 == 0:
even_squares_for_loop.append(num * num)
print(f"Even Squares (For Loop): {even_squares_for_loop}")
# 리스트 컴프리헨션과 조건문을 이용한 짝수의 제곱 리스트 생성
even_squares_comprehension = [num * num for num in numbers if num % 2 == 0]
print(f"Even Squares (List Comprehension): {even_squares_comprehension}")
# 문자열 리스트에서 길이가 5 이상인 단어만 대문자로 변환하여 새로운 리스트 생성
words = ["apple", "banana", "cherry", "date", "elderberry"]
long_words_upper = [word.upper() for word in words if len(word) >= 5]
print(f"Long words in upper case: {long_words_upper}")
- 간결성: 코드가 훨씬 짧고 읽기 쉽습니다.
- 성능: 일반적인 for 루프보다 빠를 때가 많습니다.
- 가독성: '무엇을 만들지'와 '어떻게 만들지'가 한 줄에 명확하게 표현됩니다.
2. Unpacking과 Zip - 여러 값을 더 직관적으로 다루기
언패킹(Unpacking)은 리스트나 튜플처럼 여러 값이 들어 있는 자료에서 값들을 한 번에 여러 변수에 나눠 담는 기능입니다.
쉽게 말해, 여러 값을 한 줄에 나눠 담을 수 있게 해주는 파이썬만의 편리한 방법입니다.
point = (3, 5)
x, y = point
zip() 함수는 여러 이터러블을 인자로 받아, 각 이터러블의 같은 인덱스에 해당하는 요소들을 튜플로 묶어주는 기능을 합니다. 예를 들어, 두 리스트를 하나의 쌍으로 묶어 반복해야 할 때 매우 유용합니다.
names = ["Alice", "Bob", "Charlie"]
scores = [90, 85, 78]
for name, score in zip(names, scores):
print(f"{name}의 점수는 {score}")

✔️ Java 예시 (개별 할당 및 다중 컬렉션 순회)
import java.util.Arrays;
import java.util.List;
public class JavaUnpackingZipExample {
public static void main(String[] args) {
// 개별 할당
List<String> userInfo = Arrays.asList("Alice", "30", "New York");
String name = userInfo.get(0);
String age = userInfo.get(1);
String city = userInfo.get(2);
System.out.println("Name: " + name + ", Age: " + age + ", City: " + city);
// 다중 컬렉션 순회
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> scores = Arrays.asList(85, 92, 78);
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i) + " scored " + scores.get(i));
}
}
}
✔️ Python 예시 (Unpacking 및 Zip)
# 튜플 언패킹
coordinates = (10, 20)
x, y = coordinates
print(f"Coordinates: x={x}, y={y}") # Output: Coordinates: x=10, y=20
# 리스트 언패킹
person_info = ["Bob", 25, "London"]
name, age, city = person_info
print(f"Person Info: Name={name}, Age={age}, City={city}") # Output: Person Info: Name=Bob, Age=25, City=London
# 함수에서 여러 값 반환 시 언패킹 활용
def get_user_details():
return "Charlie", 35, "Paris"
user_name, user_age, user_city = get_user_details()
print(f"User Details: Name={user_name}, Age={user_age}, City={user_city}") # Output: User Details: Name=Charlie, Age=35, City=Paris
# *를 이용한 언패킹 (일부만 할당하고 나머지는 리스트로 묶기)
data = [1, 2, 3, 4, 5, 6, 7]
first, *middle, last = data
print(f"First: {first}, Middle: {middle}, Last: {last}") # Output: First: 1, Middle: [2, 3, 4, 5, 6], Last: 7
# zip() 함수 사용 예시
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
# 이름과 점수를 함께 묶어서 순회
for name, score in zip(names, scores):
print(f"{name} scored {score}")
# Output:
# Alice scored 85
# Bob scored 92
# Charlie scored 78
# 세 개의 이터러블을 zip으로 묶기
products = ["Laptop", "Mouse", "Keyboard"]
prices = [1200, 25, 75]
quantities = [5, 10, 8]
for product, price, quantity in zip(products, prices, quantities):
print(f"Product: {product}, Price: ${price}, Quantity: {quantity}")
- 코드 간결성: 여러 변수 할당이나 병렬 순회를 한 줄로 표현할 수 있습니다.
- 가독성: 변수의 의미가 명확해지며, 코드의 의도가 더 잘 드러납니다.
- 효율성: 특히 zip은 여러 리스트를 동시에 순회할 때 인덱스 기반 접근보다 편리하고 오류를 줄일 수 있습니다.
3. Context Manager (with 문) - 자원 관리를 자동으로 처리하기
Context Manager는 Python에서 자원(파일, 데이터베이스 연결, 네트워크 소켓 등)을 안전하고 효율적으로 관리하는 데 사용되는 메커니즘입니다.
with 문을 사용하여 특정 자원을 사용하고 나면, 해당 자원이 자동으로 정리되도록 보장합니다.
이는 Java의 try-with-resources 문과 매우 유사하며, try-finally 블록에서 발생할 수 있는 자원 누수 문제를 방지하고 코드를 간결하게 만듭니다.
✔️ Java 예시 – try-with-resources
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class JavaResourceManagement {
public static void main(String[] args) {
// Try-with-resources를 이용한 파일 읽기 (권장 방식)
// example.txt 파일이 없으면 이 코드를 실행하기 전에 파일을 생성해야 합니다.
// 예: echo "Hello, world!" > example.txt
try (BufferedReader reader2 = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader2.readLine()) != null) {
System.out.println("Try-with-resources: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
✔️ Python 예시 – with 문
# example.txt 파일을 생성 (없다면)
# with open('example.txt', 'w') as f:
# f.write("Hello from Python!\n")
# f.write("This is a context manager example.")
# with 문을 이용한 파일 읽기
try:
with open("example.txt", "r") as file:
content = file.read()
print(f"File content (with statement):\n{content}")
# with 블록을 벗어나면 파일은 자동으로 닫힙니다.
# print(file.closed) # True 출력
except FileNotFoundError:
print("Error: example.txt not found. Please create the file.")
except Exception as e:
print(f"An error occurred: {e}")
print("\n--- Custom Context Manager ---")
# 사용자 정의 Context Manager
# Java의 AutoCloseable 인터페이스와 유사하게, __enter__와 __exit__ 메서드를 구현합니다.
class MyResourceManager:
def __init__(self, name):
self.name = name
print(f"{self.name}: 자원을 초기화합니다.")
def __enter__(self):
print(f"{self.name}: 자원을 획득합니다 (enter).")
# 자원 객체를 반환할 수 있습니다.
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 예외 발생 여부에 따라 exc_type, exc_val, exc_tb에 정보가 전달됩니다.
if exc_type:
print(f"{self.name}: 예외가 발생했습니다: {exc_val}")
print(f"{self.name}: 자원을 해제합니다 (exit).")
# 예외를 다시 발생시키려면 False 반환, 처리했으면 True 반환 (보통 True는 사용하지 않음)
return False
# with 문으로 사용자 정의 Context Manager 사용
with MyResourceManager("DB Connection") as db_conn:
print(f"DB Connection: 자원을 사용 중입니다.")
# raise ValueError("Something went wrong!") # 주석을 해제하여 예외 발생 테스트
print("with 블록을 벗어났습니다.")
4. 코드 스타일 가이드 (PEP8) - Python답게 쓰는 문법 습관
PEP8은 Python 코드의 가독성과 일관성을 높이기 위한 공식 스타일 가이드입니다.
Python 커뮤니티에서 가장 널리 사용되는 규칙 모음으로, Java 개발자들이 Java 코딩 컨벤션을 따르는 것과 유사하다고 생각하시면 됩니다.
PEP8을 준수하면 다른 개발자들이 여러분의 코드를 더 쉽게 읽고 이해할 수 있으며, 이는 협업 프로젝트에서 특히 중요합니다.
✔️ 주요 규칙 요약 (Java 개발자 관점)
▪️ 들여 쓰기: 4개의 공백을 사용합니다. (절대 탭을 사용하지 마세요!)
▪️ 줄 길이: 한 줄은 최대 79자(문서 문자열 등은 72자)를 넘지 않도록 합니다. 코드가 길어지면 줄 바꿈을 사용합니다.
▪️ 빈 줄: 함수와 클래스 정의 사이에는 두 줄의 빈 줄을, 메서드 정의 사이에는 한 줄의 빈 줄을 사용합니다.
▪️ 임포트(Imports):
- 각 임포트는 별도의 줄에 작성합니다.
- 항상 파일 상단에 위치시키고, 표준 라이브러리,
서드파티 라이브러리, 로컬 애플리케이션 라이브러리 순서로 그룹화합니다.
- 각 그룹 사이에는 빈 줄을 넣습니다.
▪️ 공백:
- 연산자 주위 (a = b + c)
- 쉼표 뒤 (my_list = [1, 2, 3])
- 키워드와 괄호 사이 (if foo:)
- Java와 달리, 함수 호출 시 괄호 안에는 공백을 넣지 않습니다 (func(arg)).
▪️ 이름 명명 규칙:
| 항목 | 규칙 | 예시 |
| 변수/함수 | snake_case | calculate_sum() |
| 클래스 | CamelCase | UserManager |
| 상수 | UPPER_SNAKE_CASE | MAX_SIZE |
- 변수, 함수, 메서드: 소문자와 밑줄(snake_case)을 사용합니다. my_variable, calculate_total.
- 클래스: 카멜 케이스(CamelCase)를 사용합니다. MyClass, UserManager. (Java와 동일)
- 상수: 모든 대문자와 밑줄을 사용합니다. MAX_SIZE, PI. (Java와 동일)
- 모듈: 짧고 모두 소문자로 된 이름을 사용합니다. 밑줄을 사용할 수도 있습니다.
- 비공개(Private) 멤버: 이름 앞에 밑줄 하나 _를 붙입니다. (강제성 없음, 컨벤션)
- 매직 메서드(Magic Methods): 이름 양쪽에 이중 밑줄 __을 붙입니다. __init__, __str__.
▪️ 주석: 코드를 설명하는 데 필요한 경우에만 사용합니다. 불필요한 주석은 피합니다.
✔️ PEP8 예시
# PEP8을 준수하지 않는 코드 (나쁜 예시)
# Imports grouped improperly and on same line
import os, sys
def MYFUNCTION(var_one,varTwo):
# No blank lines around function
if var_one > 100:
return 'too big'
else:
result = var_one + varTwo # Extra space
return result
class My_Class: # Incorrect class name
def __init__(self, name):
self.name = name
def print_name(self):
print(self.name)
# PEP8을 준수하는 코드 (좋은 예시)
import os
import sys
# Standard library imports first, then blank line
# Then third-party imports (none here)
# Then local application imports
MAX_VALUE = 200 # Constant name (upper_snake_case)
class MyClass: # Class name (CamelCase)
"""
This is a docstring for MyClass.
Docstrings should be enclosed in triple quotes.
"""
def __init__(self, name):
# Method name (snake_case)
self.name = name
def calculate_sum(self, value_one, value_two): # Function/method name (snake_case)
"""
Calculates the sum of two values.
Parameters:
value_one (int): The first value.
value_two (int): The second value.
Returns:
int: The sum of the two values.
"""
# Indentation with 4 spaces
# Operators have spaces around them
# Lines are not too long
if value_one > MAX_VALUE:
return 'Value too big'
else:
result = value_one + value_two
return result
def print_name(self):
"""Prints the name of the object."""
print(self.name)
# Example
my_object = MyClass("Pythonic Example")
my_object.print_name()
sum_result = my_object.calculate_sum(10, 5)
print(f"Sum result: {sum_result}")
✔️ 스타일 도구
▪️ Flake8: PEP8 규칙을 검사하고 오류를 보고하는 도구입니다.
▪️ Black: 자동으로 PEP8 스타일에 맞게 코드를 포맷팅 해주는 비타협적인 코드 포매터입니다.
▪️ isort: 임포트 순서를 자동으로 정렬해 줍니다.
이러한 도구들을 여러분의 개발 환경에 통합하여 사용하면, PEP8을 자연스럽게 지키며 Pythonic한 코드를 작성할 수 있습니다.
✔ 마무리 - Pythonic 사고의 시작점
이번 글에서는 Java 개발자에게 낯설 수 있는 Pythonic한 네 가지 핵심 습관을 정리했습니다.
🔸 리스트 컴프리헨션: 반복문을 간결하게
🔸 언패킹과 zip: 다중 데이터를 명확하게
🔸 Context Manager: 자원을 안전하게
🔸 PEP8 스타일: 코드를 Python답게
이 네 가지는 단순한 문법의 차이를 넘어서, “어떻게 생각하고 표현할 것인가”라는 개발 철학의 전환을 요구합니다.
Java의 구조화된 접근과 달리, Python은 간결함을 통해 의도를 직설적으로 드러내는 스타일을 지향합니다.
※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.
'3.SW개발 > Python' 카테고리의 다른 글
| [Java관점]10편. Python 파일 입출력 – Java IO vs Python open() 비교 (0) | 2025.11.13 |
|---|---|
| [Java관점]9편. Python 모듈과 import – Java와 다른 가져오기 구조 이해하기 (0) | 2025.11.13 |
| [Java관점]8편. Python 멀티스레딩 vs GIL – Java와 Python 병렬 처리 방식 비교 (0) | 2025.11.13 |
| [Java관점]7편. Python 컬렉션 정리 – Java List/Map/Set과의 대응 구조 (0) | 2025.11.12 |
| [Java관점]6편. Python 예외 처리 문법 – Java와 Python의 처리 구조 비교 (0) | 2025.11.12 |