"개체"클래스의 인스턴스에 속성을 설정할 수 없습니다.


87

그래서 저는 이 질문에 답하는 동안 파이썬을 가지고 놀았는데 이것이 유효하지 않다는 것을 발견했습니다.

o = object()
o.attr = 'hello'

때문에 AttributeError: 'object' object has no attribute 'attr'. 그러나 객체에서 상속 된 모든 클래스는 유효합니다.

class Sub(object):
    pass

s = Sub()
s.attr = 'hello'

인쇄시 s.attr예상대로 'hello'가 표시됩니다. 왜 그렇습니까? 파이썬 언어 사양에서 바닐라 객체에 속성을 할당 할 수 없다고 지정하는 것은 무엇입니까?


순수한 추측 : object유형이 변경 불가능하고 새 속성을 추가 할 수 없습니까? 이것은 가장 말이되는 것 같습니다.
Chris Lutz

2
@ S.Lott :이 질문의 첫 번째 줄을보십시오. 순전히 호기심.
Smashery

3
제목이 잘못되었습니다. object클래스가 아닌 클래스 인스턴스에 속성을 설정하려고합니다 object.
dhill

답변:


129

임의의 속성 할당을 지원하려면 __dict__객체에 임의의 속성을 저장할 수있는 객체와 관련된 dict가 필요합니다 . 그렇지 않으면 새 속성 을 넣을 곳이 없습니다 .

의 인스턴스는 object않습니다 하지 주위 나른다 __dict__- 그것은 한 경우, 끔찍한 순환 의존 문제 (전 이후 dict, 대부분의 다른 모든 같은에서 상속 object,이 안장 것이다 ;-) 모든 오버 헤드를 의미하는하는 DICT 파이썬의 객체를 의 많은 현재 가지고 또는 딕셔너리를 필요로하지 않는 개체 당 바이트 (기본적으로,이없는 모든 개체를 임의로 할당 속성이 있거나 딕셔너리 필요하지 않습니다).

예를 들어, 훌륭한 pympler프로젝트 ( 여기 에서 svn을 통해 얻을 수 있음)를 사용하여 몇 가지 측정을 수행 할 수 있습니다 ... :

>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16

모든 int것이 16 바이트 대신 144 바이트를 차지하는 것을 원하지 않습니까?-)

이제 클래스를 만들면 (무엇이든 상 속됨) 상황이 변경됩니다 ... :

>>> class dint(int): pass
... 
>>> asizeof.asizeof(dint(23))
184

...가 __dict__ 되어 지금 추가 (플러스, 조금 더 오버 헤드) - 소위 dint인스턴스가 임의의 속성을 가질 수있다, 그러나 당신은 그 유연성을 꽤 공간 비용을 지불해야합니다.

그래서 당신이 하나의 추가 속성으로 ints 를 원한다면 어떻게 될까요? 드물게 필요하지만 Python은 목적을위한 특별한 메커니즘을 제공합니다.foobar

>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
... 
>>> asizeof.asizeof(fint(23))
80

...하지 같은 작은으로 int당신을 마음! (또는 두 개의 int, 하나 self와 하나는 self.foobar다시 할당 될 수 있음),하지만 확실히 dint.

클래스에 __slots__특수 속성 (문자열 시퀀스) 이 있으면 class문 (더 정확하게는 기본 메타 클래스 type)이 해당 클래스의 모든 인스턴스에 유한 한 속성 (즉 임의의 속성을 가질 수있는 기능 )을 제공하지 않습니다.__dict__ , 주어진 이름을 가진 엄격한 "슬롯"세트 (기본적으로 각각 특정 객체에 대한 하나의 참조를 보유 할 수있는 장소).

유연성을 잃어버린 대가로 인스턴스 당 많은 바이트를 얻습니다 (아마도 수많은 인스턴스가 질주하는 경우에만 의미가 있지만 그에 대한 사용 사례 있습니다).


5
이것은 메커니즘이 구현되는 방법을 설명하지만 이러한 방식으로 구현되는 이유는 설명하지 않습니다. 오버 헤드의 단점은 없지만 약간의 단순함을 더할 수있는 즉석에서 dict 추가를 구현하는 적어도 두세 가지 방법을 생각할 수 있습니다 .
Георги Кременлиев apr. 2014-04-03

비어 있지 않은 그 주 __slots__와 같은 가변 길이 유형 작동하지 않습니다 str, tuple그리고 파이썬 3를 int.
arekolek


그것은 좋은 설명하지만 여전히 왜 (또는 방법) 응답하지 않습니다 Sub__dict__속성과 객체는 존재하지 않습니다 Sub에서 상속 object, 어떻게 그 속성 (와 같은 다른 사람이되어 __module__) 상속에 추가? 이 될 수있다 새로운 질문이 될 수
로드리고 E. 프린

2
객체 __dict__는 처음 필요할 때만 생성되므로 메모리 비용 상황은 asizeof출력이 보이는 것처럼 간단하지 않습니다 . ( 구체화 asizeof를 피하는 방법을 모릅니다 __dict__.) 이 예제 에서 필요할 때까지 dict가 구체화되지 않는 것을 볼 수 있으며 여기 에서 __dict__구체화를 담당하는 코드 경로 중 하나를 볼 수 있습니다 .
user2357112 모니카 지원

17

다른 답변자들이 말했듯 object이에는 __dict__. 또는을 포함한 모든 유형 object의 기본 클래스입니다 . 따라서 제공되는 것이 무엇이든 그들에게도 부담이 될 것입니다. 옵션 처럼 단순한 것조차도 각 값에 대한 추가 포인터가 필요합니다. 이것은 매우 제한된 유틸리티를 위해 시스템의 각 개체에 대해 추가 4-8 바이트의 메모리를 낭비하게됩니다.intstrobject __dict__


더미 클래스의 인스턴스를 수행하는 대신 Python 3.3 이상에서 types.SimpleNamespace이를 사용할 수 있습니다 .


4

단순히 최적화 때문입니다.

사전은 비교적 큽니다.

>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140

C로 정의 된 대부분의 (아마도 모든) 클래스에는 최적화를위한 사전이 없습니다.

소스 코드 를 보면 객체에 dict가 있는지 여부를 확인하는 많은 검사가 있음을 알 수 있습니다.


1

그래서 제 질문을 조사하면서 파이썬 언어에 대해 이것을 발견했습니다. int와 같은 것을 상속 할 수 있고 동일한 동작을 볼 수 있습니다.

>>> class MyInt(int):
       pass

>>> x = MyInt()
>>> print x
0
>>> x.hello = 4
>>> print x.hello
4
>>> x = x + 1
>>> print x
1
>>> print x.hello
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: 'int' object has no attribute 'hello'

마지막에 오류가 발생하는 이유는 add 함수가 int를 반환하기 때문이라고 생각하므로 __add__사용자 지정 속성을 유지하려면 같은 함수를 재정의해야 합니다. 하지만 이제는 "int"와 같은 "object"를 생각할 때이 모든 것이 이해가됩니다.


0

객체가 클래스가 아니라 "유형"이기 때문입니다. 일반적으로 C 확장으로 정의 된 모든 클래스 (모든 내장 데이터 유형 및 numpy 배열과 같은 것)는 임의의 속성 추가를 허용하지 않습니다.


그러나 object ()는 Sub ()가 객체 인 것처럼 객체입니다. 내 이해는 s와 o가 모두 객체라는 것입니다. 그렇다면 s와 o의 근본적인 차이점은 무엇입니까? 하나는 인스턴스화 된 유형이고 다른 하나는 인스턴스화 된 클래스입니까?
Smashery

빙고. 그것이 바로 문제입니다.
Ryan

1
Python 3에서는 유형과 클래스의 차이가 실제로 존재하지 않습니다. 따라서 "유형"과 "클래스"는 이제 상당히 동의어입니다. 그러나 __dict__Alex Martelli가 제공 한 이유로, 이없는 클래스에는 여전히 속성을 추가 할 수 없습니다 .
PM 2Ring


-2

이것은 (IMO) Python의 근본적인 제한 사항 중 하나입니다. 클래스를 다시 열 수 없습니다. 하지만 실제 문제는 C로 구현 된 클래스를 런타임에 수정할 수 없다는 사실에 기인한다고 생각합니다. 하위 클래스는 가능하지만 기본 클래스는 수정할 수 없습니다.

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