Python
Contents
Project Setup - venv
- Why to use :
- to install modules independently
- to manage modules independently
- How
-
setupvirtual environmentpython -m venv venv -
activatevirtual environment# In Windows venv\Scripts\activate # In Mac or Linux source venv/bin/activate -
managemodules# install pip install {module_name} # check the list of modules pip list # get the list within txt file pip freeze > requirements.txt # install module list using txt file pip install -r requirements.txt -
deactivatevirtunal environmentdeactivate
-
Docstring Convention
-
function
def function_name(param1: int, param2: str) -> bool: """ 한 줄 요약: 함수가 수행하는 작업을 간단히 설명합니다. 여러 줄 설명: 함수의 동작, 사용법, 특별한 조건 등을 설명합니다. 예를 들어, param1은 X를 의미하며, param2는 Y의 역할을 합니다. Args: param1 (int): 설명1. param2 (str): 설명2. Returns: bool: 반환값에 대한 설명. """ return True -
class
class ClassName: """ 클래스 요약: 클래스의 역할이나 목적을 간단히 설명합니다. 클래스의 동작, 속성, 주요 메서드 등을 상세히 기술합니다. 예를 들어, 이 클래스는 X 작업을 수행하며 Y 속성을 관리합니다. Attributes: attr1 (type): 속성1의 설명. attr2 (type): 속성2의 설명. """ def __init__(self, param1: int, param2: str) -> None: """ 클래스의 생성자: 인스턴스를 초기화합니다. Args: param1 (int): 설명1. param2 (str): 설명2. """ self.param1 = param1 self.param2 = param2
Absolute Import vs Relative Import
- Two terms are for where does code start to track
- Absolute Import
- start from
root directoryof project
- start from
- Relative Import
- start from
current script file
- start from
In python2, there was a confliction issue between original module name and created module name. So for the sake of avoiding the confliction, need to usefrom __future__ import absolute_importIn python3, this condition or setup become default, so this isnot necessary
Module
__future__
Deferred evaluation by storing module as string
-
This is for preventing the codes from circular import
from typing import TYPE_CHECKING if TYPE_CHECKING: from mod1 import School class Student: def register_school(school: 'School'): pass
typing
Union
- This represents that parameter type and return type can be one of several specific types
-
|could be used instead of Union from3.10onwardsfrom typing import Union def parse_data(data: Union[str, list]) -> list: if isinstance(data, str): return data.split(",") # 문자열을 리스트로 변환 return data def get_data() -> Union[str, dict]: # 조건에 따라 반환 타입이 달라질 수 있음 if some_condition: return "data as string" return {"key": "value"}
pandas
- CRUD
- C : create
- create empty dataframe
- create dataframe from list of dict
- R : read
- search row by condition ( column name cell value )
- selecting and indexing from Dataframe to sub dataframe ( filtering )
- read and access exact cell
- U : update
- add row with dictionary
- D : delete
- C : create
- Utility
- DataFrame.copy()
- DataFrame.to_dict(“index”)
- DataFrame.to_dict(orient=”records”)
Create
- Create table
def create_table(default_info :dict) -> pd.DataFrame: return pd.DataFrame(default_info) default_header = {"task_name":[], "task_id":[], "assignee":[], "entity_id":[]} table = create_table(default_header) print(table) # ============= # Print results # ============= # Empty DataFrame # Columns: [task_name, task_id, assignee, entity_id] # Index: []
Update
- Add rows
def add_row_to_table(tar_table :pd.DataFrame, input_info :dict) -> None: tar_table.loc[len(tar_table)] = input_info assignee = [{"name":"송태영"}] assignee_str = dumps(assignee) input_infos = [ {"task_name":"fx01", "task_id":123, "assignee":assignee_str, "entity_id":456}, {"task_name":"fx02", "task_id":111, "assignee":assignee_str, "entity_id":333}, {"task_name":"fx03", "task_id":122, "assignee":assignee_str, "entity_id":555}, ] for _info in input_infos: add_row_to_table(table, _info) print(table) # ============= # Print results # ============= # task_name task_id assignee entity_id # 0 fx01 123 [{"name": "\uc1a1\ud0dc\uc601"}] 456 # 1 fx02 111 [{"name": "\uc1a1\ud0dc\uc601"}] 333 # 2 fx03 122 [{"name": "\uc1a1\ud0dc\uc601"}] 555
Read
-
Search row by condition ( filtering )
def search_in_table(tar_table :pd.DataFrame, col_name :str, input_info :str) -> pd.DataFrame: return tar_table.loc[tar_table[col_name] == input_info] res = search_in_table(table, "task_id", 123) print(res) -
Select and Index from Dataframe to sub dataframe ( filtering )
# 한개의 column print(table["task_name"] ) # 선택한 column 들 print(table[["task_name", "entity_id"]] ) # 한개의 row print(table.loc[[1]] ) # 선택한 row들 print(table.loc[[0,2]]) -
Access exact cell
print(table.iloc[0]["task_name"]) print(loads(table.iloc[0]["assignee"])) print(table.to_dict("index")) print(table.to_dict(orient='records')) # ============= # Print results # ============= # fx01 # [{'name': '송태영'}] # {0: {'task_name': 'fx01', 'task_id': 123, 'assignee': '[{"name": "\\uc1a1\\ud0dc\\uc601"}]', 'entity_id': 456}} # [{'task_name': 'fx01', 'task_id': 123, 'assignee': '[{"name": "\\uc1a1\\ud0dc\\uc601"}]', 'entity_id': 456}]
logging
- This example below is about how to pring logs into both console and log file
- 루닥스 . (2024) . Python
logging의 이해- https://rudaks.tistory.com/entry/Python-logging%EC%9D%98-%EC%9D%B4%ED%95%B4
import logging
# Create logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
# set file handler up
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)
# set console handler up
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# set format up
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# add both of two handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# use logs
logger.debug("This is a DEBUG message")
logger.info("This is an INFO message")
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message")
logger.critical("This is a CRITICAL message")
Context Manager (with Statement)
Python의 with 구문은 자원 획득과 해제를 보장하는 Context Manager 패턴의 문법적 표현입니다. try-finally로 동일한 동작을 구현할 수 있지만, with는 해제 코드(__exit__)를 문법 구조로 강제하기 때문에 프로덕션 파이프라인에서 인적 실수를 원천 차단합니다.
with vs try-finally
with 블록을 나가는 순간 Python은 대상 객체의 __exit__() 메서드를 자동 실행합니다. as 뒤의 변수는 해당 객체의 __enter__() 반환값이며, 함수 자체의 return 값이 아닙니다.
# try-finally: 자원 해제 코드를 개발자가 직접 작성 — 누락 위험
loader = bpy.data.libraries.load(blend_path, link=False)
data_from, data_to = loader.__enter__()
try:
if "Hero_Rig" in data_from.objects:
data_to.objects.append("Hero_Rig")
finally:
loader.__exit__(None, None, None)
# with: __exit__ 자동 실행 — 누락 불가
with bpy.data.libraries.load(blend_path, link=False) as (data_from, data_to):
if "Hero_Rig" in data_from.objects:
data_to.objects.append("Hero_Rig")
Pipeline Applications (Blender)
Context Manager는 파이프라인 TD가 DCC 내 데이터 무결성을 지킬 때 세 가지 패턴으로 반복 사용됩니다.
File I/O — ShotGrid / JSON metadata
import json
with open("/path/to/shot_meta.json", "r", encoding="utf-8") as file:
try:
shot_data = json.load(file)
except json.JSONDecodeError:
print("JSON 파일 파싱 실패")
Asset Append/Link via bpy.data.libraries.load
외부 .blend 라이브러리에서 특정 데이터만 선별해 현재 씬으로 가져오는 패턴입니다. with 블록 안에서는 가져올 대상 이름을 data_to에 담기만 하고, 실제 데이터 참조 및 후속 작업(Modifier 할당 등)은 블록 밖에서 수행해야 안전합니다. 네트워크 스토리지 환경에서는 try-except를 병행해 I/O 크래시를 방지합니다.
import bpy
blend_file_path = "/net/storage/assets/hero_prop.blend"
with bpy.data.libraries.load(blend_file_path, link=False) as (data_from, data_to):
# data_from: 외부 파일의 데이터 목록 (읽기 전용)
# data_to: 현재 씬으로 가져올 대상 목록
if "Rig_IronMan" in data_from.objects:
data_to.objects.append("Rig_IronMan")
# 블록 밖에서 후속 처리 — with 블록 종료 후 데이터가 bpy.data에 병합됨
Artist Context Preservation — custom context manager
파이프라인 툴이 오브젝트 선택 상태나 편집 모드를 임시 변경하다 크래시하면 아티스트의 작업 상태가 파손됩니다. contextlib.contextmanager로 커스텀 컨텍스트 매니저를 작성하면 어떤 예외가 발생하더라도 원래 상태로 복원됩니다. 이 패턴은 대규모 스튜디오 자동화 툴셋의 프레임워크 베이스에 심어두는 것이 권장됩니다.
import bpy
from contextlib import contextmanager
@contextmanager
def preserve_user_context(context):
"""아티스트의 활성 오브젝트, 모드, 선택 상태를 보존하는 컨텍스트 매니저."""
active_obj = context.active_object
original_mode = context.mode if active_obj else 'OBJECT'
selected_objs = context.selected_objects[:] # shallow copy
try:
yield
finally:
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
bpy.ops.object.select_all(action='DESELECT')
for obj in selected_objs:
try:
obj.select_set(True)
except ReferenceError:
pass # 스크립트 실행 중 삭제된 오브젝트 예외 처리
if active_obj and active_obj.name in context.view_layer.objects:
context.view_layer.objects.active = active_obj
if original_mode in ['EDIT', 'SCULPT', 'PAINT_TEXTURE']:
bpy.ops.object.mode_set(mode=original_mode)
# Usage
with preserve_user_context(bpy.context):
bpy.ops.mesh.primitive_cube_add()
bpy.ops.object.mode_set(mode='EDIT')
# 에러가 발생해도 아티스트 작업 상태는 with 종료 시 자동 복원됨
Related: Blender pipeline 응용 맥락은 Blender 포스트의
bpy.types.Operator섹션에서 이어집니다.