[python] 파이썬의 클래스 메소드 차이점 : 바운드, 언 바운드 및 정적

다음 클래스 메소드의 차이점은 무엇입니까?

하나는 정적이고 다른 하나는 그렇지 않습니까?

class Test(object):
  def method_one(self):
    print "Called method_one"

  def method_two():
    print "Called method_two"

a_test = Test()
a_test.method_one()
a_test.method_two()



답변

파이썬에서, 구별이 바운드언 바운드 방법.

기본적으로 method_one바운드 함수 와 같은 멤버 함수에 대한 호출

a_test.method_one()

로 번역

Test.method_one(a_test)

언 바운드 메소드에 대한 호출. 이 때문에 버전에 대한 호출 method_two이 실패합니다.TypeError

>>> a_test = Test()
>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given) 

데코레이터를 사용하여 메소드의 동작을 변경할 수 있습니다

class Test(object):
    def method_one(self):
        print "Called method_one"

    @staticmethod
    def method_two():
        print "Called method two"

데코레이터는 내장 된 기본 메타 클래스 type(클래스의 클래스, 이 질문 참조 )에게에 대한 바인딩 된 메소드를 작성하지 않도록 지시합니다 method_two.

이제 인스턴스 나 클래스에서 직접 정적 메소드를 호출 할 수 있습니다.

>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two


답변

파이썬의 메소드는 디스크립터 시스템의 기본을 이해하면 매우 간단합니다. 다음 수업을 상상해보십시오.

class C(object):
    def foo(self):
        pass

이제 쉘에서 해당 클래스를 살펴 보자.

>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>

foo클래스 의 속성에 액세스하면 바인딩되지 않은 메소드를 다시 얻을 수 있지만 클래스 스토리지 (dict)에는 함수가 있습니다. 왜 그래? 그 이유는 클래스의 클래스가 __getattribute__설명자를 해결하는를 구현하기 때문입니다. 복잡하게 들리지만 그렇지 않습니다. C.foo이 특별한 경우 에이 코드와 대략 동일합니다.

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>

함수에는 __get__설명자를 만드는 메소드 가 있기 때문 입니다. 클래스의 인스턴스가 있다면 거의 동일 None합니다. 클래스 인스턴스입니다.

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x17bd4d0>>

왜 파이썬이 그렇게합니까? 메소드 객체는 함수의 첫 번째 매개 변수를 클래스의 인스턴스에 바인딩하기 때문입니다. 그곳에서 자아가 시작됩니다. 때로는 클래스가 함수를 메소드로 만드는 것을 원하지 않을 때가 있습니다 staticmethod.

 class C(object):
  @staticmethod
  def foo():
   pass

staticmethod장식은 클래스와 구현 더미 래핑 __get__하는 방법으로 함수로 래핑 된 기능을 반환하지 :

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

그것을 설명하기를 바랍니다.


답변

클래스 멤버를 호출하면 Python은 자동으로 객체에 대한 참조를 첫 번째 매개 변수로 사용합니다. 변수는 self실제로 아무것도 의미하지 않으며 단지 코딩 규칙 일뿐입니다. 원한다면 전화 할 gargaloo수 있습니다. 즉, 통화가, 상기 method_two인상 할 TypeError파이썬 자동 매개 변수를 갖지 않는 것으로 정의 된 방법과 파라미터 (부모 객체에 대한 참조)를 통과하려고하기 때문에.

실제로 작동하게하려면 이것을 클래스 정의에 추가하면됩니다.

method_two = staticmethod(method_two)

또는 @staticmethod 함수 데코레이터를 사용할 수 있습니다 .


답변

>>> class Class(object):
...     def __init__(self):
...         self.i = 0
...     def instance_method(self):
...         self.i += 1
...         print self.i
...     c = 0
...     @classmethod
...     def class_method(cls):
...         cls.c += 1
...         print cls.c
...     @staticmethod
...     def static_method(s):
...         s += 1
...         print s
...
>>> a = Class()
>>> a.class_method()
1
>>> Class.class_method()    # The class shares this value across instances
2
>>> a.instance_method()
1
>>> Class.instance_method() # The class cannot use an instance method
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method instance_method() must be called with Class instance as first argument (got nothing instead)
>>> Class.instance_method(a)
2
>>> b = 0
>>> a.static_method(b)
1
>>> a.static_method(a.c) # Static method does not have direct access to 
>>>                      # class or instance properties.
3
>>> Class.c        # a.c above was passed by value and not by reference.
2
>>> a.c
2
>>> a.c = 5        # The connection between the instance
>>> Class.c        # and its class is weak as seen here.
2
>>> Class.class_method()
3
>>> a.c
5


답변

멤버 함수를 정의하고 있지만 함수의 멤버를 알려주지 않기 때문에 method_two는 작동하지 않습니다. 마지막 줄을 실행하면 다음을 얻을 수 있습니다.

>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)

클래스의 멤버 함수를 정의하는 경우 첫 번째 인수는 항상 ‘self’여야합니다.


답변

위의 Armin Ronacher의 정확한 설명, 저의 초보자가 잘 이해할 수 있도록 답변을 확장하십시오.

정적 또는 인스턴스 메소드 (여기서 다른 유형-클래스 메소드-여기에서 논의되지 않았으므로 건너 뛰는가) 여부에 관계없이 클래스에 정의 된 메소드의 차이점은 클래스 인스턴스에 바인딩되어 있는지 여부에 있습니다. 예를 들어, 메소드가 런타임 중에 클래스 인스턴스에 대한 참조를 수신하는지 여부를 말하십시오.

class C:
    a = []
    def foo(self):
        pass

C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance

__dict__클래스 객체 의 사전 속성은 클래스 객체의 모든 속성과 메서드에 대한 참조를 보유하므로

>>> C.__dict__['foo']
<function foo at 0x17d05b0>

foo 메소드는 위와 같이 액세스 할 수 있습니다. 여기서 주목해야 할 중요한 점은 파이썬의 모든 것이 객체이므로 위의 사전의 참조는 다른 객체를 가리키는 것입니다. 클래스 속성 객체 또는 CPO라고 부르겠습니다.

CPO가 설명자인 경우 python 인터프리터 __get__()는 CPO 의 메소드를 호출하여 포함 된 값에 액세스합니다.

CPO가 설명자인지 확인하기 위해 Python 인터프리터는 설명자 프로토콜을 구현하는지 확인합니다. 디스크립터 프로토콜을 구현하는 것은 3 가지 방법을 구현하는 것입니다

def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)

예를 들어

>>> C.__dict__['foo'].__get__(c, C)

어디

  • self CPO (목록, str, 함수 등의 인스턴스 일 수 있음)이며 런타임에 의해 제공됩니다.
  • instance 이 CPO가 정의 된 클래스의 인스턴스 (위의 ‘c’개체)는 명시 적으로 제공해야합니다.
  • owner이 CPO가 정의 된 클래스 (위의 클래스 개체 ‘C’)이며 당사에서 제공해야합니다. 그러나 이것은 CPO에서 호출하기 때문입니다. 인스턴스에서 호출 할 때 런타임이 인스턴스 또는 클래스를 제공 할 수 있기 때문에 이것을 제공 할 필요가 없습니다 (다형성)
  • value CPO의 의도 된 가치이며 당사가 제공해야합니다.

모든 CPO가 설명자인 것은 아닙니다. 예를 들어

>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'

리스트 클래스가 디스크립터 프로토콜을 구현하지 않기 때문입니다.

따라서 c.foo(self)메소드 서명이 실제로 이것이기 때문에 인수 자체 입력 이 필요합니다 C.__dict__['foo'].__get__(c, C)(위에서 설명했듯이 C는 발견되거나 다형성 될 수 있으므로 필요하지 않습니다). 또한 필요한 인스턴스 인수를 전달하지 않으면 TypeError가 발생합니다.

메소드가 여전히 클래스 C 클래스를 통해 참조되고 클래스 인스턴스와의 바인딩은 인스턴스 오브젝트 형식의 컨텍스트를이 함수에 전달하여 수행됩니다.

컨텍스트를 유지하지 않거나 인스턴스에 바인딩하지 않기로 선택한 경우 설명자 CPO를 래핑하고 __get__()컨텍스트가 필요하지 않도록 해당 메서드를 재정의하는 클래스를 작성하기 만하면되므로 매우 좋습니다. 이 새로운 클래스는 데코레이터라고하며 키워드를 통해 적용됩니다.@staticmethod

class C(object):
  @staticmethod
  def foo():
   pass

새로 줄 바꿈 된 CPO에 컨텍스트가 없으면 foo오류가 발생하지 않으며 다음과 같이 확인할 수 있습니다.

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

정적 메소드의 유스 케이스는 네임 스페이스 및 코드 유지 관리 가능성이 높습니다 (클래스에서 가져 와서 모듈 전체에서 사용할 수 있도록하는 것).

물론 액세스 인스턴스 변수, 클래스 변수 등의 메서드를 정교화해야하는 경우가 아니라면 가능할 때마다 인스턴스 메서드 대신 정적 메서드를 작성하는 것이 좋습니다. 한 가지 이유는 객체에 대한 원치 않는 참조를 유지하지 않음으로써 가비지 수집을 용이하게하는 것입니다.


답변

그것은 오류입니다.

우선, 첫 번째 줄은 다음과 같아야합니다 (자본에주의하십시오)

class Test(object):

클래스의 메소드를 호출 할 때마다 첫 번째 인수 (따라서 이름 self)로 메소드를 가져오고 method_two는이 오류를 발생시킵니다

>>> a.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)