Post

[AI Assignment - Python] 🐍 νƒ€μž… νžŒνŠΈμ™€ μ»΄ν”„λ¦¬ν—¨μ…˜ β€” WeatherAnalyzer 클래슀 κ΅¬ν˜„κΈ°

[AI Assignment - Python] 🐍 νƒ€μž… νžŒνŠΈμ™€ μ»΄ν”„λ¦¬ν—¨μ…˜ β€” WeatherAnalyzer 클래슀 κ΅¬ν˜„κΈ°

🎯 과제: WeatherAnalyzer 클래슀 κ΅¬ν˜„

첫 번째 κ³Όμ œλŠ” νƒ€μž… νžŒνŠΈμ™€ μ»΄ν”„λ¦¬ν—¨μ…˜μ„ ν™œμš©ν•œ 날씨 데이터 뢄석 클래슀 λ§Œλ“€κΈ°μž…λ‹ˆλ‹€.

μž…λ ₯ 데이터

1
2
3
4
5
6
weather_data = [
    {"city": "μ„œμšΈ", "date": "2026-04-01", "temp": 15.2, "humidity": 45, "pm25": 32.1},
    {"city": "μ„œμšΈ", "date": "2026-04-02", "temp": 18.5, "humidity": 52, "pm25": 28.7},
    {"city": "λΆ€μ‚°", "date": "2026-04-01", "temp": 17.8, "humidity": 61, "pm25": 22.3},
    {"city": "λΆ€μ‚°", "date": "2026-04-02", "temp": 20.1, "humidity": 58, "pm25": 19.5},
]

μš”κ΅¬μ‚¬ν•­

  1. get_city_avg(city) β€” νŠΉμ • λ„μ‹œμ˜ temp, humidity, pm25 각각의 평균을 λ”•μ…”λ„ˆλ¦¬λ‘œ λ°˜ν™˜
  2. get_good_air_days(threshold) β€” pm25κ°€ threshold μ΄ν•˜μΈ λ‚ λ§Œ 필터링 (μ»΄ν”„λ¦¬ν—¨μ…˜ μ‚¬μš©)
  3. summary() β€” λͺ¨λ“  λ„μ‹œλ³„ 평균을 {"μ„œμšΈ": {...}, "λΆ€μ‚°": {...}} ν˜•νƒœλ‘œ λ°˜ν™˜ (λ”•μ…”λ„ˆλ¦¬ μ»΄ν”„λ¦¬ν—¨μ…˜ μ‚¬μš©)
  4. λͺ¨λ“  ν•¨μˆ˜μ— νƒ€μž… 힌트 적용

1️⃣ 1μ°¨ μ‹œλ„: 일단 κΈ°μ–΅λ‚˜λŠ” λŒ€λ‘œ μž‘μ„±

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class WeatherAnalyzer:
    def __init__(self, list_input: list[dict[str]]):
        self._list_input = list_input

    def get_city_avg(self, city: str) -> dict[str, float]:
        results = {
            item["city"]: (item["temp"] + item["humidity"] + item["pm25"]) / 3
            for item in self._list_input if item['city'] == city
        }
        return results

    def get_good_air_days(self, threshold: float = 30.0) -> list[dict]:
        results = [item for item in self._list_input if item['pm25'] <= threshold]
        return results

    def summary(self) -> dict[str, dict[str, float]]:
        results = [{
            item["city"]: (item["temp"] + item["humidity"] + item["pm25"]) / 3
            for item in self._list_input
        }]
        return results

λ¦¬λ·°μ—μ„œ 지적받은 점

νƒ€μž… 힌트 였λ₯˜: dict[str]은 μœ νš¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. dictλŠ” key와 value νƒ€μž…μ„ λ‘˜ λ‹€ λͺ…μ‹œν•΄μ•Ό ν•©λ‹ˆλ‹€. 값이 str, float, int λ“± μ—¬λŸ¬ νƒ€μž…μ΄ μ„žμ—¬ μžˆμœΌλ―€λ‘œ dict[str, Any]둜 써야 ν•©λ‹ˆλ‹€.

get_city_avg 둜직 였λ₯˜: κ°€μž₯ 큰 μ‹€μˆ˜μ˜€μŠ΅λ‹ˆλ‹€. μ˜¨λ„, μŠ΅λ„, λ―Έμ„Έλ¨Όμ§€λ₯Ό ν•©μ‚°ν•΄μ„œ 3으둜 λ‚˜λˆ„λŠ” 의미 μ—†λŠ” 계산을 ν•˜κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€. μš”κ΅¬μ‚¬ν•­μ€ 각 ν•­λͺ©μ˜ λ„μ‹œλ³„ ν‰κ· μ΄μ—ˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
# λ‚΄κ°€ ν•œ 것 (ν‹€λ¦Ό)
(item["temp"] + item["humidity"] + item["pm25"]) / 3   # β†’ 의미 μ—†λŠ” 숫자

# κΈ°λŒ€ν•œ 것
{"temp": 18.95, "humidity": 59.5, "pm25": 20.9}         # β†’ 각 ν•­λͺ©λ³„ 평균

λ”•μ…”λ„ˆλ¦¬ ν‚€ 쀑볡 문제: μ»΄ν”„λ¦¬ν—¨μ…˜μ—μ„œ 같은 ν‚€(β€œλΆ€μ‚°β€)κ°€ μ—¬λŸ¬ 번 λ‚˜μ˜€λ©΄ λ§ˆμ§€λ§‰ κ°’λ§Œ λ‚¨μŠ΅λ‹ˆλ‹€. 즉 λΆ€μ‚° 4/1 데이터가 μ‚¬λΌμ§‘λ‹ˆλ‹€.

summary의 λ°˜ν™˜ νƒ€μž… 뢈일치: 리슀트둜 κ°μ‹Έμ„œ list[dict]λ₯Ό λ°˜ν™˜ν•˜κ³  μžˆμ—ˆλŠ”λ°, μš”κ΅¬μ‚¬ν•­μ€ dict[str, dict]μ΄μ—ˆμŠ΅λ‹ˆλ‹€.


2️⃣ 2μ°¨ μ‹œλ„: 둜직 μˆ˜μ • + dataclass 도전

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from dataclasses import dataclass, field
from typing import Any

@dataclass
class WeatherAnalyzer:
    city: str
    date: str
    temp: float
    humidity: int
    pm25: float

    def __init__(self, list_input: list[dict[str, Any]]):
        self._list_input = list_input

    def get_city_avg(self, city: str) -> dict[str, float]:
        results = {
            city: [
                {'temp': sum(data['temp'] for data in self._list_input if data['city'] == city) /
                         len([data for data in self._list_input if data['city'] == city])},
                {'humidity': sum(data['humidity'] for data in self._list_input if data['city'] == city) /
                             len([data for data in self._list_input if data['city'] == city])},
                {'pm25': sum(data['pm25'] for data in self._list_input if data['city'] == city) /
                         len([data for data in self._list_input if data['city'] == city])},
            ]
        }
        return results

    def summary(self) -> dict[str, dict[str, float]]:
        cities = set(d['city'] for d in self._list_input)
        results = {item: self.get_city_avg(item) for item in cities}
        return results

μ’‹μ•„μ§„ 점

  • ν•­λͺ©λ³„ 평균을 λ”°λ‘œ κ΅¬ν•˜λŠ” λ°©ν–₯은 λ§žμ•˜μŠ΅λ‹ˆλ‹€.
  • summary()μ—μ„œ get_city_avg()λ₯Ό μž¬ν™œμš©ν•˜λŠ” 섀계도 μ’‹μ•˜μŠ΅λ‹ˆλ‹€.

μ—¬μ „νžˆ 남은 문제

@dataclass 였용: @dataclassλŠ” __init__을 μžλ™ μƒμ„±ν•΄μ£ΌλŠ” λ°μ½”λ ˆμ΄ν„°μΈλ°, 직접 __init__을 μ •μ˜ν•˜λ©΄ μžλ™μƒμ„±μ΄ λ¬΄μ‹œλ©λ‹ˆλ‹€. μœ„μ— μ„ μ–Έν•œ city, date λ“±μ˜ ν•„λ“œλ„ μ‹€μ œλ‘œ μ‚¬μš©λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

λ°˜ν™˜ ꡬ쑰: dict μ•ˆμ— list μ•ˆμ— dict 3κ°œλΌλŠ” λΆˆν•„μš”ν•˜κ²Œ λ³΅μž‘ν•œ ꡬ쑰가 λ§Œλ“€μ–΄μ‘ŒμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
# λ‚΄ λ°˜ν™˜κ°’ (λ„ˆλ¬΄ 볡작)
{"λΆ€μ‚°": [{"temp": 18.95}, {"humidity": 59.5}, {"pm25": 20.9}]}

# κΈ°λŒ€ν•œ λ°˜ν™˜κ°’ (μ‹¬ν”Œ)
{"temp": 18.95, "humidity": 59.5, "pm25": 20.9}

같은 필터링이 6번 반볡: data['city'] == city 필터링을 temp, humidity, pm25λ§ˆλ‹€ sumκ³Ό lenμ—μ„œ 각각 λ°˜λ³΅ν•˜κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 총 6번. λ¨Όμ € ν•œ 번 ν•„ν„°λ§ν•˜κ³  μž¬μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.


3️⃣ 3μ°¨ μ‹œλ„ (μ΅œμ’…): ν”Όλ“œλ°± 반영

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from typing import Any

class WeatherAnalyzer:
    def __init__(self, list_input: list[dict[str, Any]]):
        self._list_input = list_input

    def get_city_avg(self, city: str) -> dict[str, float]:
        city_data = [d for d in self._list_input if d['city'] == city]
        n = len(city_data)
        results = {
            'temp': sum(d['temp'] for d in city_data) / n,
            'humidity': sum(d['humidity'] for d in city_data) / n,
            'pm25': sum(d['pm25'] for d in city_data) / n,
        }
        return results

    def get_good_air_days(self, threshold: float = 30.0) -> list[dict]:
        return [item for item in self._list_input if item['pm25'] <= threshold]

    def summary(self) -> dict[str, dict[str, float]]:
        cities = set(d['city'] for d in self._list_input)
        return {city: self.get_city_avg(city) for city in cities}

μ‹€ν–‰ κ²°κ³Ό

1
2
3
get_city_avg('λΆ€μ‚°')  β†’ {'temp': 18.95, 'humidity': 59.5, 'pm25': 20.9}
get_good_air_days(20) β†’ [{'city': 'λΆ€μ‚°', 'date': '2026-04-02', 'temp': 20.1, 'humidity': 58, 'pm25': 19.5}]
summary()             β†’ {'λΆ€μ‚°': {'temp': 18.95, ...}, 'μ„œμšΈ': {'temp': 16.85, ...}}

πŸ† λͺ¨λ²”λ‹΅μ•ˆκ³Ό 비ꡐ

λ¦¬λ·°μ—μ„œ 받은 λͺ¨λ²”λ‹΅μ•ˆμ—λŠ” λͺ‡ κ°€μ§€ μΆ”κ°€ ν…Œν¬λ‹‰μ΄ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

ν•„λ“œ λ°˜λ³΅μ„ μƒμˆ˜λ‘œ 제거

1
2
3
4
5
6
7
class WeatherAnalyzer:
    FIELDS = ('temp', 'humidity', 'pm25')

    def get_city_avg(self, city: str) -> dict[str, float]:
        city_data = [d for d in self._data if d['city'] == city]
        n = len(city_data)
        return {f: round(sum(d[f] for d in city_data) / n, 2) for f in self.FIELDS}

FIELDSλ₯Ό μƒμˆ˜λ‘œ μ •μ˜ν•΄λ‘λ©΄ ν•„λ“œκ°€ μΆ”κ°€λ˜μ–΄λ„ ν•œ 곳만 μˆ˜μ •ν•˜λ©΄ λ©λ‹ˆλ‹€. λ”•μ…”λ„ˆλ¦¬ μ»΄ν”„λ¦¬ν—¨μ…˜μœΌλ‘œ 3쀄이 1μ€„λ‘œ μ€„μ–΄λ“­λ‹ˆλ‹€.

λ°©μ–΄ μ½”λ“œ

1
2
if not city_data:
    raise ValueError(f"λ„μ‹œ '{city}'의 데이터가 μ—†μŠ΅λ‹ˆλ‹€")

⚠️ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” λ„μ‹œλ₯Ό λ„£μœΌλ©΄ n = 0이 λ˜μ–΄ ZeroDivisionErrorκ°€ λ°œμƒν•©λ‹ˆλ‹€. μ‹€λ¬΄μ—μ„œλŠ” 이런 μ—£μ§€ μΌ€μ΄μŠ€λ₯Ό 항상 κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€.

__repr__ κ΅¬ν˜„

1
2
def __repr__(self) -> str:
    return f"WeatherAnalyzer({len(self._data)}건, λ„μ‹œ: {sorted({d['city'] for d in self._data})})"

print(analyzer) ν–ˆμ„ λ•Œ 객체 정보λ₯Ό ν•œλˆˆμ— λ³Ό 수 μžˆμ–΄ 디버깅에 μœ μš©ν•©λ‹ˆλ‹€.


πŸ“ 이번 κ³Όμ œμ—μ„œ 배운 것

νƒ€μž… 힌트: dict[str]이 μ•„λ‹ˆλΌ dict[str, Any]처럼 key/value νƒ€μž…μ„ λͺ¨λ‘ λͺ…μ‹œν•΄μ•Ό ν•©λ‹ˆλ‹€. λͺ¨λ˜ Pythonμ—μ„œ νƒ€μž… νžŒνŠΈλŠ” 선택이 μ•„λ‹Œ 사싀상 ν‘œμ€€μž…λ‹ˆλ‹€.

μ»΄ν”„λ¦¬ν—¨μ…˜μ˜ 함정: λ”•μ…”λ„ˆλ¦¬ μ»΄ν”„λ¦¬ν—¨μ…˜μ—μ„œ 같은 ν‚€κ°€ μ—¬λŸ¬ 번 λ‚˜μ˜€λ©΄ λ§ˆμ§€λ§‰ κ°’λ§Œ λ‚¨μŠ΅λ‹ˆλ‹€. 데이터λ₯Ό λ¨Όμ € κ·Έλ£Ήν•‘ν•˜κ³  집계해야 ν•©λ‹ˆλ‹€.

@dataclass의 μš©λ„: @dataclassλŠ” 데이터λ₯Ό λ‹΄λŠ” ν΄λž˜μŠ€μ— μ“°λŠ” 것이지, 둜직 μ€‘μ‹¬μ˜ 뢄석 ν΄λž˜μŠ€μ— μ–΅μ§€λ‘œ λΌμ›Œλ„£μ„ ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

필터링 ν•œ 번, μž¬μ‚¬μš© μ—¬λŸ¬ 번: 같은 쑰건의 필터링을 λ°˜λ³΅ν•˜μ§€ 말고 λ³€μˆ˜μ— λ‹΄μ•„μ„œ μž¬μ‚¬μš©ν•©λ‹ˆλ‹€. 가독성과 μ„±λŠ₯ λͺ¨λ‘ μ’‹μ•„μ§‘λ‹ˆλ‹€.

Tip: λ©”μ„œλ“œ μ•ˆμ— printλ₯Ό λ„£μ§€ μ•ŠλŠ” 것이 쒋은 μŠ΅κ΄€μž…λ‹ˆλ‹€. λ©”μ„œλ“œλŠ” 값을 λ°˜ν™˜ν•˜κ³ , 좜λ ₯은 ν˜ΈμΆœν•˜λŠ” μͺ½μ—μ„œ κ²°μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

This post is licensed under CC BY 4.0 by the author.