클래스 본문 내에서 클래스 staticmethod를 호출합니까?


159

클래스 본문 내에서 정적 메소드를 사용하려고 할 때 내장 staticmethod함수를 장식 자로 사용하여 정적 메소드를 다음과 같이 정의하십시오 .

class Klass(object):

    @staticmethod  # use as decorator
    def _stat_func():
        return 42

    _ANS = _stat_func()  # call the staticmethod

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

다음과 같은 오류가 발생합니다.

Traceback (most recent call last):<br>
  File "call_staticmethod.py", line 1, in <module>
    class Klass(object): 
  File "call_staticmethod.py", line 7, in Klass
    _ANS = _stat_func() 
  TypeError: 'staticmethod' object is not callable

왜 이런 일이 발생하는지 이해하고 (descriptor binding)_stat_func() 마지막 사용 후 정적 메소드 로 수동으로 변환 하여 해결할 수 있습니다 .

class Klass(object):

    def _stat_func():
        return 42

    _ANS = _stat_func()  # use the non-staticmethod version

    _stat_func = staticmethod(_stat_func)  # convert function to a static method

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

그래서 내 질문은 :

더 깨끗하거나 더 많은 "Pythonic"에서와 같이 더 나은 방법이 있습니까?


4
Pythonicity에 대해 묻는다면 표준 조언은 전혀 사용하지 않는 staticmethod것입니다. 일반적으로 모듈 수준 함수로 더 유용합니다.이 경우 문제는 문제가되지 않습니다. classmethod, 반면에 ...
Benjamin Hodgson

1
@ poorsod : 예, 그 대안을 알고 있습니다. 그러나이 문제가 발생한 실제 코드에서는 함수를 모듈 수준에 배치하는 대신 정적 메소드로 만드는 것이 내 질문에 사용 된 간단한 예제보다 더 의미가 있습니다.
martineau

답변:


179

staticmethod객체 __func__에는 원래 원시 함수를 저장 하는 속성 이있는 것 같습니다 (그렇게해야한다는 의미가 있습니다). 그래서 이것은 작동합니다 :

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    _ANS = stat_func.__func__()  # call the staticmethod

    def method(self):
        ret = Klass.stat_func()
        return ret

옆으로 정적 메서드 객체에 원래 함수를 저장하는 일종의 속성이 있다고 생각했지만 구체적인 내용은 알지 못했습니다. 누군가에게 물고기를주는 대신 물고기를 가르치는 정신으로, 이것이 내가 조사하고 찾은 것입니다 (파이썬 세션의 C & P).

>>> class Foo(object):
...     @staticmethod
...     def foo():
...         return 3
...     global z
...     z = foo

>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>

대화식 세션에서 비슷한 종류의 파기 ( dir매우 도움이 됨)는 종종 이러한 종류의 질문을 매우 빠르게 해결할 수 있습니다.


좋은 업데이트 ... 문서에서 그것을 보지 못했기 때문에 어떻게 알았는지 묻고 싶었습니다. "구현 세부 사항"일 수 있기 때문에 사용에 대해 약간 긴장합니다.
martineau

추가로 읽은 후에 __func__im_func파이썬 3의 순방향 호환성을 위해 또 다른 이름이며 Py 2.6에 추가되었습니다.
martineau

2
기술적으로는이 맥락에서 문서화되어 있지 않습니다.
martineau

1
@AkshayHazari __func__정적 메서드 의 속성은 staticmethod데코레이터를 사용한 적이없는 것처럼 원래 함수에 대한 참조를 가져옵니다 . 따라서 함수에 인수가 필요한 경우를 호출 할 때 인수를 전달해야합니다 __func__. 당신이 주장하지 않은 것처럼 보이는 오류 메시지. 경우 stat_func이 게시물의 예는 두 개의 인수했다, 당신은 사용합니다_ANS = stat_func.__func__(arg1, arg2)

1
@AkshayHazari 이해가 잘 모르겠습니다. 변수가 될 수 있고 범위 내 변수 일 수 있습니다. 클래스가 정의 된 동일한 범위에서 미리 정의되거나 (종종 전역 적) 클래스 범위 내에서 미리 정의됩니다 ( stat_func자체가 그러한 변수 임). 정의하고있는 클래스의 인스턴스 속성을 사용할 수 없다는 의미입니까? 사실이지만 놀랍지 않습니다. 우리는하지 않습니다 우리가 여전히 클래스를 정의하고 있기 때문에, 클래스의 인스턴스를! 그러나 어쨌든, 나는 단지 당신이 통과시키고 자하는 논쟁에 대한 의미로 그것들을 의미했습니다. 거기에 리터럴을 사용할 수 있습니다.

24

이것이 내가 선호하는 방식입니다.

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

    def method(self):
        return self.__class__.stat_func() + self.__class__._ANS

DRY 원칙Klass.stat_func 때문에이 솔루션을 선호합니다 . Python 3에 새로운 기능이있는 이유 를 상기시킵니다. :)super()

그러나 나는 다른 것에 동의합니다. 일반적으로 가장 좋은 선택은 모듈 수준 기능을 정의하는 것입니다.

예를 들어 @staticmethod함수를 사용하면 재귀가 잘 보이지 않을 수 있습니다 ( Klass.stat_funcinside 를 호출하여 DRY 원리를 깨야합니다 Klass.stat_func). self내부 정적 메서드에 대한 참조가 없기 때문 입니다. 모듈 레벨 기능을 사용하면 모든 것이 정상으로 보입니다.


self.__class__.stat_func()일반 방법으로 사용 하는 것이을 사용하는 것 보다 장점 (DRY 및 그 밖의 모든 것)이 있다는 데 동의하지만 Klass.stat_func()실제로는 내 질문의 주제가 아니 었습니다. 실제로 불일치 문제를 흐리게하지 않기 위해 전자를 사용하지 않았습니다.
martineau

1
실제로 DRY 자체의 문제는 아닙니다. self.__class__하위 클래스가 재정의 stat_func되면 하위 클래스 Subclass.method를 호출 하기 때문에 더 좋습니다 stat_func. 그러나 완전히 정직하려면 그 상황에서 정적 방법 대신 실제 방법을 사용하는 것이 훨씬 좋습니다.
asmeurer

@asmeurer : 클래스의 인스턴스가 아직 생성되지 않았기 때문에 실제 메서드를 사용할 수 없습니다. 클래스 자체가 아직 완전히 정의되지 않았기 때문에 불가능합니다.
martineau

상속 된 클래스에 대한 정적 메서드를 호출하는 방법을 원했습니다 (상 속됨, 부모는 실제로 함수를 가지고 있음).
whitey04

11

클래스 정의 후에 클래스 속성을 주입하는 것은 어떻습니까?

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    def method(self):
        ret = Klass.stat_func()
        return ret

Klass._ANS = Klass.stat_func()  # inject the class attribute with static method value

1
이것은 해결 방법에 대한 첫 번째 시도와 비슷하지만 클래스 내부에있는 것을 선호합니다 ... 부분적으로 관련 속성에 밑줄이 있고 비공개이기 때문에 부분적으로입니다.
martineau

11

이것은 staticmethod가 디스크립터이기 때문에 디스크립터 프로토콜을 실행하고 실제 호출 가능을 얻으려면 클래스 레벨 속성 페치가 필요합니다.

소스 코드에서 :

클래스 (예 :) C.f()또는 인스턴스 (예 :)에서 호출 할 수 있습니다 C().f(). 클래스를 제외하고 인스턴스는 무시됩니다.

그러나 클래스가 정의되는 동안 클래스 내부에서 직접하지는 않습니다.

그러나 한 논평자가 언급했듯이 이것은 실제로 "Pythonic"디자인이 아닙니다. 대신 모듈 레벨 기능을 사용하십시오.


내 질문에서 말했듯이 원래 코드가 작동하지 않는 이유를 이해합니다. 그것이 왜 비피 토닉으로 여겨지는지 설명 할 수 있습니까?
martineau

정적 메소드는 클래스 오브젝트 자체가 올바르게 작동해야합니다. 그러나 클래스 수준의 클래스 내에서 호출하면 클래스가 실제로 완전히 정의되지 않았습니다. 따라서 아직 참조 할 수 없습니다. 정적 메소드가 정의 된 후 클래스 외부에서 호출해도됩니다. 그러나 " Pythonic "은 실제로 미학처럼 잘 정의되어 있지 않습니다. 그 코드에 대한 첫 인상이 좋지 않다고 말할 수 있습니다.
Keith

어느 버전 (또는 둘 다)의 인상? 그리고 왜 구체적으로?
martineau

나는 당신의 궁극적 인 목표가 무엇인지 짐작할 수 있지만, 당신은 역동적이고 클래스 수준의 속성을 원한다고 생각합니다. 이것은 메타 클래스 작업처럼 보입니다. 그러나 다시 한 번 더 간단한 방법이나 기능을 그대로 유지하면서 디자인을 제거하고 단순화하는 다른 방법이있을 수 있습니다.
Keith

3
아니요, 내가하고 싶은 것은 실제로 매우 간단합니다. 일반적인 코드를 제외하고 재사용하여 클래스 생성 시간에 개인 클래스 속성을 만들고 나중에 하나 이상의 클래스 메서드 내에서 사용하는 것입니다. 이 공통 코드는 클래스 외부에서 사용되지 않으므로 당연히 코드의 일부가되기를 원합니다. 메타 클래스 (또는 클래스 데코레이터)를 사용하면 효과가 있지만 IMHO는 쉽게 할 수있는 일에 너무 과한 것처럼 보입니다.
martineau

8

이 솔루션은 어떻습니까? @staticmethod데코레이터 구현 에 대한 지식에 의존하지 않습니다 . 내부 클래스 StaticMethod는 정적 초기화 함수의 컨테이너로 재생됩니다.

class Klass(object):

    class StaticMethod:
        @staticmethod  # use as decorator
        def _stat_func():
            return 42

    _ANS = StaticMethod._stat_func()  # call the staticmethod

    def method(self):
        ret = self.StaticMethod._stat_func() + Klass._ANS
        return ret

2
독창성을 위해 +1이지만 더 이상 __func__공식적으로 문서화되어 있기 때문에 더 이상 걱정할 필요가 없습니다 ( Python 2.7의 새로운 기능의 다른 언어 변경 사항문제 5982 참조 ). 귀하의 솔루션은 2.6 이전의 Python 버전에서도 작동하기 때문에 훨씬 이식성이 뛰어납니다 ( __func__처음 동의어로 도입 되었을 때 im_func).
martineau

이것은 Python 2.6에서 작동하는 유일한 솔루션입니다.
benselme

@benselme : 파이썬 2.6이 설치되어 있지 않기 때문에 귀하의 주장을 확인할 수는 없지만 이것이 유일무이 한 것임을 의심합니다.
martineau
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.