2010-07-30 5 views
5

클래스의 __init__ 메소드에 매개 변수가 무엇인지 물어볼 수 있기를 원합니다. 직접적인 접근 방식은 다음과 같습니다.Python에서 함수의 매개 변수 찾기

cls.__init__.__func__.__code__.co_varnames[:code.co_argcount] 

그러나 클래스에 데코레이터가있는 경우에는 작동하지 않습니다. 데코레이터에 의해 반환 된 함수에 대한 매개 변수 목록을 제공합니다. 나는 원래의 __init__ 메쏘드로 내려가 원래의 매개 변수를 얻고 싶습니다. 실내 장식의 경우, 장식 기능은 장식에 의해 반환 된 함수의 폐쇄에서 찾을 수 것입니다 : 어떤 장식 폐쇄, 다른 일이있는 경우

cls.__init__.__func__.__closure__[0] 

, 그것은 더 복잡하다 수시로 할 수 있습니다 :

def Something(test): 
    def decorator(func): 
     def newfunc(self): 
      stuff = test 
      return func(self) 
     return newfunc 
    return decorator 

def test(): 
    class Test(object): 
     @Something(4) 
     def something(self): 
      print Test 
    return Test 

test().something.__func__.__closure__ 
(<cell at 0xb7ce7584: int object at 0x81b208c>, <cell at 0xb7ce7614: function object at 0xb7ce6994>) 

그리고 난 원래의 기능에서 매개 변수 또는 데코레이터에서 매개 변수를 원하는지 결정해야합니다. 데코레이터에서 반환하는 함수의 매개 변수는 *args**kwargs 일 수 있습니다. 여러 개의 데코레이터가 있고 어떤 것이 내가 신경 써야하는지 결정해야한다면 어떨까요?

그래서 함수를 장식 할 수있는 경우에도 함수의 매개 변수를 찾을 수있는 가장 좋은 방법은 무엇입니까? 또한 데코레이터의 체인을 내려 장식 된 기능으로 되돌아가는 가장 좋은 방법은 무엇입니까?

업데이트 : 값을 재미없는이 시점 자료 보고서에서

import abc 
import collections 

IGNORED_PARAMS = ("self",) 
DEFAULT_PARAM_MAPPING = {} 
DEFAULT_DEFAULT_PARAMS = {} 

class DICT_MAPPING_Placeholder(object): 
    def __get__(self, obj, type): 
     DICT_MAPPING = {} 
     for key in type.PARAMS: 
      DICT_MAPPING[key] = None 
     for cls in type.mro(): 
      if "__init__" in cls.__dict__: 
       cls.DICT_MAPPING = DICT_MAPPING 
       break 
     return DICT_MAPPING 

class PARAM_MAPPING_Placeholder(object): 
    def __get__(self, obj, type): 
     for cls in type.mro(): 
      if "__init__" in cls.__dict__: 
       cls.PARAM_MAPPING = DEFAULT_PARAM_MAPPING 
       break 
     return DEFAULT_PARAM_MAPPING 

class DEFAULT_PARAMS_Placeholder(object): 
    def __get__(self, obj, type): 
     for cls in type.mro(): 
      if "__init__" in cls.__dict__: 
       cls.DEFAULT_PARAMS = DEFAULT_DEFAULT_PARAMS 
       break 
     return DEFAULT_DEFAULT_PARAMS 

class PARAMS_Placeholder(object): 
    def __get__(self, obj, type): 
     func = type.__init__.__func__ 
     # unwrap decorators here 
     code = func.__code__ 
     keys = list(code.co_varnames[:code.co_argcount]) 
     for name in IGNORED_PARAMS: 
      try: keys.remove(name) 
      except ValueError: pass 
     for cls in type.mro(): 
      if "__init__" in cls.__dict__: 
       cls.PARAMS = tuple(keys) 
       break 
     return tuple(keys) 

class BaseMeta(abc.ABCMeta): 
    def __init__(self, name, bases, dict): 
     super(BaseMeta, self).__init__(name, bases, dict) 
     if "__init__" not in dict: 
      return 
     if "PARAMS" not in dict: 
      self.PARAMS = PARAMS_Placeholder() 
     if "DEFAULT_PARAMS" not in dict: 
      self.DEFAULT_PARAMS = DEFAULT_PARAMS_Placeholder() 
     if "PARAM_MAPPING" not in dict: 
      self.PARAM_MAPPING = PARAM_MAPPING_Placeholder() 
     if "DICT_MAPPING" not in dict: 
      self.DICT_MAPPING = DICT_MAPPING_Placeholder() 


class Base(collections.Mapping): 
    __metaclass__ = BaseMeta 
    """ 
    Dict-like class that uses its __init__ params for default keys. 

    Override PARAMS, DEFAULT_PARAMS, PARAM_MAPPING, and DICT_MAPPING 
    in the subclass definition to give non-default behavior. 

    """ 
    def __init__(self): 
     pass 
    def __nonzero__(self): 
     """Handle bool casting instead of __len__.""" 
     return True 
    def __getitem__(self, key): 
     action = self.DICT_MAPPING[key] 
     if action is None: 
      return getattr(self, key) 
     try: 
      return action(self) 
     except AttributeError: 
      return getattr(self, action) 
    def __iter__(self): 
     return iter(self.DICT_MAPPING) 
    def __len__(self): 
     return len(self.DICT_MAPPING) 

print Base.PARAMS 
#() 
print dict(Base()) 
# {} 

:

여기

내가 지금이 권리를하고있는 중이 야 (이름은 피고인의 신원을 보호하기 위해 변경되었습니다) 얼마나 효과적으로이다 인스턴스의 네 가지 상수 및 사전 버전은 비어 있습니다.

class Sub1(Base): 
    def __init__(self, one, two): 
     super(Sub1, self).__init__() 
     self.one = one 
     self.two = two 

Sub1.PARAMS 
# ("one", "two") 
dict(Sub1(1,2)) 
# {"one": 1, "two": 2} 

class Sub2(Base): 
    PARAMS = ("first", "second") 
    def __init__(self, one, two): 
     super(Sub2, self).__init__() 
     self.first = one 
     self.second = two 

Sub2.PARAMS 
# ("first", "second") 
dict(Sub2(1,2)) 
# {"first": 1, "second": 2} 
+1

도대체 왜 그걸 원할거야? – delnan

+4

이것이 어렵다는 사실은 당신에게 옳은 일이 아니라는 것을 알려 주어야합니다. – katrielalex

+0

클래스의 객체를 dicts로 사용할 수 있고 '__getitem__' 및'__iter__'을 통해 공개되는 키를 명시 적으로 제어 할 수 있기를 원합니다. '__init__'의 매개 변수는 위대한 기본 키를 만듭니다. 그래서 프로그래밍 방식으로 이들을 가져옵니다. 나는 단지 디스크립터가 관련 될 때와 같이 코너 케이스를 처리하려고하고있다. –

답변

3

이 장식 고려 : 그러나, 서브 클래스 경우 네 가지 중 하나를 대체 할 수 있습니다, 또는 당신이 __init__에 다른 매개 변수를 포함 할 수 있습니다 그것에서

def rickroll(old_function): 
    return lambda junk, junk1, junk2: "Never Going To Give You Up" 

class Foo(object): 
    @rickroll 
    def bar(self, p1, p2): 
     return p1 * p2 

print Foo().bar(1, 2) 

을의 rickroll 데코레이터 바 소요 메서드를 삭제하고이를 다르게 명명 된 매개 변수를 무시하고 대신 클래식 노래에서 한 줄을 반환하는 새로운 함수로 바꿉니다.

은 원래의 기능에 더 이상의 언급이없고, 가비지 컬렉터가 와서 그것을 좋아 언제든지 제거 할 수 있습니다.

그런 경우 매개 변수 이름 p1과 p2를 어떻게 찾을 수 있는지 알 수 없습니다. 필자가 알고 있듯이 파이썬 인터프리터조차도 그들이 사용하던 것을 전혀 모른다.

+0

LOL 나는 그 rickroll 데코레이터를 가지고 당신을 만났습니다. 이제 답을 읽으세요 ... –

+0

그게 중요한 포인트입니다. 완전히 다른, 관련없는 기능을 반환하는 데코레이터를 확실히 고려하지 않았습니다. 참고 문헌이 부족하다는 것이 맞습니다. 그것은 의미가 있지만, 동시에 클래스에 정의 된 함수가 사라지는 것은 직관적이지 않은 것처럼 보입니다. 너는 나에게 생각할 무언가를 주었다. 감사! –

+0

필자는 http://stackoverflow.com/questions/3481872/decorated-for-python-decorators에서이 주제를보다 구체적인 방식으로 추구해 왔습니다. –

관련 문제