LangGraph 상태에서
state["messages"]로 꺼내기도 하고state.messages로 꺼내기도 한다. DeepEval의metric.score와 크롤링한 JSON의data["price"]가 같은 자리에서 섞여 나온다. “언제 점이고 언제 대괄호야?”가 헷갈리는 이유 한 번에 정리. 관련: [[파이썬에서 () 소괄호와 [] 대괄호]], isinstance랑 getattr
1. 공통 원리 : 내부에 있는 무언가를 지칭한다
. 과 [] 는 겉보기엔 다르지만 “어떤 컨테이너 안의 하위 요소를 가리킨다” 는 점에서 같은 역할이다. 차이는 “어떤 컨테이너냐” 와 “어떻게 꺼내느냐” 뿐.
| 문법 | 쓰는 대상 | 꺼내는 키 | 내부 훅 메서드 |
|---|---|---|---|
obj.attr | 객체(클래스 인스턴스, 모듈, 패키지) | 코드에 고정된 이름 | __getattr__, __getattribute__ |
obj["key"] | 리스트, 튜플, 딕셔너리, Series, DataFrame 등 subscriptable 객체 | 문자열/숫자/슬라이스(동적) | __getitem__ |
2. 점 . 의 두 얼굴
2-1. 패키지/모듈에서의 점
“커다란 상자 안에서 특정 도구를 꺼낼 때” 사용.
import os.path # os 상자 안의 path 도구
from deepeval import metrics # deepeval 안의 metrics관련 모듈과 객체 참고.
2-2. 객체(Object)에서의 점
객체란 데이터와 기능을 한데 묶어놓은 덩어리. 그 덩어리 안의 상세 정보를 꺼낼 때 점을 씀.
metric = PersonaConsistency()
metric.name # "PersonaConsistency"
metric.score # 0.85
metric.measure() # 메서드 호출 (점 + 소괄호)비유
학생 객체가 하나 있다고 치면:
person = Student(name="홍길동", age=20)
print(person.name) # "홍길동"
print(person.age) # 20학생이라는 객체는 당연히 가지고 있어야 할 특징(name, age) 이 코드 안에 고정돼 있다. 그래서 점으로 꺼냄.
3. 대괄호 [] : 주소나 라벨로 찾기
“여러 개가 모여 있는 주머니에서 순서나 이름표로 찾을 때” 쓴다. 머신러닝/데이터 다룰 때 많이 보이는 이유는 데이터가 보통 수천, 수만 개 목록(List)이나 표(Dictionary)로 되어 있기 때문.
| 대상 | 키 종류 | 예시 |
|---|---|---|
| 리스트(List) | 정수 인덱스 | apartment[101] |
| 튜플(Tuple) | 정수 인덱스 | pair[0], pair[-1] |
| 딕셔너리(Dict) | 문자열/기타 해시 가능 키 | locker["내신발"] |
| 문자열(Str) | 정수 인덱스, 슬라이스 | s[0:5] |
| DataFrame | 컬럼명, 행 조건 | df["price"], df[df["age"] > 20] |
객체에 대괄호 썼을 때 에러
TypeError: 'LLMTestCase' object is not subscriptable의미: “LLMTestCase 객체는 대괄호([])로 값을 꺼낼 수 없는 형식입니다”
반대로 딕셔너리에 점 쓰면:
>>> d = {"name": "John"}
>>> d.name
AttributeError: 'dict' object has no attribute 'name'4. 점 vs 대괄호 정면 비교
| 구분 | 점 . | 대괄호 [] |
|---|---|---|
| 주요 용도 | 미리 정해진 특징(속성) 꺼내기 | 목록에서 순서나 이름표로 찾기 |
| 키가 코드에 고정 | O (런타임 변경 불가) | X (변수로 넘길 수 있음) |
| 키에 공백/한글 가능 | X (obj.my name 안 됨) | O (d["my name"], d["이름"]) |
| 사용 예 | metric.name, metric.score | case_list[0], q_info["id"] |
| 느낌 | ”그 녀석의 이름이 뭐야?" | "그 주머니의 몇 번째 혹은 어떤 이름표를 꺼낼까?“ |
5. 실제 코드 분석 : 섞여 있는 경우
q_info = case_list[case_index][1]단계 분해
1단계 → case_list : 여러 테스트 결과가 줄 서 있는 리스트(목록)
2단계 → [case_index] : “그 목록 중 case_index번째 상자 꺼내줘”
3단계 → 꺼내온 게 (test_case, question_info) 형태의 튜플
4단계 → [1] : 파이썬은 0부터 세므로, 두 번째 요소(question_info) 를 꺼내라
6. Agent 코드에서 자주 보이는 혼합 패턴
6-1. LangGraph 상태 접근
# state는 TypedDict 또는 dict
messages = state["messages"] # 대괄호 (딕셔너리 키)
last_msg = messages[-1] # 대괄호 (리스트 인덱스)
content = last_msg.content # 점 (AIMessage 속성)
tool_name = getattr(last_msg, "name", "") # getattr (안전한 점)한 줄에 세 가지가 다 나온다. 각 객체 종류가 뭐냐에 따라 문법이 바뀜.
6-2. LLM 응답 파싱
# OpenAI 응답
text = response.choices[0].message.content
# └── 객체 ─┘ 리스트 └ 객체 ─┘ 객체
# API raw JSON(dict)으로 받으면
text = response["choices"][0]["message"]["content"]같은 구조인데 점이 전부 대괄호로 바뀐 걸 볼 수 있다. Pydantic 객체는 점, dict는 대괄호.
6-3. Pydantic 모델 & dict 왕복
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
u = User(name="Kim", age=30)
u.name # 객체 속성 접근 → "Kim"
d = u.model_dump() # dict로 변환 → {"name": "Kim", "age": 30}
d["name"] # dict 접근 → "Kim"같은 데이터지만 형태가 객체냐 dict냐에 따라 접근 문법이 갈림.
7. 안전한 접근 : getattr , .get()
직접 꺼내면 키/속성 없을 때 에러가 뜬다. 방어적으로 접근하려면:
| 대상 | 직접 접근 | 안전 접근 |
|---|---|---|
| 객체 | obj.attr → AttributeError | getattr(obj, "attr", default) |
| 딕셔너리 | d["key"] → KeyError | d.get("key", default) |
| 리스트 | lst[10] → IndexError | lst[10] if len(lst) > 10 else default |
자세한 getattr 설명은 isinstance랑 getattr 참고.
8. 동적 키로 꺼내야 할 때
사용자 입력이나 반복문 변수로 키가 바뀌는 경우.
fields = ["name", "age", "email"]
# 딕셔너리는 대괄호로 자연스럽게 동적 키
for f in fields:
print(user_dict[f])
# 객체는 getattr로
for f in fields:
print(getattr(user_obj, f, None))반대로 user_obj.f라고 쓰면 글자 그대로 f라는 속성을 찾아 감. 동적 키 접근은 점 문법이 불가능하다.
9. 직접 확인
>>> class Student:
... def __init__(self, name):
... self.name = name
>>> s = Student("홍길동")
>>> s.name
'홍길동'
>>> s["name"]
TypeError: 'Student' object is not subscriptable
>>> d = {"name": "홍길동"}
>>> d["name"]
'홍길동'
>>> d.name
AttributeError: 'dict' object has no attribute 'name'10. 왜 이렇게 나뉘어 있는가
- 점은 인터프리터가 컴파일 시점에 어떤 속성일지 알 수 있어 빠름. 키는 코드에 박혀 있음
- 대괄호는 런타임에 키를 결정. 변수로 넘길 수 있어 유연하지만 실행해봐야 존재 여부를 알 수 있음
- 두 체계가 나뉘어 있어야 정적 분석 도구(mypy, pylint) 가 객체 속성의 오타를 잡아낼 수 있음. 딕셔너리는 어차피 동적이라 정적으로 못 잡음
트레이드오프
Pydantic, dataclass처럼 구조를 확정짓는 도구를 쓰면 점 접근의 안전성을 얻고, raw JSON을 다룰 땐 대괄호의 유연성을 취함. Agent 코드에선 외부 경계(API 응답, 파일)는 dict로 받아 Pydantic으로 변환 → 내부 로직은 점 접근 패턴이 관례.
11. 한마디 요약
점은 “객체의 고정된 특징”, 대괄호는 “컨테이너 속 요소를 주소/라벨로 지목”이다. 같은 ‘꺼낸다’이지만 대상 종류가 다르다.