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


242

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

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

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()

18
method_two () 정의 이외의 차이점은 유효하지 않으며 호출에 실패합니다.
anatoly techtonik

14
@ techtonik : method_two의 정의에 아무런 문제가 없습니다! 잘못되었거나 유효하지 않은 사양, 즉 추가 인수로 호출되었습니다.
0xc0de

1
귀하는 모두 클래스 메소드가 아닌 인스턴스 메소드입니다. 정의 에 적용 하여 클래스 메서드 를 만듭니다 @classmethod. 첫 번째 매개 변수는 호출 할 필요가 cls대신 self하고, 클래스 객체가 아닌 클래스의 인스턴스를 받게됩니다 Test.method_three()a_test.method_three()동일합니다.
Lutz Prechelt

self인수 없이 함수 정의를 작성하려는 이유는 무엇 입니까? 이것에 대한 강력한 사용 사례가 있습니까?
alpha_989

답변:


412

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

기본적으로 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

17
나는이 대답을지지한다. 그것은 나의 것보다 우수하다. 잘한 Torsten :)
자유 공간

24
파이썬 3에서는 언 바운드 메소드가 더 이상 사용되지 않습니다. 대신 기능이 있습니다.
boldnik

@boldnik, 왜 바인딩되지 않은 메소드가 더 이상 사용되지 않는다고 말합니까? 정적 메소드는 여전히 문서에 있습니다 : docs.python.org/3/library/functions.html#staticmethod
alpha_989

195

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

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>

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


12
staticmethod장식은 클래스 (...) 랩 이 문구가 포장되고있는 클래스로 오해의 소지가 조금 클래스이다 방법 foo이 아닌 된 클래스 foo정의된다.
Piotr Dobrogost

12

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

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

method_two = staticmethod(method_two)

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


4
"@staticmethod 함수 데코레이터 구문"을 의미합니다.
tzot

11
>>> 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

2
Class.instance_method() # The class cannot use an instance method사용할 수 있습니다. 인스턴스를 수동으로 전달하십시오.Class.instance_method(a)
warvariuc

@warwaruk 거기 TyeError있습니다. 아래 줄을보십시오.
kzh

네 나중에 봤어요 여전히, imo, '클래스 메소드를 사용할 수 없습니다'라고 말하는 것은 올바르지 않습니다. 단 한 줄 아래에했기 때문입니다.
warvariuc

@kzh, 설명해 주셔서 감사합니다. 를 호출하면이 ( 가)로 업데이트 된 a.class_method()것 같습니다 a.c. 따라서을 (를 1) 호출 Class.class_method()하면 Class.c변수가 (가)로 업데이트되었습니다 2. 그러나을 (를) 할당 할 때 ?로 업데이트되지 않은 a.c=5이유는 무엇입니까? Class.c5
alpha_989

@ alpha_989 python은 먼저 ovjects 자체 인스턴스에서 직접 속성을 찾고없는 경우 기본적으로 클래스에서 속성을 찾습니다. 이에 대한 다른 질문이 있으시면 언제든지 질문을 열고 여기에 연결하십시오. 추가 도움을 드리겠습니다.
kzh

4

멤버 함수를 정의하고 있지만 함수의 멤버를 알려주지 않기 때문에 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'여야합니다.


3

위의 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>

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

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


1

그것은 오류입니다.

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

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)

1

두 번째는 작동하지 않습니다. 파이썬이 내부적으로 a_test 인스턴스를 첫 번째 인수로 호출하려고 시도하지만 method_two는 인수를 허용하지 않으므로 작동하지 않기 때문에 작동하지 않습니다. 오류. 정적 메소드와 동등한 것을 원하면 클래스 메소드를 사용할 수 있습니다. Java 또는 C #과 같은 언어의 정적 메소드보다 Python의 클래스 메소드에 대한 필요성이 훨씬 적습니다. 가장 좋은 해결책은 클래스 정의 외부에서 클래스 메소드보다 더 효율적으로 작동하는 모듈의 메소드를 사용하는 것입니다.


함수를 정의하면 Class Test(object): @staticmethod def method_two(): print(“called method_two”) 하나의 유스 케이스는 함수가 클래스의 일부가되기를 원하지만 사용자가 함수에 직접 액세스하기를 원하지 않는 경우입니다. 따라서 인스턴스 method_two내부의 다른 함수에 의해 호출 될 수 Test있지만를 사용하여 호출 할 수는 없습니다 a_test.method_two(). 을 사용 def method_two()하는 경우이 사용 사례에 적용됩니까? 아니면 함수 정의를 수정하는 더 좋은 방법이 있습니까? 그래서 위의 사용 사례에서 의도 한대로 작동합니까?
alpha_989

1

method_two를 호출하면 Python 런타임이 자동으로 전달하는 자체 매개 변수를 수락하지 않는 예외가 발생합니다.

Python 클래스에서 정적 메소드를 작성하려면로 메소드를 장식하십시오 staticmethod decorator.

Class Test(Object):
  @staticmethod
  def method_two():
    print "Called method_two"

Test.method_two()


0

의 정의 method_two가 유효하지 않습니다. 에 전화 하면 통역사로부터 연락을 method_two받습니다 TypeError: method_two() takes 0 positional arguments but 1 was given.

인스턴스 메소드는 호출 할 때 바운드 함수 a_test.method_two()입니다. self인스턴스 Test를 가리키는를 첫 번째 매개 변수로 자동 수락 합니다. 관통 self매개 변수, 인스턴스 메소드는 속성에 액세스 자유롭게 할 수와 같은 객체를 수정합니다.


0

언 바운드 방법

언 바운드 메소드는 아직 특정 클래스 인스턴스에 바인드되지 않은 메소드입니다.

바운드 방법

바운드 메서드는 클래스의 특정 인스턴스에 바인딩 된 메서드입니다.

여기 에 문서화 된 것처럼 self는 함수가 바인드되거나 바인드되지 않거나 정적 인 상태에 따라 다른 것을 참조 할 수 있습니다.

다음 예를 살펴보십시오.

class MyClass:    
    def some_method(self):
        return self  # For the sake of the example

>>> MyClass().some_method()
<__main__.MyClass object at 0x10e8e43a0># This can also be written as:>>> obj = MyClass()

>>> obj.some_method()
<__main__.MyClass object at 0x10ea12bb0>

# Bound method call:
>>> obj.some_method(10)
TypeError: some_method() takes 1 positional argument but 2 were given

# WHY IT DIDN'T WORK?
# obj.some_method(10) bound call translated as
# MyClass.some_method(obj, 10) unbound method and it takes 2 
# arguments now instead of 1 

# ----- USING THE UNBOUND METHOD ------
>>> MyClass.some_method(10)
10

obj마지막 호출 에서 클래스 인스턴스를 사용하지 않았기 때문에 정적 메서드처럼 보일 수 있습니다.

그렇다면 데코레이터로 MyClass.some_method(10)장식 된 정적 함수에 대한 호출과 호출 의 차이점은 무엇 @staticmethod입니까?

데코레이터를 사용하면 먼저 인스턴스를 만들지 않고 메서드가 사용됨을 분명히 알 수 있습니다. 일반적으로 클래스 멤버 메소드가 인스턴스없이 사용될 것으로 예상하지 않고 액세스하면 메소드 구조에 따라 가능한 오류가 발생할 수 있습니다.

또한 @staticmethod데코레이터 를 추가 하면 객체를 통해 도달 할 수 있습니다.

class MyClass:    
    def some_method(self):
        return self    

    @staticmethod
    def some_static_method(number):
        return number

>>> MyClass.some_static_method(10)   # without an instance
10
>>> MyClass().some_static_method(10)   # Calling through an instance
10

인스턴스 메소드로는 위의 예제를 수행 할 수 없습니다. 당신은 첫 번째 살아남을 수있다 (우리가 전에했던 것처럼)하지만, 두 번째는 언 바운드 호출로 변환됩니다 MyClass.some_method(obj, 10)을 올릴 TypeError인스턴스 방법은 하나 개의 인자를 가지고 당신이 실수로 두 가지를 전달하려고하기 때문이다.

"나는 인스턴스와 클래스 모두를 통해 정적 메서드를 호출 할 수 있다면, 당신은 말할 수 MyClass.some_static_methodMyClass().some_static_method같은 방법이어야한다." 예!


0

바운드 방법 = 인스턴스 방법

언 바운드 방법 = 정적 방법.


다른 사람들이 배울 수 있도록 답변에 추가 설명을 추가하십시오. 예를 들어,이 질문에 대한 다른 답변에서보세요이
니코 하세
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.