code

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

starcafe 2023. 7. 17. 21:17
반응형

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

다음 항목 사이에 의미 있는 차이가 있습니까?

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

대.

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

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

성능을 고려할 때 다음과 같은 의미론적 차이가 있습니다.

  • 속성이 인스턴스에 정의된 경우(일반적으로 수행하는 작업) 여러 개체가 참조될 수 있습니다. 속성은 완전히 별개의 버전을 가집니다.
  • 속성이 클래스에 정의될 때 참조되는 기본 개체는 하나뿐이므로 해당 클래스의 다른 인스턴스에 대한 작업이 둘 다 속성 설정/(set/install/insert/ 등)을 시도하면 다음과 같습니다.
    • 속성이 기본 제공 유형(예: int, float, boolean, string)인 경우 한 개체에 대한 작업이 값을 덮어씁니다.
    • 속성이 목록이나 딕트와 같은 변형 가능한 유형인 경우 원치 않는 누출이 발생합니다.

예:

>>> 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    
[]

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

C++에서 가져온 경우 클래스의 속성은 정적 멤버 변수에 더 가깝습니다.

여기 아주 좋은 게시물이 있는데, 아래와 같이 요약해 주세요.

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 = Bar(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

그리고 시각적인 형태로.

enter image description here

클래스 속성 할당

  • 클래스에 액세스하여 클래스 특성을 설정하면 모든 인스턴스에 대한 값이 재정의됩니다.

      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
    

여기 댓글과 더프로 표시된 다른 두 질문의 사람들은 모두 같은 방식으로 이에 대해 혼동하고 있는 것으로 보이기 때문에, 알렉스 코번트리의 답변 위에 추가적인 답변을 추가할 가치가 있다고 생각합니다.

알렉스가 목록과 같은 가변형의 값을 할당하고 있다는 사실은 공유되는 것과 관련이 없습니다.우리는 을 우는이볼수있다니습으로 볼 수 .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여기에 않은 다른 두 에 부딪히는 것을 입니다; 두로, 로 만들어진 그것내가여기여고지싶은않전히완른다하두지가다다니습제졌는어만것들딪히에니로별도을것입부하피위두완전히로다유이가른지한기은문에관▁that▁which▁for,다▁whole▁avoid▁different니습▁separ졌;▁issues▁twos▁to'▁running어들만▁reasons▁entirely로▁other▁into▁two도별그▁hereately-▁into전created▁i것히완▁get▁don,두▁wantt▁to다5검색은 숫자의 동일한 인스턴스가 될 수 있습니다.5하지만 완전히 따로따로 만들어진object()스캔.)


그럼, 왜 그런 거지?a.foo.append(5)에서는 영향을 줍니다.b.foo,그렇지만a.foo = 5제 예에서는 그렇지 않나요?음, 해보세요.a.foo = 5알렉스의 예에서, 그리고 그것이 영향을 미치지 않는다는 것을 알아차립니다.b.foo거기도 마찬가지야.

a.foo = 5▁making.a.foo5그것은 영향을 미치지 않습니다.b.foo인 는또이값다이름른대한에전▁for▁or▁any.a.foo*▁ 인스턴스 을 만드는 까다롭긴 **는 일단 알게 되면 일이 .클래스 속성을 숨기는 인스턴스 속성을 만드는 것은 조금 까다롭지만 **을(를) 얻으면 여기서 복잡한 작업이 수행되지 않습니다.


이제 알렉스가 목록을 사용한 이유가 분명해졌으면 합니다. 목록을 변형할 수 있다는 사실은 두 변수가 동일한 목록의 이름을 지정한다는 것을 보여주는 것이 더 쉽다는 것을 의미합니다. 또한 실제 코드에서 두 개의 목록을 가지고 있는지 아니면 두 개의 목록에 대한 이름을 가지고 있는지 아는 것이 더 중요하다는 것을 의미합니다.


C++와 같은 언어에서 온 사람들에게 혼란스러운 점은 파이썬에서 값이 변수에 저장되지 않는다는 것입니다.값은 가치 랜드에 존재합니다. 변수는 값의 이름일 뿐이고 할당은 값의 새 이름을 만들 뿐입니다.도움이 된다면 각 Python 변수를 다음과 같이 생각합니다.shared_ptr<T>신에대 T.

일부 사용자는 클래스 속성을 인스턴스가 설정할 수 있거나 설정하지 않을 수 있는 인스턴스 속성의 "기본값"으로 사용하여 이러한 이점을 활용합니다.이것은 경우에 따라 유용할 수도 있지만 혼란스러울 수도 있으므로 주의해야 합니다.

한 가지 상황이 더 있습니다.

클래스 및 인스턴스 특성이 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 definition(PyObject_GenericGetAttr 정의) 및 훌륭한 게시물입니다.

설명하라.

만약 속성이 객체 MRO를 구성하는 클래스의 사전에서 발견된다면, 조회되는 속성이 데이터 기술자를 가리키는지 확인합니다. (이것은 둘 다를 구현하는 클래스에 지나지 않습니다.)__get__그리고__set__방법)을 선택합니다.해당되는 경우 다음을 호출하여 특성 조회를 해결합니다.__get__데이터 기술자의 방법(라인 28–33).

언급URL : https://stackoverflow.com/questions/207000/what-is-the-difference-between-class-and-instance-attributes

반응형