포도가게의 개발일지

Python (6) 본문

Tech

Python (6)

grape.store 2025. 7. 9. 14:55
반응형

일급 객체

  • 런타임에 생성가능
  • 데이터 구조체를 변수나 요소에 할당 가능
  • 함수로 parameter로 전달 가능
  • 함수 결과로 반환 가능

함수 객체 내부

  • 함수도 하나의 객체(object)이며, 이 객체의 속성을 분석하여 함수의 매개변수(parameter) 정보를 알아내는 방법

  • 파이썬에서 함수는 단순히 코드를 실행하는 묶음이 아니라, 그 자체로 하나의 객체입니다. 객체이므로 여러 속성(attribute)을 가집니다.

    __doc__: 함수의 설명문(docstring)을 담고 있습니다. (help(함수명) 실행 시 보입니다.)
    **dict**: 사용자 정의 속성을 추가할 수 있는 딕셔너리입니다.
    **name**: 함수의 이름을 담고 있습니다.

매개변수 정보 가져오기: 왜 필요한가?

import bobo

@bobo.query('/')  
def hello(person):  
    return f'Hello {person}!'
  • 위 코드에서 프로그래머는 웹 요청(request)을 직접 다루지 않습니다. 대신 Bobo 프레임워크가 hello 함수를 내부적으로 조사(introspect)해서 person이라는 매개변수가 필요하다는 것을 알아냅니다. 그리고 웹 요청 URL에 포함된 person 값을 자동으로 이 매개변수에 전달해 줍니다.
import inspect

# tag(name, *content, class_=None, **attrs) 라는 함수가 있다고 가정
sig = inspect.signature(tag)

my_tag = {'name': 'img', 'title': 'Sunset Boulevard', 'src': 'sunset.jpg'}

# my_tag 딕셔너리를 tag 함수의 시그니처에 바인딩
bound_args = sig.bind(**my_tag)
print(bound_args.arguments)
# 출력: {'name': 'img', 'attrs': {'title': 'Sunset Boulevard', 'src': 'sunset.jpg'}}
# -> name은 필수 인수로, 나머지는 **attrs로 잘 묶인 것을 볼 수 있습니다.

# 필수 인수를 빼고 다시 시도
del my_tag['name']
sig.bind(**my_tag)  # TypeError: missing a required argument: 'name' 발생
  • .bind() 메서드를 사용하면, 함수를 실제로 호출하기 전에 넘겨줄 인수가 올바른지 미리 검증할 수 있습니다.
  • 이처럼 .bind()는 실제 함수 호출과 동일한 규칙을 적용하여 인수를 매개변수에 묶어주므로, 잘못된 인수 전달을 사전에 방지하는 데 매우 유용합니다.

고차함수(고위함수, higher-order function)

  • 함수를 인수로 받거나, 실행 결과로 함수를 반환하는 함수,(map(), filter(), reduce ....)

콜러블 객체

  • def, lambda 사용자 정의함수
  • 내장함수 len()
  • 내장 메서드 dict.get()
  • 메서드
  • 클래스, 클래스는 호출될때ㅔ new()메서드를 실행해 init으로 초기화함 이후 인스턴스 반환
  • 클래스 인스턴스, 클래스가 call 메서드를 구현하면 함수로서 호출가능
  • 제너레이터 함수, 호출되면 제너레이터 객체 반환
  • 네이티브 코루틴, aync def, 호출되면 코루틴 객체 반환
  • 비동기 제너레이터 함수, async def 및 yield문, 호출되면 async for문에 사용할 제너레이터 반환.

위치 매개변수, 키워드 매개변수

def tag(name, *content, class_=None, **attrs):
  • name이후에 지점 되지않은 parameter는 content로 전부 tuple형태로 들어감
  • 지정해주면 정의 되어있지 않다면 attrs로 할당됨.

함수 자료형 힌트

  • 자료형 힌트는 주로 IDE나 CI를 이용한 전문 소프트웨어 엔지니어들이 혜택을본다. 이로 인해 자료형 힌트가 선택적 기능으로 제공된다.
  • 전문 소프트웨어 엔지니어들만 파이썬을 사용하지 않는다. 과학자, 상품거래자, 언론인 다양항 학생들이 있다. 정적 타이핑, 서브타이핑, 제네릭 기능이 있는 프로그래밍 언어에 경험이 없는 대부분 사람은 자료형 힌트를 배우는데 상당항 노력이 필요하다.

그래듀얼 타이핑 시스템

  • 대표적으로 typescript, dart 등이 있음.
  • 선택적임
  • 실행 시 자료형 에러를 감지하지 않음
  • 성능을 향상하지 않음

자료형

  • 값의 집합 및 이 값에 적용할 수 있는 함수의 집합.

덕 타이핑

  • 객체는 자료형이 있지만, 매개변수를 포함한 변수는 무형이다.
  • 사실 객체의 선언된 자료형이 무엇이든 상관없고, 그 객체가 어느 연산을 지원하는지가 중요하다.
  • 새가 꽥꽥 운다면 그 새를 오리라 생각하는것
  • 덕 타이핑은 객체에 연산을 시도하는 실행 시에만 적용된다, 실행시 에러가 발생하는 문제가 있다

Any vs object

  • object는 파이썬의 모든 객체가 상속받는 최상위 클래스입니다. 이 변수는 어떤 타입이든 될 수 있지만, 나는 이 변수가 최소한 object라는 것만 알아. 그래서 object가 지원하는 아주 기본적인 연산(메서드)만 허용할게
  • Any 이 변수는 어떤 타입이든 될 수 있고, 어떤 연산이든 지원할 거라고 내가 보장할게. 그러니 이 변수에 대한 타입 검사는 그냥 통과시켜 줘

포스텔 법칙

  • 받을 때는 너그럽게, 보낼 때는 엄격하게
  • 받을 때 (함수 매개변수): 최대한 너그럽게, 즉 추상적인 타입으로 힌트를 작성합니다. 이렇게 하면 함수가 다양한 종류의 입력을 처리할 수 있어 유연성이 높아집니다. 예를 들어, list 대신 Iterable이나 Sequence를 사용합니다.
  • 보낼 때 (함수 반환값): 최대한 엄격하게, 즉 구체적인 타입으로 힌트를 작성합니다. 이렇게 하면 함수를 호출한 쪽에서 반환된 객체의 타입과 사용 가능한 메서드를 명확히 알 수 있어 안정성이 높아집니다. 예를 들어, list나 dict를 직접 사용합니다.

왜 추상 베이스 클래스(ABC)를 사용하는가?

  • 유연성: 함수가 더 다양한 타입의 인수를 받을 수 있어 재사용성이 높아집니다.
  • 명확성: 타입 힌트가 함수에 필요한 최소한의 기능(행동)이 무엇인지를 명확하게 표현합니다.

왜 타입 변수(TypeVar)를 사용하는가?

  • 재사용성: 하나의 함수가 다양한 타입에 대해 안전하게 동작하도록 만들어 코드 중복을 피할 수 있습니다.
  • 타입 안정성: 함수의 입력과 출력 타입 간의 관계를 명확히 하여, Any를 사용할 때보다 훨씬 더 강력한 타입 검사를 제공합니다.
  • 정확한 제약: bound를 사용하여 함수가 요구하는 타입의 능력(capability)(예: 해시 가능)을 정확하게 명시할 수 있습니다.

프로토콜

  • 클래스의 상속 관계와 상관없이, 필요한 메서드나 속성 같은 구조(structure)만 맞으면 같은 타입으로 인정해주는 강력한 기능입니다. 이를 '정적 덕 타이핑(static duck typing)'이라고도 부릅니다.
from typing import Protocol, Any

class SupportsLessThan(Protocol):
    def __lt__(self, other: Any) -> bool: ...

from typing import TypeVar

LT = TypeVar('LT', bound=SupportsLessThan)


def top(series: Iterable[LT], length: int) -> list[LT]:
    ordered = sorted(series, reverse=True)
    return ordered[:length]
  • 위와 같이 여기서 T는 어떤 타입이든 될 수 있습니다. 하지만 이 함수는 내부적으로 sorted()를 사용하므로, series에 들어있는 요소들은 반드시 서로 크기를 비교할 수 있어야 합니다. 즉, < 연산자(내부적으로는 lt 메서드)를 지원해야 합니다. 단순한 TypeVar로는 "크기 비교가 가능한 타입"이라는 행동(behavior)을 표현할 수 없습니다.
  • 결론적으로 사용하는 이유는 아래와 같습니다.
  • 프로토콜(Protocol): 클래스의 이름이나 상속 관계가 아닌 구조와 행동(메서드 유무)에 기반하여 타입을 정의하는 방식입니다.
  • 정적 덕 타이핑: "오리처럼 걷고 오리처럼 꽥꽥거리면, 그것은 오리다"라는 덕 타이핑 개념을 코드 실행 전 타입 검사 시점에 적용하는 것입니다.
  • TypeVar와 Protocol의 결합: TypeVar('T', bound=MyProtocol) 형태로 사용하면, 제네릭 함수가 요구하는 인수의 능력(capability)을 매우 정확하고 유연하게 표현할 수 있어 타입 안정성과 재사용성을 모두 높일 수 있습니다.

'Tech' 카테고리의 다른 글

파이썬 (9)  (0) 2025.07.15
파이썬 (8)  (1) 2025.07.14
python (5)  (0) 2025.07.01
파이썬 (4)  (0) 2025.07.01
파이썬 (3)  (0) 2025.06.30
Comments