클래스 본문 내에서 클래스 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.