클래스 속성과 인스턴스 속성의 차이점은 무엇입니까?


132

다음과 같은 의미있는 차이점이 있습니까?

class A(object):
    foo = 5   # some default value

vs.

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

많은 인스턴스를 작성하는 경우 두 스타일의 성능 또는 공간 요구 사항에 차이가 있습니까? 코드를 읽을 때 두 스타일의 의미가 크게 다르다고 생각하십니까?


1
방금 비슷한 질문을하고 여기에 답했습니다 : stackoverflow.com/questions/206734/… 이 질문을 삭제해야합니까?
Dan Homerick

2
귀하의 질문이므로 언제든지 삭제하십시오. 그것이 당신의 것이기 때문에 왜 다른 사람의 의견을 물어보십시오?
S.Lott

답변:


146

성능 고려 사항 외에도 의미 상 중요한 차이가 있습니다. 클래스 속성의 경우 하나의 객체 만 참조됩니다. 인스턴스 속성 세트에서 인스턴스에는 여러 객체가 참조 될 수 있습니다. 예를 들어

>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
...  def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo    
[]

4
변경 가능한 유형 만 공유됩니다. 좋아 int하고 str그들은 여전히 ​​수업이 아닌 각 인스턴스에 붙어 있습니다.
Babu

11
@Babu : 아니, int그리고이 str되어 정확히 같은 방식으로 공유했다. is또는로 쉽게 확인할 수 있습니다 id. 또는 각 인스턴스 __dict__와 클래스를 살펴보십시오 __dict__. 불변 유형이 공유되는지 여부는 대개 중요하지 않습니다.
abarnert

17
그러나 a.foo = 5두 경우 모두 b.fooreturn 이 표시 []됩니다. 첫 번째 경우 클래스 a.foo이름을 같은 이름의 새 인스턴스 속성으로 덮어 쓰고 있기 때문입니다 .
Konstantin Schubert

39

차이점은 클래스의 속성이 모든 인스턴스에서 공유된다는 것입니다. 인스턴스의 속성은 해당 인스턴스에 고유합니다.

C ++에서 오는 경우 클래스의 속성은 정적 멤버 변수와 비슷합니다.


1
공유 가능한 가변 유형만이 아닌가? 허용 된 답변은 작동하는 목록을 보여 주지만 int 인 경우 인스턴스 attr과 동일한 것 같습니다. >>> class A(object): foo = 5 >>> a, b = A(), A() >>> a.foo = 10 >>> b.foo 5
Rafe

6
@Rafe : 아니요. 모든 유형이 공유됩니다. 혼란스러워하는 이유 a.foo.append(5)a.foo참조 하는 값을 변경하는 것이 value 의 새 이름을 a.foo = 5만드는 것 입니다. 따라서 클래스 속성을 숨기는 인스턴스 속성으로 끝납니다. Alex 버전 에서도 동일하게 시도하면 변경되지 않은 것을 알 수 있습니다 . a.foo5a.foo = 5b.foo
abarnert

30

다음은 매우 좋은 게시물 이며 아래에 요약되어 있습니다.

class Bar(object):
    ## No need for dot syntax
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)

## Finds i_var in foo's instance namespace
foo.i_var
## 2

## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1

그리고 시각적 인 형태로

여기에 이미지 설명을 입력하십시오

클래스 속성 할당

  • 클래스에 액세스하여 클래스 속성을 설정하면 모든 인스턴스 의 값이 무시됩니다.

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
  • 클래스 변수는 인스턴스에 액세스하여 설정되어있는 경우,이 값을 무시합니다 해당 인스턴스 . 이것은 본질적으로 클래스 변수를 무시하고 해당 인스턴스에 대해서만 직관적으로 사용 가능한 인스턴스 변수로 바꿉니다 .

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1

언제 클래스 속성을 사용 하시겠습니까?

  • 상수 저장 . 클래스 속성은 클래스 자체의 속성으로 액세스 할 수 있으므로 클래스 전체의 클래스 별 상수를 저장하는 데 사용하는 것이 좋습니다.

    class Circle(object):
         pi = 3.14159
    
         def __init__(self, radius):
              self.radius = radius   
        def area(self):
             return Circle.pi * self.radius * self.radius
    
    Circle.pi
    ## 3.14159
    c = Circle(10)
    c.pi
    ## 3.14159
    c.area()
    ## 314.159
  • 기본값 정의 . 간단한 예를 들어, 제한된 목록 (예 : 특정 개수 이하의 요소 만 포함 할 수있는 목록)을 만들고 기본 한도를 10 개로 설정하도록 선택할 수 있습니다.

    class MyClass(object):
        limit = 10
    
        def __init__(self):
            self.data = []
        def item(self, i):
            return self.data[i]
    
        def add(self, e):
            if len(self.data) >= self.limit:
                raise Exception("Too many elements")
            self.data.append(e)
    
     MyClass.limit
     ## 10

19

여기에 의견을 가진 사람들과 멍청이로 표시된 다른 두 가지 질문에 모두 동일한 방식으로 혼란스러워하는 것처럼 보이므로 Alex Coventry 's 에 추가 답변을 추가 할 가치가 있다고 생각합니다 .

Alex가 목록과 같이 변경 가능한 유형의 값을 할당한다는 사실은 공유 여부와는 관련이 없습니다. id함수 나 is연산자로 이것을 볼 수 있습니다 :

>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
...     def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False

(왜 내가 object()대신에 사용했는지 궁금하다면 5여기에 들어가고 싶지 않은 두 가지 다른 문제가 발생하지 않도록하는 것입니다. 두 가지 이유로 인해 완전히 개별적으로 생성 된 5것은 숫자의 동일한 인스턴스 5이지만 완전히 개별적으로 작성된 object()것은 불가능합니다.)


그럼, 왜이다 a.foo.append(5)알렉스의 예에 영향을 b.foo하지만, a.foo = 5내 예제에서하지 않는 이유는 무엇입니까? 음, 시도 a.foo = 5알렉스의 예에서, 그것은 영향을주지 않습니다 통지 b.foo .

a.foo = 5a.foo에 대한 이름을 만들고 5있습니다. b.foo, 또는 이전에 a.foo사용 했던 이전 값의 다른 이름 에는 영향을 미치지 않습니다 . * 클래스 속성을 숨기는 인스턴스 속성을 만드는 것은 약간 까다 롭습니다. 여기서 일어나고 있습니다.


Alex가 왜 목록을 사용했는지 분명히 알 수있을 것입니다. 목록을 변경할 수 있다는 사실은 두 변수가 같은 목록이라는 이름을 표시하는 것이 더 쉽다는 것을 의미하며, 실제 코드에서 두 개의 목록이 있는지 여부를 아는 것이 더 중요하다는 것을 의미합니다 동일한 목록에 대한 두 개의 이름.


C ++과 같은 언어에서 온 사람들의 혼란은 파이썬에서 값이 변수에 저장되지 않는다는 것입니다. 값은 자체적으로 값 랜드에 존재하며 변수는 값의 이름 일 뿐이며 할당은 값의 새 이름을 만듭니다. 도움이된다면 각 Python 변수를 a shared_ptr<T>대신 로 생각 하십시오 T.

** 어떤 사람들은 인스턴스 속성을 설정하거나 설정하지 않을 수있는 인스턴스 속성에 대해 "기본 값"으로 클래스 속성을 사용하여이를 활용합니다. 경우에 따라 유용 할 수 있지만 혼동 될 수 있으므로주의하십시오.


0

상황이 하나 더 있습니다.

클래스 및 인스턴스 속성은 Descriptor 입니다.

# -*- encoding: utf-8 -*-


class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        return self.val


class Base(object):
    attr_1 = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.attr_2 = RevealAccess(10, 'var "x"')


def main():
    b = Base()
    print("Access to class attribute, return: ", Base.attr_1)
    print("Access to instance attribute, return: ", b.attr_2)

if __name__ == '__main__':
    main()

위의 결과는 다음과 같습니다.

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

클래스 또는 인스턴스를 통한 동일한 유형의 인스턴스 액세스는 다른 결과를 반환합니다!

그리고 나는 c.PyObject_GenericGetAttr 정의 와 훌륭한 게시물 에서 발견했습니다 .

설명

구성하는 클래스의 사전에 속성이있는 경우 그런 다음 조회 된 속성이 데이터 디스크립터를 가리키는 지 확인하십시오 (클래스 __get____set__메소드를 모두 구현하는 클래스에 지나지 않습니다 ). 그렇다면 __get__, 데이터 디스크립터 의 메소드 (28-33 행) 를 호출하여 속성 조회를 해결하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.