그래서 저는 최근에 메모 작성에 관한 질문을하고 큰 답을 얻었습니다. 이제 나는 그것을 다음 단계로 가져 가고 싶습니다. 꽤 많은 인터넷 검색 결과를 바탕으로, 키워드 인수를 취한 함수를 캐시 할 수있는 메모 데코레이터의 참조 구현을 찾을 수 없었습니다. 실제로 대부분의 사람들은 단순히 캐시 조회를위한 키로 *args
을 사용 했으므로 목록이나 dicts를 인수로 허용하는 함수를 메모하려는 경우에도 손상 될 수 있습니다.파이썬에서 메모 데코레이터의 키워드 인수를 지원하는 파이썬적인 방법이 있습니까?
필자의 경우 함수의 첫 번째 인수는 캐시 조회를위한 dict 키로 사용하기에 적합한 고유 한 식별자이지만 키워드 인수를 사용하고 동일한 캐시에 계속 액세스 할 수 있기를 원했습니다. 내 말은 my_func('unique_id', 10)
과 my_func(foo=10, func_id='unique_id')
모두 동일한 캐시 된 결과를 반환해야한다는 것입니다.
이 작업을 수행하려면 '첫 번째 인수에 해당하는 키워드에 대해 kwargs를 검사하십시오.'라는 말도 안되는 방법이 필요합니다. 이것은 내가 생각해 낸 것입니다 :
class memoize(object):
def __init__(self, cls):
if type(cls) is FunctionType:
# Let's just pretend that the function you gave us is a class.
cls.instances = {}
cls.__init__ = cls
self.cls = cls
self.__dict__.update(cls.__dict__)
def __call__(self, *args, **kwargs):
"""Return a cached instance of the appropriate class if it exists."""
# This is some dark magic we're using here, but it's how we discover
# that the first argument to Photograph.__init__ is 'filename', but the
# first argument to Camera.__init__ is 'camera_id' in a general way.
delta = 2 if type(self.cls) is FunctionType else 1
first_keyword_arg = [k
for k, v in inspect.getcallargs(
self.cls.__init__,
'self',
'first argument',
*['subsequent args'] * (len(args) + len(kwargs) - delta)).items()
if v == 'first argument'][0]
key = kwargs.get(first_keyword_arg) or args[0]
print key
if key not in self.cls.instances:
self.cls.instances[key] = self.cls(*args, **kwargs)
return self.cls.instances[key]
이것은 실제로 작동한다는 것입니다. 예, 당신은 다음과 같이 장식하는 경우 :
@memoize
class FooBar:
instances = {}
def __init__(self, unique_id, irrelevant=None):
print id(self)
그런 다음 코드에서 당신이
FooBar('12345', 20)
또는
FooBar(irrelevant=20, unique_id='12345')
및
중 하나가 실제로는 foobar의 동일한 인스턴스를 얻을 호출 할 수 있습니다. 그런 다음 첫 번째 인수에 대해 다른 이름을 가진 다른 클래스를 정의 할 수 있습니다. 일반적인 방식으로 작동하기 때문입니다 (예 : 데코레이터가이 클래스가 작동하도록 데코 레이팅하는 클래스에 대해 특정 정보를 알 필요가 없음).
문제가
, 그것은 경건 치 못한 혼란 ;-) inspect.getcallargs
당신이 그것을 제공 한 인수에 정의 된 키워드를 매핑 사전인가를 반환하기 때문에 의미가 있습니다
, 그래서 좀 가짜 인수를 공급하고 다음의 DICT를 검사 통과 된 첫 번째 인수
그런 것들이 존재한다면, 두 가지 종류의 인수를 반환하는 inspect.getcallargs
의 유사어가 키워드 인수의 사전이 아니라 인수 목록으로 통합됩니다. 즉,이 같은 수있는 것 :
def __call__(self, *args, **kwargs):
key = inspect.getcallargsaslist(self.cls.__init__, None, *args, **kwargs)[1]
if key not in self.cls.instances:
self.cls.instances[key] = self.cls(*args, **kwargs)
return self.cls.instances[key]
나는이 직접 조회 캐시 키로 inspect.getcallargs
에서 제공하는 DICT를 사용하는 것입니다 태클로 볼 수있는 다른 방법으로, 그러나 그것은에서 동일한 문자열을 만들기 위해 반복적 인 방법을 필요로 (열쇠를 정렬 한 후에 문자열을 직접 만들어야한다고 생각합니다.) 동일한 해시가 있습니다.
아무도 이것에 대한 의견이 있습니까? 키워드 인수가있는 함수를 호출하고 결과를 캐시하려는 것은 잘못입니까? 아니면 그냥 아주 어려운가?
이 모양이 마음에 들지만'key'가'tuple (a.items()) '를 반환하는 부분에 대해서는 걱정이됩니다. 그것은 분명하지만 똑같은 dicts에 대해 동일한 순서로 키를 정렬 할 수 있습니까? 나는 dict의 순서가 매겨지지 않고 동일한 입력이 주어지면 반복 가능한 문자열을 생성하기 위해'str ({1 : 2,3 : 4}) '과 같은 것에 의존한다고 들었습니다. Very Very Wrong. – robru
'inspect.getargspec (func) .args [0]'은 내가 물어 본 특정 질문 (첫 번째 인수의 이름을 찾는 방법)에 대한 정확한 대답 인 것으로 보이지만 이것을 보다 일반적인 해결책. 내가 그것을 기교있게 할 시간이 있으면 나중에 결과를 게시 할 것입니다. – robru
@Robru : dict 정렬에 대한 좋은 지적. 'tuple (sorted (a.items()))'(다른 옵션은'frozenset (a.items())')으로 변경되었습니다. – georg