2012-04-06 1 views
13

는 다음 파이썬 코드를 고려 같은 기능 안에서?파이썬 반성 : 나는 그래서 이름과 함수의 문서화 문자열에서를 얻을에 물음표를 교체해야 할 무엇</p> <pre><code>def function(): "Docstring" name = ??? doc = ??? return name, doc >>> function() "function", "Docstring" </code></pre> <p>: 함수 정의 내부 액세스 기능 이름과 참조 문은

편집 : 답변의 대부분은 지금까지 명시 적으로 정의 내부의 함수의 이름을 하드 코딩. 가능한 새로운 함수 get_name_doc가 호출 된 외부 프레임에서 함수에 액세스하고 이름과 문서를 반환하는 것과 같은 일을 할 수 있습니까?

def get_name_doc(): 
    ??? 

def function(): 
    "Docstring" 

    name, doc = get_name_doc() 

    return name, doc 

>>> function() 
"function", "Docstring" 

답변

13

이 일관된 방법으로 깨끗하게 할 수 없습니다.

그러나 함수의 이름을 바꾸거나 장식하지 않는 한이 값을 사용할 수 있습니다.

>>> def test(): 
...  """test""" 
...  doc = test.__doc__ 
...  name = test.__name__ 
...  return doc, name 
... 
>>> test() 
('test', 'test') 
>>> 

전혀 신뢰할 수 없습니다. 다음은 잘못된 예입니다. 이름 test는 기능이 실제로 생성되는 시간에 정의 된 함수의 글로벌 참조가되어 있지 않기 때문에

>>> def dec(f): 
...  def wrap(): 
...   """wrap""" 
...   return f() 
...  return wrap 
... 
>>> @dec 
... def test(): 
...  """test""" 
...  return test.__name__, test.__doc__ 
... 
>>> test() 
('wrap', 'wrap') 
>>> 

이다. 따라서 모든 실행시 전역 범위에서 조회됩니다. 따라서 데코레이터와 같은 전역 범위의 이름을 변경하면 코드가 손상 될 수 있습니다.

+1

그런 경우'올바른'문서 문자열을 얻을 수있는 방법이 있습니까? 문제 해결을위한 모든 작업이 필요합니까? – George

+0

@ George, 아이디어를 얻으려면 내 게시물 하단을 참조하십시오. 나는 주변에서 해킹하는 것과 같은 것들로 놀았지만 생산 준비로 간주하는 수준에서 작동시키지 못했습니다. – aaronasterling

+0

이 링크의'inspect.stack()'에 관해서 : http://stackoverflow.com/questions/900392/getting-the-caller-function-name-inside-another-function-in-python, 그는 어떻게 알 수 있습니까? 어떤 인덱스가 스택에 액세스합니까? 나는 그것이 추측이 맞지 않다고 생각한다. 어떤 stack [x] [y]를 접근 할 수 있는지 동적으로 파악할 수 있다면 관심이 있습니까? – George

1

당신은 그것을 얻을 수있는 함수의 이름을 사용해야합니다 : http://docs.python.org/library/inspect.html :

def function(): 
    "Docstring" 

    name = function.__name__ 
    doc = function.__doc__ 

    return name, doc 

검사라고도 모듈이있다. 이것은 함수 (또는 파이썬 객체)에 대한 추가 정보를 얻는 데 유용합니다.

+0

명시 적으로 이름을 사용하지 않고 그것을 할 수 있습니까? – dzhelil

+2

파이썬은 명시 적입니다. – jamylak

+1

이 답변은 잘못되었거나 신뢰할 수 없습니다. 데코레이터를 적용 해보십시오. – aaronasterling

1
def function(): 
    "Docstring" 

    name = function.__name__ 
    doc = function.__doc__ 

    return name, doc  

이 그것을해야, 당신의 경우, function에, 함수의 이름을 사용합니다. 여기

그것에 대해 이야기 아주 좋은 튜토리얼 :

그리고 물론 http://epydoc.sourceforge.net/docstrings.html : http://docs.python.org/tutorial/controlflow.html#documentation-strings

편집 : 질문의 수정 된 버전을 참조하십시오, 나는 당신이 엉망이 있다고 생각 inspect.stack()from this SO question. Ayman Hourieh의 대답은 작은 예를 보여줍니다. 이름 변경 및 재 할당 할 수 있기 때문에

1
>>> def function(): 
     "Docstring" 

     name = function.__name__ 
     doc = function.__doc__ 

     return name, doc 

>>> function() 
('function', 'Docstring') 
1

get_doc을 호출하는 함수의 이름과 문서를 찾습니다. 내 의미에서 는 get_doc는 인수와 기능이 있어야합니다 (달성하기 위해 그 정말 쉽게 만들었을 것입니다,하지만 방법은 덜 재미))

import inspect 

def get_doc(): 
    """ other doc 
    """ 
    frame = inspect.currentframe() 

    caller_frame = inspect.getouterframes(frame)[1][0] 
    caller_name = inspect.getframeinfo(caller_frame).function 
    caller_func = eval(caller_name) 

    return caller_name, caller_func.__doc__ 


def func(): 
    """ doc string """ 
    print get_doc() 
    pass 


def foo(): 
    """ doc string v2 """ 
    func() 

def bar(): 
    """ new caller """ 
    print get_doc() 

func() 
foo() 
bar() 
+0

은 함수 호출이 장식되어 있는지 테스트해야하지만 데코레이터가 다음 프레임이되어야하며 이전 프레임이 아니어야하므로 문제가되어서는 안됩니다. –

+0

프레임을 del하지 않으면주기가 만들어집니다. – aaronasterling

+0

@ 또한 글로벌 범위에서 이름을 평가하기 위해'eval'을 사용하기 때문에 데코레이터에서 실패합니다. 그래서 이것은 여러분이 global scope에서'eval'의 이름을 얻기 위해'inspect'를 사용하고 그 함수가 예를 들어 모듈에 존재하지 않는다고 가정한다는 것을 제외하고는 다른 사람들이 준 것과 같은 대답입니다. – aaronasterling

6

아래 코드는 함수의 이름에 대한 문제를 해결한다. 그러나, aaronasterling에 의해 주어진 예제에 대한 올바른 docstring을 감지하지 못합니다. 바이트 코드 객체와 관련된 추상 구문 트리로 되돌아 갈 수있는 방법이 있는지 궁금합니다. 그러면 문서화 문자열을 읽기가 아주 쉬울 것입니다.이것에 대해

import inspect 

def get_name_doc(): 
    outerframe = inspect.currentframe().f_back 
    name = outerframe.f_code.co_name 
    doc = outerframe.f_back.f_globals[name].__doc__  
    return name, doc 

if __name__ == "__main__": 

    def function(): 
     "Docstring" 

     name, doc = get_name_doc() 

     return name, doc 

    def dec(f): 
     def wrap(): 
      """wrap""" 
      return f() 
     return wrap 

    @dec 
    def test(): 
     """test""" 
     return get_name_doc() 

    assert function() == ('function', "Docstring") 
    #The assertion below fails:. It gives: ('test', 'wrap') 
    #assert test() == ('test', 'test') 
1

방법 :

import functools 

def giveme(func): 
    @functools.wraps(func) 
    def decor(*args, **kwargs): 
     return func(decor, *args, **kwargs) 
    return decor 

@giveme 
def myfunc(me): 
    "docstr" 
    return (me.__name__, me.__doc__) 

# prints ('myfunc', 'docstr') 
print myfunc() 

가 얼마의 giveme 장식은 첫 번째 인수로 (장식) 함수 객체를 추가합니다. 이렇게하면 함수는 자신의 이름과 호출 할 때 문서화 문자열에 액세스 할 수 있습니다.

장식으로 인해 원래 myfunc 기능은 decor으로 바뀝니다. 첫 번째 인수가 정확히 myfunc과 같도록하려면 함수에 전달되는 값은 decor이고 func이 아닌 값입니다.

functools.wraps 데코레이터는 decor에 원래 myfunc 기능의 속성 (이름, 문서 문자열 등)을 지정하는 데 사용됩니다.

+0

당신은 래핑 된 함수를 데코레이터를 첫 번째 인수로 전달하고 함수가 아닌 ... – aaronasterling

+0

예, 왜 설명했는지 설명합니다. – yak

+0

설명에서 함수 객체를 전달한다고합니다. – aaronasterling

2
>>> import inspect 
>>> def f(): 
...  """doc""" 
...  name = inspect.getframeinfo(inspect.currentframe()).function 
...  doc = eval(name + '.__doc__') 
...  return name, doc 
... 
>>> f() 
('f', 'doc') 
>>> class C: 
...  def f(self): 
...   """doc""" 
...   name = inspect.getframeinfo(inspect.currentframe()).function 
...   doc = eval(name + '.__doc__') 
...   return name, doc 
... 
>>> C().f() 
('f', 'doc') 
1

'잘 동작하는'데코레이터와 함께 잘 작동하는 하드 코드 된 버전입니다. 함수 뒤에 선언해야합니다. 함수가 나중에 리바운드되면 나중에 변경 사항이 업데이트됩니다.

def get_name_doc(): 
    # global function # this is optional but makes your intent a bit more clear. 
    return function.__name__, function.__doc__ 

이것은 기본 args가 작동하는 방식을 악용한다는 점에서 다소 불쾌한 해킹입니다. 이 함수가 초기화 될 때 바인딩 된 함수를 사용하고 함수가 리바운드 되더라도이를 기억합니다. args로 호출하면 흥미로운 결과로 이어질 것입니다.

def get_name_doc(fn=function): 
    return fn.__name__, fn.__doc__ 

여전히 하드 코딩되었지만 True 인수로 호출되는 함수에서 업데이트됩니다. 기본적으로이 버전은 그렇게 할 때만 업데이트됩니다.

def get_name_doc(update=False): 
    global fn 
    if update: 
     fn = function 
    return fn.__name__, fn.__doc__ 

물론 여기에도 데코레이터 예제가 있습니다.

@decorator # applying the decorator decorator to make it well behaved 
def print_name_doc(fn, *args, **kwargs): 
    def inner(*args, **kwargs): 
     print(fn.__doc__, fn.__name__) # im assuming you just want to print in this case 
     return fn(*args, **kwargs) 
return inner 

데코레이터 데코레이터 (atleast)를 읽어야합니다. 하드 코드 된 것을 포함하는 NamedTuple 소스 (컬렉션 모듈에서)를보십시오. 슬프게도 명명 된 튜플 코드는 다소 이상합니다. 그것은 전통적인 형식보다는 eval과 함께 사용되는 문자열 형식입니다. 그러나 그것은 정말로 깔끔하게 작동합니다. 이것은 가장 유망한 변종 인 것 같습니다. 메타 클래스를 사용하여이 작업을 수행 할 수도 있지만 깔끔한 코드로 이어질 수 있지만 코딩해야 할 장면 뒤에 숨겨진 불쾌한 내용이 포함되어 있습니다. 이 ID는

에 대해 조언합니다. 모듈 끝 부분에 다음 줄을 추가하기 만하면 inspection/reflection/templates/metaclasess에 들어가기보다 쉬운 방법 일 가능성이 있습니다.

help(<module>) 

여기서는 작업중인 모듈의 이름 (문자열)입니다. 또는 변수 __name__. 이것은 복수 모듈 또는 개별 클래스에서 작업하는 경우에도 __init__.py 파일에서 수행 할 수 있습니다. 라고 생각하면으로 생각됩니다.

1

여러 번 언급했듯이 함수 내부에서 함수 이름을 사용하는 것은 실제로 현재 모듈의 globals()에서 동적으로 조회합니다. 모든 종류의 eval()을 사용하는 것은 globals() 사전을 사용하여 이름 확인을 다시 수행하기 때문에 변형 된 것일뿐입니다.대부분의 예제는 멤버 함수로 실패합니다. 먼저 globals()에서 클래스 이름을 찾아야하며, 그 다음 멤버 함수에 액세스 할 수 있습니다. 그래서 실제로

def function(): 
    """ foo """ 
    doc = function.__doc__ 

class Class: 
    def function(): 
     """ bar """ 
     doc = Class.function.__doc__ 

이 동적 조회 충분합니다 많은 경우에

def function(): 
    """ foo """ 
    doc = globals()["function"].__doc__ 

class Class: 
    def function(): 
     """ bar """ 
     doc = globals()["Class"].function.__doc__ 

에 해당합니다. 하지만 실제로 함수 안에 함수 이름을 다시 입력해야합니다. 그러나 헬퍼 함수를 ​​작성하여 호출자의 문서 문자열을 찾으면 헬퍼 함수가 다른 globals() 사전을 사용하여 다른 모듈에있을 수 있다는 사실에 직면하게됩니다. 그래서 유일한 올바른 방법은 현재 프레임 정보를 사용하여 함수를 찾는 것입니다.하지만 Python의 프레임 객체는 함수 객체에 대한 참조를 가지고 있지 않으며 사용하는 "f_code"코드에 대한 참조 만 전달합니다. 그것은 다음과 같은 예를 들어, 함수 객체에 f_code에서 매핑을 찾아 사전 참조 "f_globals"를 검색 할 필요가있다 :

그것은 get_caller_doc 이름
import inspect 

def get_caller_doc(): 
    frame = inspect.currentframe().f_back.f_back 
    for objref in frame.f_globals.values(): 
     if inspect.isfunction(objref): 
      if objref.func_code == frame.f_code: 
       return objref.__doc__ 
     elif inspect.isclass(objref): 
      for name, member in inspect.getmembers(objref): 
       if inspect.ismethod(member): 
        if member.im_func.func_code == frame.f_code: 
         return member.__doc__ 

() 대신 get_my_doc()로 인해 대다수 몇 가지 도우미 함수의 인수로 전달할 문서 문자열을 갖기를 원할 경우 그러나 도우미 함수는 호출자로부터 doc 문자열을 쉽게 얻을 수 있습니다.이 함수는 헬퍼 함수가 테스트의 doc 문자열을 사용하여 일부 로그에 게시하거나 실제 테스트 데이터로 사용할 수있는 unittest 스크립트에서 사용합니다. 그래서 제시된 도우미는 테스트 함수와 테스트 멤버 함수의 doc 문자열 만 찾습니다.

class MyTest: 
    def test_101(self): 
     """ some example test """ 
     self.createProject("A") 
    def createProject(self, name): 
     description = get_caller_doc() 
     self.server.createProject(name, description) 

다른 사용 사례를 위해 예제를 확장하려면 독자에게 맡 깁니다.

2

내 개인 프로젝트의 경우 함수 및 클래스 메소드에 대한 doc 복구 기술을 개발했습니다. 이것은 메인에 자체 테스트가있는 가져 오기 가능한 모듈 (SelfDoc.py)로 구현됩니다. 아래에 포함되어 있습니다. 이 코드는 Linux 및 MacOS의 Python 2.7.8에서와 같이 실행됩니다. 현재 사용되고 있습니다.

#!/usr/bin/env python 

from inspect import (getframeinfo, currentframe, getouterframes) 

class classSelfDoc(object): 

    @property 
    def frameName(self): 
     frame = getframeinfo(currentframe().f_back) 
     return str(frame.function) 

    @property 
    def frameDoc(self): 
     frame = getframeinfo(currentframe().f_back) 
     doc = eval('self.'+str(frame.function)+'.__doc__') 
     return doc if doc else 'undocumented' 

def frameName(): 
    return str(getframeinfo(currentframe().f_back).function) 

def frameDoc(): 
    doc = eval(getframeinfo(currentframe().f_back).function).__doc__ 
    return doc if doc else 'undocumented' 

if __name__ == "__main__": 

    class aClass(classSelfDoc): 
     "class documentation" 

     def __init__(self): 
      "ctor documentation" 
      print self.frameName, self.frameDoc 

     def __call__(self): 
      "ftor documentation" 
      print self.frameName, self.frameDoc 

     def undocumented(self): 
      print self.frameName, self.frameDoc 

    def aDocumentedFunction(): 
     "function documentation" 
     print frameName(), frameDoc() 

    def anUndocumentedFunction(): 
     print frameName(), frameDoc() 

    anInstance = aClass() 
    anInstance() 
    anInstance.undocumented() 

    aDocumentedFunction() 
    anUndocumentedFunction() 
1

참조 http://stefaanlippens.net/python_inspect

import inspect 
# functions 
def whoami(): 
    return inspect.stack()[1][3] 
def whocalledme(): 
    return inspect.stack()[2][3] 
def foo(): 
    print "hello, I'm %s, daddy is %s" % (whoami(), whocalledme()) 
    bar() 
def bar(): 
    print "hello, I'm %s, daddy is %s" % (whoami(), whocalledme()) 
johny = bar 
# call them! 
foo() 
bar() 
johny() 

출력 :

hello, I'm foo, daddy is ? 
hello, I'm bar, daddy is foo 
hello, I'm bar, daddy is ? 
hello, I'm bar, daddy is ?