[python] Python3의“함수 주석”에 대한 유용한 사용법

함수 주석 : PEP-3107

Python3의 함수 주석을 보여주는 코드 스 니펫을 살펴 보았습니다. 개념은 간단하지만 왜 이것이 Python3으로 구현되었는지 또는 그것들을 잘 사용하는지 생각할 수 없습니다. 아마도 그렇게 나를 밝게 할 수 있습니까?

작동 방식 :

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

인수 후 콜론 다음에 오는 모든 것은 ‘주석’이며 -> 는 함수의 반환 값에 대한 주석입니다.

foo.func_annotations는 사전을 반환합니다 :

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

이것을 사용할 수 있다는 것은 어떤 의미입니까?



답변

나는 이것이 실제로 훌륭하다고 생각합니다.

학문적 배경에서 온 주석은 Java와 같은 언어에 대해 스마트 정적 분석기를 활성화하는 데 귀중한 것으로 판명되었습니다. 예를 들어 상태 제한, 액세스 가능한 스레드, 아키텍처 제한 등과 같은 의미를 정의 할 수 있으며이를 읽고 처리하여 컴파일러에서 얻는 것 이상의 보증을 제공 할 수있는 도구가 많이 있습니다. 전제 조건 / 사후 조건을 확인하는 것을 작성할 수도 있습니다.

필자는 타이핑이 약하기 때문에 특히 파이썬에서 이와 같은 것이 필요하다고 생각하지만 실제로이 구문을 공식적이고 간단하게 만드는 구문은 없었습니다.

보증 이외의 주석에는 다른 용도가 있습니다. Java 기반 도구를 Python에 적용하는 방법을 알 수 있습니다. 예를 들어, 메소드에 특별한 경고를 할당하고 호출 할 때 문서를 읽어야한다는 표시를 제공하는 도구가 있습니다 (예 : 음수 값으로 호출해서는 안되는 메소드가 있다고 가정하십시오). 이름에서 직관적이지 않음). 주석을 사용하면 기술적으로 Python에 이와 같은 것을 작성할 수 있습니다. 마찬가지로 공식 구문이있는 경우 태그를 기반으로 큰 클래스에서 메소드를 구성하는 도구를 작성할 수 있습니다.


답변

함수 주석은 당신이 만드는 것입니다.

문서화에 사용할 수 있습니다.

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

사전 조건 확인에 사용할 수 있습니다.

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ...
AssertionError: Var: y  Value: 31.1 Test: is_int

형식 검사를 구현하는 방법 은 http://www.python.org/dev/peps/pep-0362/ 를 참조 하십시오 .


답변

이것은 답변이 늦었지만, 현재 가장 효과적으로 함수 주석을 사용하는 AFAICT는 PEP- 0484MyPy 입니다.

Mypy는 Python의 선택적 정적 유형 검사기입니다. Python 3.5 베타 1 (PEP 484)에 도입 된 형식 주석에 대한 다음 표준을 사용하여 Python 프로그램에 형식 힌트를 추가하고 mypy를 사용하여 정적으로 형식을 검사 할 수 있습니다.

이렇게 사용 :

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b


답변

그냥 내 대답에서 좋은 사용의 구체적인 예를 추가하는 여기 multimethods에 대한 간단한 메커니즘을 수행 할 수 있습니다 장식과 함께.

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

사용 예 :

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

이것은 Guido의 원래 포스트 쇼 처럼 데코레이터에 유형을 추가하여 수행 할 수 있지만 매개 변수와 유형이 잘못 일치하지 않도록 매개 변수 자체에 주석을 추가하는 것이 좋습니다.

참고 : Python에서는 Python 3 에서 스타일이 제거 function.__annotations__되지 않고 주석에 액세스 할 수 있습니다 .function.func_annotationsfunc_*


답변

Uri는 이미 정답을 제시 했으므로 덜 심각합니다. 따라서 문서 문자열을 더 짧게 만들 수 있습니다.


답변

처음으로 주석을 보았을 때 “좋아요! 마지막으로 유형 확인을 선택할 수 있습니다!”라고 생각했습니다. 물론 주석이 실제로 적용되지 않았다는 것을 알지 못했습니다.

그래서 나는 간단한 함수 데코레이터를 작성하여 그것들을 시행 하기로 결정 했습니다 .

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

확인 라이브러리에 추가했습니다 .


답변

이것이 요청 된 이후 오랜 시간이 지났지 만 질문에 주어진 예제 스 니펫은 PEP 3107에서 (PWP의 끝 부분에서) PEP 예제의 끝 부분에 있습니다. 보기;)

다음은 PEP3107에서 인용 한 것입니다

사용 사례

주석을 논의하는 과정에서 많은 사용 사례가 제기되었습니다. 이 중 일부는 여기에 어떤 종류의 정보가 전달되는지에 따라 분류되어 있습니다. 주석을 사용할 수있는 기존 제품 및 패키지의 예도 포함되어 있습니다.

  • 타이핑 정보 제공
    • 유형 확인 ([3], [4])
    • IDE가 함수가 기대하고 반환하는 타입을 보여 주도록하라 ([17])
    • 함수 오버로딩 / 일반 함수 ([22])
    • 외국어 교량 ([18], [19])
    • 적응 ([21], [20])
    • 술어 논리 함수
    • 데이터베이스 쿼리 매핑
    • RPC 매개 변수 마샬링 ([23])
  • 기타 정보
    • 매개 변수 및 리턴 값에 대한 문서 ([24])

특정 요점 (및 참조)에 대한 자세한 내용은 PEP 를 참조하십시오.