다음과 같은 의미있는 차이점이 있습니까?
class A(object):
foo = 5 # some default value
vs.
class B(object):
def __init__(self, foo=5):
self.foo = foo
많은 인스턴스를 작성하는 경우 두 스타일의 성능 또는 공간 요구 사항에 차이가 있습니까? 코드를 읽을 때 두 스타일의 의미가 크게 다르다고 생각하십니까?
다음과 같은 의미있는 차이점이 있습니까?
class A(object):
foo = 5 # some default value
vs.
class B(object):
def __init__(self, foo=5):
self.foo = foo
많은 인스턴스를 작성하는 경우 두 스타일의 성능 또는 공간 요구 사항에 차이가 있습니까? 코드를 읽을 때 두 스타일의 의미가 크게 다르다고 생각하십니까?
답변:
성능 고려 사항 외에도 의미 상 중요한 차이가 있습니다. 클래스 속성의 경우 하나의 객체 만 참조됩니다. 인스턴스 속성 세트에서 인스턴스에는 여러 객체가 참조 될 수 있습니다. 예를 들어
>>> 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
[]
int
하고 str
그들은 여전히 수업이 아닌 각 인스턴스에 붙어 있습니다.
int
그리고이 str
되어 도 정확히 같은 방식으로 공유했다. is
또는로 쉽게 확인할 수 있습니다 id
. 또는 각 인스턴스 __dict__
와 클래스를 살펴보십시오 __dict__
. 불변 유형이 공유되는지 여부는 대개 중요하지 않습니다.
a.foo = 5
두 경우 모두 b.foo
return 이 표시 []
됩니다. 첫 번째 경우 클래스 a.foo
이름을 같은 이름의 새 인스턴스 속성으로 덮어 쓰고 있기 때문입니다 .
차이점은 클래스의 속성이 모든 인스턴스에서 공유된다는 것입니다. 인스턴스의 속성은 해당 인스턴스에 고유합니다.
C ++에서 오는 경우 클래스의 속성은 정적 멤버 변수와 비슷합니다.
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
는 a.foo
참조 하는 값을 변경하는 것이 value 의 새 이름을 a.foo = 5
만드는 것 입니다. 따라서 클래스 속성을 숨기는 인스턴스 속성으로 끝납니다. Alex 버전 에서도 동일하게 시도하면 변경되지 않은 것을 알 수 있습니다 . a.foo
5
a.foo = 5
b.foo
다음은 매우 좋은 게시물 이며 아래에 요약되어 있습니다.
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
여기에 의견을 가진 사람들과 멍청이로 표시된 다른 두 가지 질문에 모두 동일한 방식으로 혼란스러워하는 것처럼 보이므로 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 = 5
a.foo
에 대한 이름을 만들고 5
있습니다. b.foo
, 또는 이전에 a.foo
사용 했던 이전 값의 다른 이름 에는 영향을 미치지 않습니다 . * 클래스 속성을 숨기는 인스턴스 속성을 만드는 것은 약간 까다 롭습니다. 여기서 일어나고 있습니다.
Alex가 왜 목록을 사용했는지 분명히 알 수있을 것입니다. 목록을 변경할 수 있다는 사실은 두 변수가 같은 목록이라는 이름을 표시하는 것이 더 쉽다는 것을 의미하며, 실제 코드에서 두 개의 목록이 있는지 여부를 아는 것이 더 중요하다는 것을 의미합니다 동일한 목록에 대한 두 개의 이름.
C ++과 같은 언어에서 온 사람들의 혼란은 파이썬에서 값이 변수에 저장되지 않는다는 것입니다. 값은 자체적으로 값 랜드에 존재하며 변수는 값의 이름 일 뿐이며 할당은 값의 새 이름을 만듭니다. 도움이된다면 각 Python 변수를 a 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 정의 와 훌륭한 게시물 에서 발견했습니다 .
구성하는 클래스의 사전에 속성이있는 경우 그런 다음 조회 된 속성이 데이터 디스크립터를 가리키는 지 확인하십시오 (클래스
__get__
와__set__
메소드를 모두 구현하는 클래스에 지나지 않습니다 ). 그렇다면__get__
, 데이터 디스크립터 의 메소드 (28-33 행) 를 호출하여 속성 조회를 해결하십시오 .