OS๋ณ ์ค์น ๊ฐ์ด๋(uv/pyenv/winget), ๊ธฐ์ด ๋ฌธ๋ฒ, F-String, ์ฌ๋ผ์ด์ฑ, ์ปดํ๋ฆฌํจ์
, ๋ฐ์ดํฐํด๋์ค, ๋ฐ์ฝ๋ ์ดํฐ, ํ์ค ๋ชจ๋, ๊ฐ์ํ๊ฒฝ ๋ฐ ์ค๋ฌด ํ ๋ ํผ๋ฐ์ค
## 1. Python ์ค์น & ํ๊ฒฝ ๊ตฌ์ฑ (Installation & Version Managers)
```bash
# โก [1] ์ฐจ์ธ๋ ์ด๊ณ ์ ๊ด๋ฆฌ์ 'uv' ์ค์น (๊ฐ์ฅ ์ถ์ฒ: ํ์ด์ฌ ์๋ ๋ค์ด๋ก๋ ๋ฐ ์คํ)
# macOS / Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# ์ํ๋ ํ์ด์ฌ ๋ฒ์ 1์ด ๋ง์ ์ค์น ๋ฐ ํ์ธ
uv python install 3.12
uv python list
uv run --python 3.12 python --version
```
```bash
# ๐ช [2] Windows ํ๊ฒฝ ๊ณต์ ์ค์น
# ๋ฐฉ๋ฒ A. Windows ํจํค์ง ๊ด๋ฆฌ์ (winget ์ถ์ฒ)
winget install Python.Python.3.12
# ๋ฐฉ๋ฒ B. python.org ๊ณต์ ์ธ์คํจ๋ฌ(.exe) ์ค์น ์ ์ฃผ์์ฌํญ
# โ ๏ธ ๋ฐ๋์ ์ค์น ํ๋ฉด ์ฒซ ํ์ด์ง์ [Add python.exe to PATH] ์ฒดํฌ๋ฐ์ค ์ ํ ํ์!
# ์ค์น ํ์ธ ๋ฐ ๋ฒ์ ํ์ธ (Python Launcher)
py -0 # ์ค์น๋ ๋ชจ๋ ํ์ด์ฌ ๋ฒ์ ๋ชฉ๋ก
py -3.12 --version # ํน์ ๋ฒ์ ์คํ
# ๐ [3] macOS ํ๊ฒฝ ์ค์น
# Homebrew๋ฅผ ํตํ ์ค์น
brew update
brew install python@3.12
# ๋ค์ค ๋ฒ์ ๊ด๋ฆฌ๊ฐ ํ์ํ ๋ (pyenv)
brew install pyenv
pyenv install 3.12.3
pyenv global 3.12.3
# ๐ง [4] Linux (Ubuntu / Debian) ํ๊ฒฝ ์ค์น
sudo apt update
sudo apt install -y python3 python3-pip python3-venv
# ์ต์ ํ์ด์ฌ ๋ฒ์ ์ค์น๋ฅผ ์ํ deadsnakes PPA ๋ฑ๋ก
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.12 python3.12-venv python3.12-dev
```
## 2. ๊ธฐ๋ณธ ๋ฌธ๋ฒ & ์ ์ด๋ฌธ (Basic Syntax & Control Flow)
```python
# [1] ๋ณ์ & f-string ํฌ๋งทํ
(Python 3.6+)
name = "Lucky"
score = 95.678
print(f"์ด๋ฆ: {name}, ์ ์: {score:.2f}") # ์์์ ๋์งธ์๋ฆฌ: 95.68
# ๋๋ฒ๊น
์ฉ self-documenting ํํ์ (Python 3.8+)
print(f"{name=}, {score=}") # name='Lucky', score=95.678
# [2] ์กฐ๊ฑด๋ฌธ & ์ผํญ ์ฐ์ฐ์ (Ternary Operator)
status = "Adult" if age >= 18 else "Minor"
# [3] Walrus ์ฐ์ฐ์ (๋์
ํํ์ := Python 3.8+)
# ์กฐ๊ฑด๋ฌธ ์์์ ๋ณ์๋ฅผ ํ ๋นํ๊ณ ์ฆ์ ํ๊ฐ
if (n := len(data)) > 10:
print(f"๋ฐ์ดํฐ๊ฐ ๋๋ฌด ๊น๋๋ค: {n}๊ฐ")
# [4] ๊ตฌ์กฐ์ ํจํด ๋งค์นญ (match-case Python 3.10+)
def handle_status(status_code):
match status_code:
case 200:
return "OK"
case 400 | 404 as err:
return f"ํด๋ผ์ด์ธํธ ์ค๋ฅ: {err}"
case 500:
return "์๋ฒ ์ค๋ฅ"
case _:
return "์ ์ ์๋ ์ฝ๋"
```
## 3. ํต์ฌ ์๋ฃ๊ตฌ์กฐ (Lists, Dicts, Sets, Tuples)
```python
# ๐ [1] List (๋ฆฌ์คํธ: ์์ ์์, ๊ฐ๋ณ)
fruits = ["apple", "banana", "cherry"]
fruits.append("orange") # ๋์ ์ถ๊ฐ
fruits.insert(1, "grape") # ์ธ๋ฑ์ค 1์ ์ฝ์
fruits.extend(["mango", "kiwi"]) # ๋ค๋ฅธ ๋ฆฌ์คํธ ๋ณํฉ
last = fruits.pop() # ๋ง์ง๋ง ์์ ๊บผ๋ด๊ธฐ
fruits.sort(reverse=True) # ์๋ณธ ์ ๋ ฌ
new_list = sorted(fruits) # ์๋ณธ ์ ์ง, ์ ๋ ฌ๋ ์ ๋ฆฌ์คํธ ๋ฐํ
# ๐ก ์ฌ๋ผ์ด์ฑ ๋งค์ง
arr = [0, 1, 2, 3, 4, 5]
print(arr[1:4]) # [1, 2, 3] (์์:๋-1)
print(arr[:3]) # [0, 2] (์ฒ์๋ถํฐ)
print(arr[::2]) # [0, 2, 4] (2์นธ์ฉ ๊ฑด๋๋ฐ๊ธฐ)
print(arr[::-1]) # [5, 4, 3, 2, 1, 0] (์ญ์ ๋ค์ง๊ธฐ)
# ๐ [2] Dictionary (๋์
๋๋ฆฌ: Key-Value, ์์ ๋ณด์กด)
user = {"name": "lucky", "role": "admin"}
age = user.get("age", 20) # ํค๊ฐ ์์ ๋ ๊ธฐ๋ณธ๊ฐ 20 ๋ฐํ (KeyError ๋ฐฉ์ง)
user.setdefault("tags", []).append("python") # ํค๊ฐ ์์ผ๋ฉด ์ด๊ธฐํ ํ ์ฐ์ฐ
# ๋์
๋๋ฆฌ ๋ณํฉ (Python 3.9+ | ์ฐ์ฐ์)
default_cfg = {"host": "localhost", "port": 8080}
custom_cfg = {"port": 9000, "debug": True}
merged = default_cfg | custom_cfg # custom_cfg๊ฐ ์ฐ์ ์ ์ฉ๋จ
# ๐ข [3] Set (์งํฉ: ์ค๋ณต ๋ถ๊ฐ, ์์ ์์)
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
print(s1 & s2) # ๊ต์งํฉ {3, 4}
print(s1 | s2) # ํฉ์งํฉ {1, 2, 3, 4, 5, 6}
print(s1 - s2) # ์ฐจ์งํฉ {1, 2}
print(s1 ^ s2) # ๋์นญ์ฐจ์งํฉ(๋ ์ค ํ๋์๋ง ์๋ ๊ฒ) {1, 2, 5, 6}
# ๐ [4] Tuple & ์ธํจํน
point = (10, 20)
x, y = point
a, b, *rest = [1, 2, 3, 4, 5] # rest = [3, 4, 5]
```
## 4. ์ปดํ๋ฆฌํจ์
& ์ ๋๋ ์ดํฐ (Comprehensions & Generators)
```python
# [1] List Comprehension (๋ฆฌ์คํธ ๋ดํฌ)
evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
squares = [x**2 if x % 2 == 0 else x for x in range(5)] # ์กฐ๊ฑด๋ถ if-else
# [2] Dict & Set Comprehension
words = ["apple", "banana", "cherry"]
word_len = {w: len(w) for w in words} # {'apple': 5, 'banana': 6, 'cherry': 6}
unique_lens = {len(w) for w in words} # {5, 6}
# [3] Generator (์ ๋๋ ์ดํฐ: ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ง์ฐ ํ๊ฐ)
# ๊ดํธ()๋ฅผ ์ฌ์ฉํ๋ฉด ํ ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์๊ณ ํ์ํ ๋ ํ๋์ฉ ์์ฑ
gen = (x**2 for x in range(1_000_000))
print(next(gen)) # 0
print(next(gen)) # 1
# yield๋ฅผ ์ฌ์ฉํ ์ ๋๋ ์ดํฐ ํจ์
def fibonacci(limit):
a, b = 0, 1
for _ in range(limit):
yield a
a, b = b, a + b
for num in fibonacci(5):
print(num, end=" ") # 0 1 1 2 3
```
## 5. ํจ์ & ๋งค๊ฐ๋ณ์ ๊ธฐ๋ฒ (Functions & Args)
```python
# [1] ๊ฐ๋ณ ์ธ์ (*args, **kwargs)
def calculate_summary(title, *args, prefix="[LOG]", **kwargs):
total = sum(args)
print(f"{prefix} {title}: ํฉ๊ณ={total}, ์ถ๊ฐ์ต์
={kwargs}")
calculate_summary("์ ์", 10, 20, 30, user="lucky", active=True)
# [2] ์์น ์ ์ฉ(/) ๋ฐ ํค์๋ ์ ์ฉ(*) ์ธ์ (Python 3.8+)
# / ์์ ์ธ์๋ ๋ฐ๋์ ์์น๋ก๋ง, * ๋ค์ ์ธ์๋ ๋ฐ๋์ ์ด๋ฆ์ผ๋ก๋ง ์ ๋ฌ
def connect(ip, port, /, *, timeout=10, secure=True):
pass
# connect(ip="127.0.0.1", 80) -> ์๋ฌ (ip๋ ์์น๋ก๋ง)
connect("127.0.0.1", 80, timeout=5, secure=False) # ์ ์
# [3] ๋๋ค(Lambda) & ๊ณ ์ฐจ ํจ์
nums = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, nums)) # [2, 4]
squared = list(map(lambda x: x**2, nums)) # [1, 4, 9, 16, 25]
# any(), all() ๋ถ๋ฆฌ์ธ ๊ฒ์ฌ
has_negative = any(x < 0 for x in nums) # ํ๋๋ผ๋ ์์๋ฉด True -> False
all_positive = all(x > 0 for x in nums) # ๋ชจ๋ ์์๋ฉด True -> True
```
## 6. ํด๋์ค & ๋ฐ์ดํฐํด๋์ค (OOP & Dataclasses)
```python
# [1] @dataclass (Python 3.7+ ์ค๋ฌด ๊ถ์ฅ DTO)
# __init__, __repr__, __eq__ ๋ฑ์ ์๋ ์์ฑ
from dataclasses import dataclass, field
@dataclass
class User:
id: int
name: str
roles: list[str] = field(default_factory=list) # ๊ฐ๋ณ ๊ธฐ๋ณธ๊ฐ์ factory ์ฌ์ฉ
is_active: bool = True
def has_admin_role(self) -> bool:
return "admin" in self.roles
u1 = User(id=1, name="Lucky", roles=["admin", "user"])
print(u1) # User(id=1, name='Lucky', roles=['admin', 'user'], is_active=True)
# [2] ์ผ๋ฐ ํด๋์ค & ํ๋กํผํฐ (Getter / Setter)
class Account:
def __init__(self, owner: str, balance: int = 0):
self.owner = owner
self._balance = balance # ๊ด๋ก์ ๋น๊ณต๊ฐ ๋ณ์ (_)
@property
def balance(self) -> int:
return self._balance
@balance.setter
def balance(self, amount: int):
if amount < 0:
raise ValueError("์์ก์ ์์๊ฐ ๋ ์ ์์ต๋๋ค.")
self._balance = amount
```
## 7. ๋ฐ์ฝ๋ ์ดํฐ & ์ปจํ
์คํธ ๋งค๋์ (Decorators & Context Managers)
```python
# [1] ์ปค์คํ
๋ฐ์ฝ๋ ์ดํฐ (์คํ ์๊ฐ ์ธก์ )
import time
from functools import wraps
def measure_time(func):
@wraps(func) # ์๋ณธ ํจ์์ __name__, docstring ๋ณด์กด ํ์
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
duration = time.perf_counter() - start
print(f"[{func.__name__}] ์คํ ์๊ฐ: {duration:.4f}์ด")
return result
return wrapper
@measure_time
def heavy_computation():
time.sleep(0.5)
heavy_computation()
# [2] @contextmanager๋ฅผ ํ์ฉํ ์์ ๊ด๋ฆฌ (with ๋ฌธ)
from contextlib import contextmanager
@contextmanager
def temporary_flag():
print(">> ์์: ์์ ํ ๋น")
try:
yield
finally:
print(">> ์ข
๋ฃ: ์์ ์ ๋ฆฌ (์์ธ ๋ฐ์ ์์๋ ๋ฐ๋์ ์คํ)")
with temporary_flag():
print("์์
์ํ ์ค...")
```
## 8. ์์ธ ์ฒ๋ฆฌ & ํ์ผ ์
์ถ๋ ฅ (Exceptions & I/O)
```python
# [1] ๊ฒฌ๊ณ ํ ์์ธ ์ฒ๋ฆฌ ๊ตฌ์กฐ
class InsufficientFundsError(Exception):
"""์ฌ์ฉ์ ์ ์ ๋น์ฆ๋์ค ์์ธ"""
pass
try:
value = int("invalid_num")
except ValueError as e:
print(f"์
๋ ฅ ๋ณํ ์คํจ: {e}")
except (TypeError, KeyError) as e:
print(f"ํ์
๋๋ ํค ์ค๋ฅ: {e}")
else:
print("์์ธ๊ฐ ๋ฐ์ํ์ง ์์์ ๋๋ง ์คํ")
finally:
print("์์ธ ๋ฐ์ ์ฌ๋ถ์ ์๊ด์์ด ํญ์ ์คํ")
# [2] ํ๋์ ์ธ pathlib ํ์ผ ์
์ถ๋ ฅ (๊ถ์ฅ)
from pathlib import Path
# ํ์ผ ์ฐ๊ธฐ (UTF-8)
file_path = Path("output.txt")
file_path.write_text("Hello, Python World!", encoding="utf-8")
# ํ์ผ ์ฝ๊ธฐ
content = file_path.read_text(encoding="utf-8")
# ๋๋ ํ ๋ฆฌ ์ํ
data_dir = Path("./logs")
if data_dir.exists():
for f in data_dir.glob("*.log"):
print(f.name, f.stat().st_size)
```
## 9. ๊ฐ์ํ๊ฒฝ & ํจํค์ง ๊ด๋ฆฌ (venv & pip / uv)
```bash
# ๐ฆ [1] ํ์ค venv ๊ฐ์ํ๊ฒฝ ์์ฑ ๋ฐ ํ์ฑํ
python -m venv .venv
# ๊ฐ์ํ๊ฒฝ ํ์ฑํ (OS๋ณ)
source .venv/bin/activate # macOS / Linux
.venv\Scripts\activate # Windows PowerShell / CMD
# ๋นํ์ฑํ
deactivate
# ๐ [2] ์์กด์ฑ ํจํค์ง ๊ด๋ฆฌ
pip install requests fastapi uvicorn
pip freeze > requirements.txt # ํ์ฌ ํ๊ฒฝ์ ํจํค์ง ๋ชฉ๋ก ์ถ์ถ
pip install -r requirements.txt # ํ์ผ๋ก๋ถํฐ ์ผ๊ด ์ค์น
# โก [3] ์ด๊ณ ์ ๋ชจ๋ ํจํค์ง ๋งค๋์ 'uv' (pip ๋๋น 10~100๋ฐฐ ๋น ๋ฆ)
# uv ์ค์น ํ ์ฌ์ฉ (Rust ๊ธฐ๋ฐ)
pip install uv
uv venv # .venv ์์ฑ (0.01์ด)
uv pip install -r requirements.txt # ์ด๊ณ ์ ํจํค์ง ์ค์น
```
## 10. ํ์ ํ์ค ๋ด์ฅ ๋ชจ๋ (Standard Library Gems)
```python
# [1] collections ๋ชจ๋
from collections import defaultdict, Counter, deque
# defaultdict: ์กด์ฌํ์ง ์๋ ํค ์ ๊ทผ ์ ์๋ ๊ธฐ๋ณธ๊ฐ ์์ฑ
graph = defaultdict(list)
graph["node1"].append("node2") # KeyError ์์ด ๋น ๋ฆฌ์คํธ ์๋ ์์ฑ
# Counter: ์์๋ณ ๋น๋์ ์๋ ๊ณ์ฐ
counts = Counter(["apple", "banana", "apple", "cherry", "apple"])
print(counts.most_common(1)) # [('apple', 3)]
# deque: O(1) ์๋ฐฉํฅ ํ
q = deque([1, 2, 3])
q.appendleft(0) # ๋งจ ์ ์ฝ์
O(1)
q.pop() # ๋งจ ๋ค ์ถ์ถ O(1)
# [2] itertools ๋ชจ๋
from itertools import combinations, permutations, chain
arr = [1, 2, 3]
print(list(permutations(arr, 2))) # ์์ด: [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]
print(list(combinations(arr, 2))) # ์กฐํฉ: [(1,2), (1,3), (2,3)]
# [3] datetime & json
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
iso_str = now.isoformat() # ISO 8601 ๋ฌธ์์ด ๋ณํ
import json
data = {"user": "lucky", "active": True}
json_str = json.dumps(data, indent=2, ensure_ascii=False)
parsed = json.loads(json_str)
```
## 11. ํ์
ํํธ (Type Hints: Typing)
```python
from typing import Optional, Union, Callable
# Python 3.10+ ์ต์ ์ ๋์จ ๊ตฌ๋ฌธ (| ์ฐ์ฐ์)
def find_user(user_id: int) -> dict[str, str] | None:
if user_id == 1:
return {"name": "Lucky"}
return None
# ํจ์ ํ์
ํํธ (Callable[[์ธ์ํ์
], ๋ฐํํ์
])
def process_data(data: list[int], transformer: Callable[[int], int]) -> list[int]:
return [transformer(x) for x in data]
# TypedDict: ์ฌ์ ๊ฐ์ฒด์ ํค-๊ฐ ๊ตฌ์กฐ ์๊ฒฉ ๊ฒ์ฆ
from typing import TypedDict
class UserProfile(TypedDict):
id: int
email: str
nickname: str
profile: UserProfile = {"id": 1, "email": "test@test.com", "nickname": "Lucky"}
```
## 12. ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ (AsyncIO Concurrency)
```python
import asyncio
import time
async def fetch_data(task_id: int, delay: float) -> str:
print(f"ํ์คํฌ {task_id} ์์...")
await asyncio.sleep(delay) # ๋
ผ๋ธ๋กํน ๋น๋๊ธฐ ๋๊ธฐ (time.sleep ์ฌ์ฉ ๊ธ์ง!)
print(f"ํ์คํฌ {task_id} ์๋ฃ")
return f"๊ฒฐ๊ณผ-{task_id}"
async def main():
# ์ฌ๋ฌ ๋น๋๊ธฐ ์์
์ ๋ณ๋ ฌ๋ก ๋์ ์คํ (gather)
start = time.perf_counter()
results = await asyncio.gather(
fetch_data(1, 1.0),
fetch_data(2, 1.5),
fetch_data(3, 0.8)
)
print("์ ์ฒด ๊ฒฐ๊ณผ:", results)
print(f"์ด ์์ ์๊ฐ: {time.perf_counter() - start:.2f}์ด (์ฝ 1.5์ด)")
# ๋น๋๊ธฐ ์ด๋ฒคํธ ๋ฃจํ ์คํ
if __name__ == "__main__":
asyncio.run(main())
```
## 13. ์ค๋ฌด ํ & Pythonic ํธ๋ฆญ (Pythonic Tricks)
```python
# [1] enumerate() - ์ธ๋ฑ์ค์ ์์๋ฅผ ๋์์ ์ํ
names = ["Alice", "Bob", "Charlie"]
for idx, name in enumerate(names, start=1):
print(f"{idx}์: {name}")
# [2] zip() - ๋ ๊ฐ ์ด์์ ์ดํฐ๋ฌ๋ธ์ ๋ณ๋ ฌ ๋ฌถ์ ์ํ
ages = [24, 30, 28]
for name, age in zip(names, ages):
print(f"{name} ({age}์ธ)")
# [3] ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ __slots__ (๋๋ ๊ฐ์ฒด ์์ฑ ์ ๋ฉ๋ชจ๋ฆฌ 50% ์ด์ ์ ๊ฐ)
class Point:
__slots__ = ('x', 'y') # ์ธ์คํด์ค ๋์
๋๋ฆฌ(__dict__) ์์ฑ์ ์ต์
def __init__(self, x, y):
self.x = x
self.y = y
# [4] ๋ฆฌ์คํธ ์ค๋ณต ์ ๊ฑฐํ๋ฉด์ ์๋ ์์ ์ ์ง (Python 3.7+ dict ํน์ง ํ์ฉ)
raw = [3, 1, 2, 3, 4, 1, 5]
deduped = list(dict.fromkeys(raw)) # [3, 1, 2, 4, 5] (set(raw)๋ ์์๊ฐ ์์)
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์