Python에서 매개 변수의 강제 이름 지정


111

Python에서는 함수 정의가있을 수 있습니다.

def info(object, spacing=10, collapse=1)

다음 방법 중 하나로 호출 할 수 있습니다.

info(odbchelper)                    
info(odbchelper, 12)                
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

파이썬이 이름이 지정되어있는 한 임의의 순서 인수를 허용했기 때문입니다.

우리가 겪고있는 문제는 더 큰 함수 중 일부가 커짐에 따라 사람들이 spacing와 사이에 매개 변수를 추가 collapse할 수 있다는 것입니다. 즉, 이름이 지정되지 않은 매개 변수에 잘못된 값이 전달 될 수 있습니다. 또한 때때로 무엇이 들어가야하는지 항상 명확하지 않은 경우도 있습니다. 우리는 사람들이 특정 매개 변수의 이름을 지정하도록 강제하는 방법을 찾고 있습니다. 코딩 표준이 아니라 이상적으로는 플래그 또는 pydev 플러그인입니까?

위의 4 가지 예에서 모든 매개 변수의 이름이 지정되므로 마지막 사람 만 검사를 통과합니다.

확률은 우리가 특정 기능에 대해서만 활성화 할 것이지만이를 구현하는 방법에 대한 제안 또는 가능하다면 감사하겠습니다.

답변:


214

Python 3-예, *인수 목록에서 지정할 수 있습니다 .

에서 문서 :

"*"또는 "* identifier"뒤의 매개 변수는 키워드 전용 매개 변수이며 사용 된 키워드 인수로만 전달 될 수 있습니다.

>>> def foo(pos, *, forcenamed):
...   print(pos, forcenamed)
... 
>>> foo(pos=10, forcenamed=20)
10 20
>>> foo(10, forcenamed=20)
10 20
>>> foo(10, 20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 positional argument (2 given)

다음과 함께 사용할 수도 있습니다 **kwargs.

def foo(pos, *, forcenamed, **kwargs):

32

다음과 같은 방법으로 함수를 정의하여 사람들이 Python3에서 키워드 인수를 사용하도록 할 수 있습니다.

def foo(*, arg0="default0", arg1="default1", arg2="default2"):
    pass

첫 번째 인수를 이름이없는 위치 인수로 만들면 함수를 호출하는 모든 사람이 내가 요청한 키워드 인수를 사용하도록 강요합니다. Python2에서이를 수행하는 유일한 방법은 다음과 같은 함수를 정의하는 것입니다.

def foo(**kwargs):
    pass

그러면 호출자가 kwargs를 사용하도록 강요하지만 필요한 인수 만 수락하도록 확인을해야하는만큼 좋은 해결책은 아닙니다.


11

사실, 대부분의 프로그래밍 언어 는 매개 변수 순서를 함수 호출 계약의 일부로 만들지 만 그럴 필요 는 없습니다 . 왜 그럴까요? 질문에 대한 나의 이해는 파이썬이 이와 관련하여 다른 프로그래밍 언어와 다른지 여부입니다. Python 2에 대한 다른 좋은 답변 외에도 다음을 고려하십시오.

__named_only_start = object()

def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
    if _p is not __named_only_start:
        raise TypeError("info() takes at most 3 positional arguments")
    return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)

호출자가 인수를 제공 spacing하고 collapse위치 (예외없이) 를 제공 할 수있는 유일한 방법은 다음 과 같습니다.

info(arg1, arg2, arg3, module.__named_only_start, 11, 2)

이미 다른 모듈에 속하는 private 요소를 사용하지 않는 규칙은 Python에서 매우 기본적입니다. Python 자체와 마찬가지로 매개 변수에 대한이 규칙은 부분적으로 만 적용됩니다.

그렇지 않으면 호출은 다음과 같은 형식이어야합니다.

info(arg1, arg2, arg3, spacing=11, collapse=2)

통화

info(arg1, arg2, arg3, 11, 2)

매개 변수에 값 11을 할당 _p하고 함수의 첫 번째 명령에 의해 발생하는 예외 를 지정합니다 .

형질:

  • 앞의 매개 변수 _p=__named_only_start는 위치 (또는 이름)로 허용됩니다.
  • 이후의 매개 변수 _p=__named_only_start는 이름으로 만 제공되어야합니다 (특수 감시 개체에 대한 지식을 __named_only_start얻고 사용 하지 않는 경우 ).

장점 :

  • 매개 변수는 숫자와 의미에서 명시 적입니다 (물론 좋은 이름도 선택되면 나중에).
  • 센티넬이 첫 번째 매개 변수로 지정되면 모든 인수를 이름으로 지정해야합니다.
  • 함수를 호출 할 때 __named_only_start해당 위치에서 센티넬 객체 를 사용하여 위치 모드로 전환 할 수 있습니다 .
  • 다른 대안보다 더 나은 성능을 기대할 수 있습니다.

단점 :

  • 검사는 컴파일 타임이 아닌 런타임 중에 발생합니다.
  • 추가 매개 변수 (인수가 아님) 및 추가 검사 사용. 일반 기능에 비해 약간의 성능 저하.
  • 기능은 언어의 직접적인 지원이없는 해킹입니다 (아래 참고 참조).
  • 함수를 호출 할 때 __named_only_start올바른 위치에있는 센티넬 객체 를 사용하여 위치 모드로 전환 할 수 있습니다 . 예, 이것은 또한 프로로 볼 수 있습니다.

이 답변은 Python 2에서만 유효하다는 점을 명심하십시오. Python 3은 다른 답변에서 설명한 유사하지만 매우 우아하고 언어 지원 메커니즘을 구현합니다.

나는 마음을 열고 그것에 대해 생각할 때 어떤 질문이나 다른 사람의 결정이 어리 석고 멍청하거나 어리석은 것처럼 보이지 않는다는 것을 발견했습니다. 오히려 오히려 나는 일반적으로 많은 것을 배웁니다.


"검사는 컴파일 타임이 아니라 런타임 중에 발생합니다." -모든 함수 인수 검사에 해당한다고 생각합니다. 실제로 함수 호출 라인을 실행하기 전까지는 어떤 함수가 실행되고 있는지 항상 알 수는 없습니다. 또한 +1- 이것은 영리합니다.
Eric

@Eric : 정적 검사를 선호했을뿐입니다. 하지만 당신 말이 맞습니다 : 그것은 전혀 파이썬이 아니었을 것입니다. 결정적인 포인트는 아니지만 Python 3의 "*"구조도 동적으로 확인됩니다. 귀하의 의견에 감사드립니다.
Mario Rossi

또한 모듈 변수의 이름을 지정하면 _named_only_start외부 모듈에서 참조 할 수 없게되어 장단점이 제거됩니다. (모듈 범위에서 단일 최고의 밑줄, IIRC을 비공개)
에릭

센티넬의 이름 지정과 관련하여 우리는 a __named_only_start와 a named_only_start(초기 밑줄 없음)를 모두 가질 수 있습니다. 두 번째는 이름이 지정된 모드가 "권장"임을 나타내지 만 "적극적으로 승격"되는 수준은 아닙니다 (공개 및 다른 것은 아닙니다). _names밑줄 로 시작 하는 "비공개 성"에 관해서는 언어에 의해 그다지 강력하게 적용되지 않습니다. 특정 (*가 아닌) 수입품 또는 정규화 된 이름을 사용하여 쉽게 우회 할 수 있습니다. 이것이 여러 파이썬 문서가 "비공개"대신 "비공개"라는 용어를 선호하는 이유입니다.
Mario Rossi

6

"자연스럽게"발생하지 않는 기본값으로 "가짜"첫 번째 키워드 인수를 만들어 Python 2와 Python 3에서 모두 작동 하는 방식으로 이를 수행 할 수 있습니다 . 해당 키워드 인수 앞에 값이없는 하나 이상의 인수가 올 수 있습니다.

_dummy = object()

def info(object, _kw=_dummy, spacing=10, collapse=1):
    if _kw is not _dummy:
        raise TypeError("info() takes 1 positional argument but at least 2 were given")

이렇게하면 다음이 가능합니다.

info(odbchelper)        
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

하지만:

info(odbchelper, 12)                

기능을 다음과 같이 변경하는 경우 :

def info(_kw=_dummy, spacing=10, collapse=1):

모든 인수에는 키워드가 있어야하며 info(odbchelper)더 이상 작동하지 않습니다.

이렇게하면 _kw마지막 항목 뒤에 추가 키워드 인수를 배치 하지 않고도, 뒤에 추가 키워드 인수를 배치 할 수 있습니다 . 예를 들어 논리적으로 그룹화하거나 키워드를 알파벳순으로 정렬하면 유지 관리 및 개발에 도움이 될 수 있습니다.

따라서 def(**kwargs)스마트 편집기에서 서명 정보 를 사용 하고 잃어 버릴 필요가 없습니다 . 당신의 사회적 계약은 (일부) 키워드를 요구함으로써 특정 정보를 제공하는 것입니다. 키워드가 제시된 순서는 무의미 해졌습니다.


2

최신 정보:

사용 **kwargs하면 문제가 해결되지 않는다는 것을 깨달았습니다 . 프로그래머가 원하는대로 함수 인수를 변경하면 예를 들어 함수를 다음과 같이 변경할 수 있습니다.

def info(foo, **kwargs):

이전 코드는 다시 깨질 것입니다 (이제 모든 함수 호출에는 첫 번째 인수가 포함되어야하기 때문입니다).

그것은 정말로 Bryan이 말한 것에 달려 있습니다.


(...) 사람들이 spacingcollapse(...) 사이에 매개 변수를 추가 할 수 있습니다.

일반적으로 함수를 변경할 때 새 인수는 항상 끝에 있어야합니다. 그렇지 않으면 코드가 깨집니다. 분명해야합니다.
누군가 코드가 깨지도록 함수를 변경하면이 변경은 거부되어야합니다.
(브라이언이 말했듯이 계약과 같습니다)

(...) 때때로 들어가야 할 것이 무엇인지가 항상 명확하지 않습니다.

함수 (즉 def info(object, spacing=10, collapse=1)) 의 시그니처를 보면 기본값 이 없는 모든 인수 가 필수라는 것을 즉시 알 수 있습니다.
무엇 인수가입니다, 문서화 문자열로 가야한다.


이전 답변 (완전성을 위해 유지) :

이것은 아마도 좋은 해결책이 아닐 것입니다.

다음과 같이 함수를 정의 할 수 있습니다.

def info(**kwargs):
    ''' Some docstring here describing possible and mandatory arguments. '''
    spacing = kwargs.get('spacing', 15)
    obj = kwargs.get('object', None)
    if not obj:
       raise ValueError('object is needed')

kwargs키워드 인수를 포함하는 사전입니다. 필수 인수가 있는지 여부를 확인하고 그렇지 않은 경우 예외를 발생시킬 수 있습니다.

단점은 어떤 인수가 가능한지 더 이상 명확하지 않을 수도 있지만 적절한 독 스트링을 사용하면 괜찮을 것입니다.


3
나는 당신의 이전 대답을 더 좋아했습니다. 함수에서 ** kwargs 만 허용하는 이유에 대한 설명을 입력하십시오. 결국, 누구나 소스 코드의 모든 것을 변경할 수 있습니다. 결정의 의도와 목적을 설명하는 문서가 필요합니다.
Brandon

이 답변에는 실제 답변이 없습니다!
Phil

2

python3 키워드 전용 인수 ( *)는 다음을 사용하여 python2.x에서 시뮬레이션 할 수 있습니다.**kwargs

다음 python3 코드를 고려하십시오.

def f(pos_arg, *, no_default, has_default='default'):
    print(pos_arg, no_default, has_default)

및 그 동작 :

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 3 were given
>>> f(1, no_default='hi')
1 hi default
>>> f(1, no_default='hi', has_default='hello')
1 hi hello
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required keyword-only argument: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'wat'

이것은 내가 전환의 자유를 촬영했습니다 참고, 다음을 사용하여 시뮬레이션 할 수 있습니다 TypeErrorKeyError은 "이름 필수 인수"경우를, 너무 많은 일이 같은 예외 유형 있는지 확인도 할 수없는 것

def f(pos_arg, **kwargs):
    no_default = kwargs.pop('no_default')
    has_default = kwargs.pop('has_default', 'default')
    if kwargs:
        raise TypeError('unexpected keyword argument(s) {}'.format(', '.join(sorted(kwargs))))

    print(pos_arg, no_default, has_default)

그리고 행동 :

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (3 given)
>>> f(1, no_default='hi')
(1, 'hi', 'default')
>>> f(1, no_default='hi', has_default='hello')
(1, 'hi', 'hello')
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
KeyError: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in f
TypeError: unexpected keyword argument(s) wat

레시피는 python3.x에서도 똑같이 작동하지만 python3.x 인 경우에는 피해야합니다.


아, kwargs.pop('foo')파이썬 2 관용구입니까? 코딩 스타일을 업데이트해야합니다. 저는 여전히 Python 3에서이 접근 방식을 사용하고있었습니다 🤔
Neil

0

함수를 수신 **args전용 으로 선언 할 수 있습니다 . 그것은 키워드 인자를 요구하지만 유효한 이름 만 전달되도록하기위한 추가 작업이 필요합니다.

def foo(**args):
   print args

foo(1,2) # Raises TypeError: foo() takes exactly 0 arguments (2 given)
foo(hello = 1, goodbye = 2) # Works fine.

1
키워드 검사를 추가해야 할뿐만 아니라 서명을 사용하여 메서드를 호출해야한다는 것을 알고있는 소비자에 대해 생각해보십시오 foo(**kwargs). 나는 그것에 무엇을 전달합니까? foo(killme=True, when="rightnowplease")
Dagrooms

정말 다릅니다. 고려하십시오 dict.
Noufal Ibrahim

0

다른 답변에서 말했듯이 함수 서명을 변경하는 것은 나쁜 생각입니다. 끝에 새 매개 변수를 추가하거나 인수가 삽입 된 경우 모든 호출자를 수정하십시오.

그래도하고 싶다면 함수 데코레이터inspect.getargspec 함수를 사용하세요. 다음과 같이 사용됩니다.

@require_named_args
def info(object, spacing=10, collapse=1):
    ....

의 구현은 require_named_args독자를위한 연습으로 남겨집니다.

나는 신경 쓰지 않을 것입니다. 함수가 호출 될 때마다 속도가 느려지고 코드를 더 신중하게 작성하면 더 나은 결과를 얻을 수 있습니다.


-1

**연산자를 사용할 수 있습니다 .

def info(**kwargs):

이런 식으로 사람들은 명명 된 매개 변수를 사용해야합니다.


2
그리고 코드를 읽지 않고 메서드를 호출하는 방법을 모르고 소비자의인지 부하를 증가시킵니다 :(
Dagrooms

언급 된 이유 때문에 이것은 정말 나쁜 습관이며 피해야합니다.
David S.

-1
def cheeseshop(kind, *arguments, **keywords):

파이썬에서 * args를 사용하면이 매개 변수에 대해 n 개의 인수를 전달할 수 있음을 의미합니다.이 매개 변수는 액세스 할 함수 내부의 목록이됩니다.

그리고 키워드 인수를 의미하는 ** kw를 사용하면 dict로 액세스 할 수 있습니다 .n-number의 kw 인수를 전달할 수 있으며 해당 사용자를 제한하려면 시퀀스와 인수를 순서대로 입력해야합니다. * 및 **-(큰 아키텍처에 대한 일반적인 솔루션을 제공하는 비단뱀 방식 ...)

기본값으로 함수를 제한하려면 내부를 확인할 수 있습니다.

def info(object, spacing, collapse)
  spacing = spacing or 10
  collapse = collapse or 1

간격이 0이되기를 원하면 어떻게됩니까? (답변, 당신은 10을 얻습니다). 이 대답은 다른 모든 ** kwargs가 동일한 이유로 대답하는 것처럼 잘못되었습니다.
Phil

-2

프로그래머가 처음에 다른 두 사람 사이에 매개 변수를 추가하는 이유를 모르겠습니다.

함수 매개 변수를 이름 (예 :)과 함께 사용 info(spacing=15, object=odbchelper)하려면 정의 된 순서가 중요하지 않으므로 새 매개 변수를 끝에 넣는 것이 좋습니다.

순서가 중요하기를 원하면 변경하면 아무것도 작동하지 않을 것입니다!


2
이것은 질문에 대한 답이 아닙니다. 그것이 좋은 아이디어인지 아닌지는 관련이 없습니다. 누군가는 어쨌든 그것을 할 수 있습니다.
Graeme Perrow

1
Graeme이 언급했듯이 누군가는 어쨌든 그것을 할 것입니다. 또한 다른 사용자가 사용할 라이브러리를 작성하는 경우 키워드 전용 인수를 강제로 전달 (파이썬 3에만 해당)하면 API를 리팩터링해야 할 때 추가 유연성이 허용됩니다.
s0undt3ch
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.