Development/Python

파이썬 클린코드

위풍당당 가리비 2024. 5. 5. 17:22
반응형
코드 포맷팅
  • PEP8 : 파이썬 코드 포맷 기준 https://peps.python.org/pep-0008/
  • Linter : PEP8을 따르는 지 검사할 수 있는 라이브러리. lint = 보풀, linter = 코드의 보풀을 제거하는 장치.
    • Pylint
    • Flake8 : 코드 스타일 강제화와 강제 변환이 없음.
    • Black : 코드 스타일 강제 변환.
    • Flake8 + Black 조합이면 괜찮은 것 같음.

 

Pythonic 코드 작성법
  • Pythonic code의 장점
    1. 일반적으로 더 좋은 성능을 낸다.
    2. 여러명이 작업하기 편해지고 실수를 줄일 수 있다.
  • 음수 인덱싱, 슬라이싱
    • C에는 없는 파이썬 고유 기능.
  • Context manager
    • 파일, 소켓 연결 후에 자동으로 리소스를 해제해주는 기능.
    • 리소스 해제를 고민할 필요없음.
    • 리소스 해제 뿐만 아니라, 독립적으로 유지되어야하는 코드를 분리하는 좋은 방법임.
      • 더보기
        예를 들어서 DB를 백업해야한다고 할 때 이미 실행되고 있는 DB를 껐다가 백업하고 다시 켜야하고, 백업하는 과정에서 오류가 발생하더라도 DB는 다시 시작되어야한다. 이때 에러 났을 때마다 하나하나 다 DB를 키는 코드를 추가하기 보다는 context manager를 사용하면 가독성 좋게 코드를 짤 수 있다.

        그리고 contextlib를 사용하면 데코레이터 형태로도 내가 구현을 할 수 있다.
        - contextlib.contextmanager
        - contextlib.ContextDecorator
        위 두개 사용해서 custom context manager를 만들 수 있음.
    • Example
      • 더보기
        ## not pythonic
        fd = open(filename)
        try:
        	process_file(fd)
        finally:
        	fd.close() # 수작업으로 닫아줘야한다.
        
        ## pythonic code
        with open(filename) as fd: # 자동으로 닫아준다.
        	process_file(fd)
  • Property
    • Python은 모든 메소드와 멤버 변수는 public이다. Private, protected라는 개념이 없음. 그래서 밑줄 한개0로 구분함.
    • 밑줄 X : Public
    • _ : Private (강제는 아님.)
    • _ _ : 네임 맹글링
      • 외부에서 멤버 변수의 접근을 어렵하게 하기 위해서 "__<변수이름>"  ☞ ☞   "_<클래스 이름>__<변수 이름>" 으로 바꿔버림. ※주의 : 접근이 아예 불가능한거는 아님.
      • 원래 여러번 상속되는 클래스에서 오버라이딩할 때 이름이 겹치는 문제를 회피하기 위한 기능이라서 private 용도로 사용하는 건 X.
  • 그 외에도 책에 더 많이 나와있는데 이거 다보다가는 시간이 없을 듯.
좋은 코드의 일반적인 특징
  • 클린 코드의 궁극적인 목표 : 1) 견고하고, 2) 결함을 최소화하며, 3) 명확히 이해할 수 있도록 하는 것.
    • 조금 더 풀어서 설명하면,
      1. 견고한 소프트웨어 = 프로그램이 예상치 못한 상황에서도 중단되지 않아야함.
      2. 잘못된 데이터를 쉽게 다룬다.
      3. 요구사항이 바뀌더라도 유지보수가 쉬운 소프트웨어 설계
      4. 재사용 가능
      5. 생산성을 높이는 효율적인 코드 작성

 

  • 계약(Contract)에 의한 디자인
    • 코드를 실행하려면 원하는 바를 명확히 정하고 계약을 어겼을 경우에는 예외를 발생시켜서 코드가 실행되지 않도록 해야한다.
    • Input, Output에 대해서 조건을 무조건 작성하고 사전에 약속된 형태가 맞는 지 확인하는 코드가 들어가야함. 이런 확인코드가 들어감으로써 누구의 잘못인지를 정확하게 파악할 수 있다.
    • Precondition : 함수가 제대로 동작하기 위해서 보장해야하는 모든 것들. 변수 타입뿐만 아니라 값까지 확인이 되어야함.
    • Postcondition : 함수의 output이 원하는 값인지를 확인. 클라이언트가 필요로하는 모든 것을 검사한다.

 

  • 방어적 프로그래밍
    • 예상할 수 있는 오류(error handling)나 발생하지 않아야할 오류(=assertion)를 처리하는 방법.
    • 에러 핸들링
      • 오류가 발생하기 쉬운 상황에서 에러 핸들링 프로시저를 사용.
      • 주로 입력 데이터 확인할 때 사용.
      • 에러 핸들링의 주목적은 예상되는 에러를 처리해서 다음 코드를 실행할지, 아니면 프로그램을 중단할지를 결정하는 것.
      • 에러 핸들링은 주로 3가지 방법으로 가능
        1. 값 대체 = 디폴트 값 사용.
        2. 에러 로깅
        3. 예외 처리 = 예외상황을 처리하는 것, (try-except 구문 같은 상황) 그렇지만 예외처리가 많다는 것은 해당 함수가 너무 많은 책임을 가지고 있다는 것이다. 예외가 너무 많이 발생하면 함수를 나눠야하는 신호로 생각해라. 예외는 오직 한가지 일을 하는 함수의 한 부분이어야한다. 예외 처리는 해당 기능을 하는 함수 내에서 해야한다. 바깥에서 하는게 아니다!
    • Assertion
      • 그냥 프로그램 종료. 발생하지 않아야할 오류는 다른 대체 코드를 실행하면 안된다.

 

  • 관심사 분리
    • 책임이 다르면 컴포넌트, 계층, 모듈로 분리되어야한다. 프로그램의 각 부분은 자기 기능만 책임을 지며, 나머지 부분에 대해서는 알 필요 없다.
    • 이렇게 관심사를 구분하는 이유는 파급 효과를 최소화하여 유지보수성을 향상시키는 것
      • 컴포넌트 : 실행되는 가장 작은 하나의 단위. 아래 내용들과 같은 개념으로 묶이는 것이 아님.
      • 라이브러리 : 여러 패키지와 모듈을 모아둔 것.
      • 패키지 : 특정 기능을 위해 여러 모듈을 모아둔 것.
      • 모듈 : 실행과 관계없이 어떤 기능들을 모아둔 것.
    • 응집력과 결합력
      • 응집력 : 객체가 작고 잘 정의된 목적을 가져야 응집력이 높다고 함. 응집력이 높으면 재사용성이 높아짐.
      • 결합력 : 두 개 이상의 객체가 어떻게 의존하는지를 나타냄. 낮을 수록 좋다.
      • 관련 내용

 

  • 개발 지침 약어
    • 개발할 때 중요한 것들을 슬로건처럼 약어로 축약한 용어들이 있음.
    • DRY (Do not Repeat Yourself) / OAOO (Once And Only Once)
      • 코드를 중복해서 작성하지 말아라. 여기저기에 똑같은 함수를 만들어두지 마라.
    • YANGNI (You Ain't Gonna Need It)
      • 미래의 모든 요구사항을 염두에 두고 코드를 복잡하게 짜지 말아라. 현재 필요한 만큼만 짜라.
    • KIS (Kepp It Simple)
      • 과잉 엔지니어링을 하지말고, 최대한 간단하게 짜라.
      • 간단하게 짤수록 유지보수가 쉽다.
    • EAFP (Easier to Ask Forgiveness than Permission) / LBYL (Look Before You Leap)
      • EAFP : 일단 코드 실행하고 동작하지 않을 경우에 대응. try - except
      • LBYL : 예를 들어서 파일을 불러오기 전에 그 파일이 있는지부터 확인.
      • 파이썬스러운 코드는 EAFP라고 함. 근데 굳이 따라야할 필요가 있나 싶음.

 

  • 컴포지션과 상속
    • 컴포지션 (Has-A 관계) : 합성, 상속받지 않고 하나의 멤버변수로 상속받으려는 클래스를 포함하는 것. 인터페이스로만 접근하기 때문에 상속에 비해 결합력을 많이 낮출 수 있다. 상속보다 권장하는 방법.
    • 상속 (Is-A 관계): 부모 클래스를 상속받아서 새로운 클래스를 만드는 것. 좋은 기능이지만 코드의 결합력을 높이는 문제가 있다.
      • 상속을 사용해도 되는 경우 : Is-A + 행동호환성 (하는 행동이 똑같은 경우).
반응형