키워드 인수를 얻는 것은 실제로 Python 방법으로 전달되었습니다.
문제
나는 명백한 키워드 args가있는 파이썬 방법을 꿈꾸고 있습니다.
def func(a=None, b=None, c=None):
for arg, val in magic_arg_dict.items(): # Where do I get the magic?
print '%s: %s' % (arg, val)
나는 발신자가 실제로 그 방법에 전달한 주장에 대한 사전을 얻고 싶다. **kwargs
, 그러나 나는 발신자가 오래된 임의의 args를 통과 할 수 있기를 원하지 않습니다. **kwargs
.
>>> func(b=2)
b: 2
>>> func(a=3, c=5)
a: 3
c: 5
그래서 : 그런 약자가 있습니까? 제 경우에는 각 인수와 비교하여 다른 인수를 비교하여 다른 주장을 찾을 수 있지만, 이것은 9 가지 논쟁이있을 때 부적절하며 지루합니다. 보너스 포인트의 경우 발신자가 키워드 인수를 통과 할 때에도 기본값을 할당 할 때에도 알려주는 안학을 제공합니다.
>>> func(a=None)
a: None
교활한!
편집하다: (어휘) 함수 서명은 그대로 유지되어야합니다. 그것은 공개 API의 일부이며, 명백한 키워드 Args의 주요 가치는 다큐멘터리 가치에 있습니다. 일을 흥미롭게 만들기 위해. :)
해결책
나는 Lost-Ather의 데코레이터의 장점에서 영감을 받았으며, 그것과 약간의 놀면서 이것을 생각해 냈습니다.
def actual_kwargs():
"""
Decorator that provides the wrapped function with an attribute 'actual_kwargs'
containing just those keyword arguments actually passed in to the function.
"""
def decorator(function):
def inner(*args, **kwargs):
inner.actual_kwargs = kwargs
return function(*args, **kwargs)
return inner
return decorator
if __name__ == "__main__":
@actual_kwargs()
def func(msg, a=None, b=False, c='', d=0):
print msg
for arg, val in sorted(func.actual_kwargs.iteritems()):
print ' %s: %s' % (arg, val)
func("I'm only passing a", a='a')
func("Here's b and c", b=True, c='c')
func("All defaults", a=None, b=False, c='', d=0)
func("Nothin'")
try:
func("Invalid kwarg", e="bogon")
except TypeError, err:
print 'Invalid kwarg\n %s' % err
이것을 인쇄하는 것은 다음과 같습니다.
I'm only passing a a: a Here's b and c b: True c: c All defaults a: None b: False c: d: 0 Nothin' Invalid kwarg func() got an unexpected keyword argument 'e'
나는 이것에 만족한다. 보다 유연한 접근 방식은 '실제_kwargs'로 하드 코딩하는 대신 데코레이터에 사용하려는 속성의 이름을 전달하는 것입니다. 그러나 이것은 솔루션을 보여주는 가장 간단한 접근법입니다.
음, 파이썬은 맛있습니다.
다른 팁
가장 쉽고 간단한 방법은 다음과 같습니다.
def func(a=None, b=None, c=None):
args = locals().copy()
print args
func(2, "egg")
이것은 출력을 제공합니다. {'a': 2, 'c': None, 'b': 'egg'}
. 이유 args
사본이어야합니다 locals
사전은 사전이 변이 가능하다는 것입니다. 따라서이 기능에서 로컬 변수를 만든 경우 args
인수뿐만 아니라 모든 로컬 변수와 그 값을 포함합니다.
내장에 대한 더 많은 문서 locals
기능 여기.
하나의 가능성 :
def f(**kw):
acceptable_names = set('a', 'b', 'c')
if not (set(kw) <= acceptable_names):
raise WhateverYouWantException(whatever)
...proceed...
IOW, 통과 된 이름이 허용 가능한 세트 내에 있는지 확인하기가 매우 쉽고 Python이 올라 가기를 원하는 것을 올리려고합니다 (TypeError, I '는 추측 ;-). 데코레이터로 바꾸기가 매우 쉽습니다. BTW.
또 다른 가능성 :
_sentinel = object():
def f(a=_sentinel, b=_sentinel, c=_sentinel):
...proceed with checks `is _sentinel`...
독특한 물건을 만들어 _sentinel
발신자가 우연히 통과 할 수있는 위험을 제거합니다. None
(또는 발신자가 통과 할 수있는 다른 비 유니 키 기본값 값). 이게 전부입니다 object()
BTW : 우연히 다른 물체와 혼동 될 수없는 극도로 조중 중량의 고유 한 센티넬에 좋습니다 ( is
운영자).
약간 다른 문제의 경우 두 솔루션 중 하나가 바람직합니다.
데코레이터를 사용하여 들어오는 kwargs를 검증하는 것은 어떻습니까?
def validate_kwargs(*keys):
def entangle(f):
def inner(*args, **kwargs):
for key in kwargs:
if not key in keys:
raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys))
return f(*args, **kwargs)
return inner
return entangle
###
@validate_kwargs('a', 'b', 'c')
def func(**kwargs):
for arg,val in kwargs.items():
print arg, "->", val
func(b=2)
print '----'
func(a=3, c=5)
print '----'
func(d='not gonna work')
이 출력을 제공합니다.
b -> 2
----
a -> 3
c -> 5
----
Traceback (most recent call last):
File "kwargs.py", line 20, in <module>
func(d='not gonna work')
File "kwargs.py", line 6, in inner
raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys))
ValueError: Received bad kwarg: 'd', expected: ('a', 'b', 'c')
이것은 참고 자료의 단일 인스턴스로 가장 쉽게 달성됩니다.
# Top of module, does not need to be exposed in __all__
missing = {}
# Function prototype
def myFunc(a = missing, b = missing, c = missing):
if a is not missing:
# User specified argument a
if b is missing:
# User did not specify argument b
이 접근법의 좋은 점은 우리가 "IS"연산자를 사용하고 있기 때문에 발신자는 공허한 덕트를 인수 값으로 전달할 수 있으며, 우리는 여전히 그것을 통과하려는 의미가 아니라는 것을 알게 될 것입니다. 우리는 또한 이런 식으로 불쾌한 데코레이터를 피하고 코드를 조금 더 깨끗하게 유지합니다.
이 작업을 수행하는 더 좋은 방법이있을 것입니다. 그러나 여기에 내 테이크가 있습니다.
def CompareArgs(argdict, **kwargs):
if not set(argdict.keys()) <= set(kwargs.keys()):
# not <= may seem weird, but comparing sets sometimes gives weird results.
# set1 <= set2 means that all items in set 1 are present in set 2
raise ValueError("invalid args")
def foo(**kwargs):
# we declare foo's "standard" args to be a, b, c
CompareArgs(kwargs, a=None, b=None, c=None)
print "Inside foo"
if __name__ == "__main__":
foo(a=1)
foo(a=1, b=3)
foo(a=1, b=3, c=5)
foo(c=10)
foo(bar=6)
그리고 출력 :
Inside foo Inside foo Inside foo Inside foo Traceback (most recent call last): File "a.py", line 18, in foo(bar=6) File "a.py", line 9, in foo CompareArgs(kwargs, a=None, b=None, c=None) File "a.py", line 5, in CompareArgs raise ValueError("invalid args") ValueError: invalid args
이것은 아마도 데코레이터로 변환 될 수 있지만 내 장식자는 작업이 필요합니다. 독자에게 운동으로 떠났다 : p
아마도 *args를 통과하면 오류를 제기할까요?
def func(*args, **kwargs):
if args:
raise TypeError("no positional args allowed")
arg1 = kwargs.pop("arg1", "default")
if kwargs:
raise TypeError("unknown args " + str(kwargs.keys()))
varname 목록이나 일반 구문 분석 기능을 사용하는 것이 간단합니다. 이것을 데코레이터 (Python 3.1)로 만드는 것은 너무 어렵지 않을 것입니다.
def OnlyKwargs(func):
allowed = func.__code__.co_varnames
def wrap(*args, **kwargs):
assert not args
# or whatever logic you need wrt required args
assert sorted(allowed) == sorted(kwargs)
return func(**kwargs)
참고 : 이것이 이미 포장 된 기능이나 기능을 얼마나 잘 작동 시킬지 잘 모르겠습니다. *args
또는 **kwargs
이미.
마법은 대답이 아닙니다.
def funky(a=None, b=None, c=None):
for name, value in [('a', a), ('b', b), ('c', c)]:
print name, value