인스턴스간에 클래스 데이터를 공유하지 않으려면 어떻게해야합니까?


145

내가 원하는 것은이 동작입니다.

class a:
    list = []

x = a()
y = a()

x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)

print(x.list) # prints [1, 3]
print(y.list) # prints [2, 4]

물론 인쇄 할 때 실제로 발생하는 일은 다음과 같습니다.

print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]

분명히 그들은 수업 시간에 데이터를 공유하고 있습니다 a. 원하는 동작을 수행하기 위해 별도의 인스턴스를 얻으려면 어떻게해야합니까?


14
list속성 이름으로 사용하지 마십시오 . list새 목록을 구성하는 buil-in 함수입니다. 대문자로 이름 클래스를 작성해야합니다.
Marc Tudurí

답변:


147

당신은 이것을 원합니다 :

class a:
    def __init__(self):
        self.list = []

클래스 선언 내부의 변수를 선언하면 인스턴스 멤버가 아닌 "클래스"멤버가됩니다. __init__메서드 내에서 선언 하면 멤버의 새 인스턴스가 객체의 모든 새 인스턴스와 함께 만들어 지므로 원하는 동작입니다.


5
추가 설명 : 인스턴스 중 하나에서 목록 속성을 다시 할당하면 다른 속성에는 영향을 미치지 않습니다. 따라서와 같은 작업을 수행 한 경우 x.list = []이를 변경하고 다른 사람에게 영향을 미치지 않을 수 있습니다. 당신이 직면 문제가 있다는 것입니다 x.list그리고 y.list당신은 하나 APPEND 호출 할 때, 그것은 다른 영향을 미치는, 동일한 목록입니다.
매트 Moriarity

그러나 왜 이것이 목록에 대해서만 발생합니까? init 외부에서 정수 또는 문자열을 선언했을 때 객체간에 공유되지 않았습니까? 누구든지이 개념에 대한 문서 링크를 공유 할 수 있습니까?
Amal Ts

1
@AmalTs 파이썬에서 할당이 어떻게 작동하는지 이해하지 못하는 것 같습니다. 이 비디오 또는 이 SO 게시물을 참조하십시오 . 표시되는 동작은 목록을 변경하지만 참조와 int 및 문자열을 리 바인드하기 때문에 발생합니다.
Андрей Беньковский

4
@AmalTs 참고 : 클래스 속성을 인스턴스 속성의 "게으른"기본값으로 사용하는 것은 나쁜 습관으로 간주됩니다. 속성이 불변 유형 인 경우에도 속성을 내부에 할당하는 것이 좋습니다 __init__.
Андрей Беньковский

21

받아 들여진 대답은 효과가 있지만 조금 더 설명해도 아프지 않습니다.

인스턴스 생성시 클래스 속성은 인스턴스 속성이되지 않습니다. 값이 할당되면 인스턴스 속성이됩니다.

원래 코드에서는 list인스턴스화 후 속성에 값이 할당되지 않습니다 . 그래서 클래스 속성으로 남아 있습니다. 인스턴스화 후 호출 __init__되므로 작업 내부 목록 정의 __init__또는이 코드는 원하는 출력을 생성합니다.

>>> class a:
    list = []

>>> y = a()
>>> x = a()
>>> x.list = []
>>> y.list = []
>>> x.list.append(1)
>>> y.list.append(2)
>>> x.list.append(3)
>>> y.list.append(4)
>>> print(x.list)
[1, 3]
>>> print(y.list)
[2, 4]

그러나 질문에서 혼란스러운 시나리오는 숫자와 문자열과 같은 불변의 객체에는 절대로 발생하지 않습니다. 할당없이 값을 변경할 수 없기 때문입니다. 예를 들어 문자열 속성 유형을 가진 원본과 비슷한 코드는 아무런 문제없이 작동합니다.

>>> class a:
    string = ''


>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'

요약하자면, 클래스 속성은 인스턴스화 후 __init__메소드에 있는지 여부에 관계없이 값이 지정된 경우에만 인스턴스 속성이됩니다 . 인스턴스화 후 속성에 값을 할당하지 않으면 정적 속성을 가질 수 있기 때문에 이것은 좋은 방법입니다.


11

"인스턴스 레벨 특성"이 아닌 "목록"을 "클래스 레벨 특성"으로 선언했습니다. 인스턴스 수준에서 속성 범위를 지정하려면 __init__메서드 에서 "self"매개 변수를 참조하여 (또는 상황에 따라 다른 곳 에서) 속성을 초기화해야합니다 .

__init__메서드 에서 인스턴스 속성을 엄격하게 초기화 할 필요는 없지만 이해하기 쉽습니다.


11

허용 된 답변이 발견되었지만 약간의 설명을 추가하고 싶습니다.

작은 운동을하자

우선 다음과 같이 클래스를 정의하십시오.

class A:
    temp = 'Skyharbor'

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

    def change(self, y):
        self.temp = y

그래서 우리는 여기에 무엇을해야합니까?

  • 우리는 temp문자열 인 속성 을 가진 매우 간단한 클래스를 가지고 있습니다
  • __init__설정 하는 방법self.x
  • 변경 방법 세트 self.temp

지금까지 꽤 직설적입니까? 이제이 수업을 시작해 봅시다. 이 클래스를 먼저 초기화 해 봅시다 :

a = A('Tesseract')

이제 다음을 수행하십시오.

>>> print(a.temp)
Skyharbor
>>> print(A.temp)
Skyharbor

글쎄, a.temp예상대로 작동했지만 어떻게 A.temp작동 했습니까? temp는 클래스 속성이기 때문에 잘 작동했습니다. 파이썬의 모든 것은 객체입니다. 여기 A는 class의 객체이기도합니다 type. 따라서 속성 temp는 A클래스가 보유한 속성 이므로 temp의 값을 A(를 통해가 아닌) 통해 a변경하면 변경된 값이 모든 A클래스 의 인스턴스에 반영됩니다 . 계속해서 해봅시다 :

>>> A.temp = 'Monuments'
>>> print(A.temp)
Monuments
>>> print(a.temp)
Monuments

흥미롭지 않습니까? 그리고 점에 유의 id(a.temp)하고 id(A.temp)여전히 동일합니다 .

모든 파이썬 객체 __dict__에는 속성 목록이 포함 된 속성 이 자동으로 부여됩니다 . 예제 객체에 대해이 사전에 포함 된 내용을 조사해 보겠습니다.

>>> print(A.__dict__)
{
    'change': <function change at 0x7f5e26fee6e0>,
    '__module__': '__main__',
    '__init__': <function __init__ at 0x7f5e26fee668>,
    'temp': 'Monuments',
    '__doc__': None
}
>>> print(a.__dict__)
{x: 'Tesseract'}

참고 temp속성 사이에 나열되어 A있는 동안 클래스의 속성 x인스턴스에 대해 나열됩니다.

따라서 a.temp인스턴스에 대해 나열되지 않은 경우 의 정의 된 값을 얻는 방법은 무엇입니까 a? 이것이 바로 __getattribute__()방법 의 마술입니다 . 파이썬에서 점으로 구분 된 구문은이 메소드를 자동으로 호출하므로 우리가 작성할 때 a.temp파이썬이 실행 a.__getattribute__('temp')됩니다. 이 메소드는 속성 조회 조치를 수행합니다. 즉, 다른 위치를보고 속성의 값을 찾습니다.

__getattribute__()검색 의 표준 구현은 먼저 객체 의 내부 사전 ( dict )과 객체 자체의 유형을 차례로 검색합니다. 이 경우 a.__getattribute__('temp')먼저 실행 a.__dict__['temp']한 다음a.__class__.__dict__['temp']

자 이제 우리의 change방법을 사용하자 :

>>> a.change('Intervals')
>>> print(a.temp)
Intervals
>>> print(A.temp)
Monuments

글쎄 지금 우리가 사용하는 것으로 self, print(a.temp)우리는 다른 값을 제공합니다 print(A.temp).

이제 id(a.temp)와 를 비교하면 id(A.temp)서로 다릅니다.


3

예. 목록이 클래스 속성이 아닌 객체 속성이되도록하려면 "생성자"에서 선언해야합니다.


3

따라서 거의 모든 반응이 특정 요점을 놓친 것 같습니다. 클래스 변수 아래 코드에서 보여주는 것처럼 인스턴스 변수가 되지 않습니다 . 메타 클래스를 사용하여 클래스 수준에서 변수 할당을 가로 채면 a.myattr이 다시 할당 될 때 클래스의 필드 할당 마법 메서드가 호출되지 않음을 알 수 있습니다. 할당 이 새 인스턴스 변수를 작성 하기 때문 입니다. 이 문제는 없습니다 절대적으로 아무것도 더 클래스 변수가 없습니다 아직 여전히 필드 할당을 허용하는 두 번째 클래스에 의해 입증 된 바와 같이 클래스 변수로 할 수 있습니다.

class mymeta(type):
    def __init__(cls, name, bases, d):
        pass

    def __setattr__(cls, attr, value):
        print("setting " + attr)
        super(mymeta, cls).__setattr__(attr, value)

class myclass(object):
    __metaclass__ = mymeta
    myattr = []

a = myclass()
a.myattr = []           #NOTHING IS PRINTED
myclass.myattr = [5]    #change is printed here
b = myclass()
print(b.myattr)         #pass through lookup on the base class

class expando(object):
    pass

a = expando()
a.random = 5            #no class variable required
print(a.random)         #but it still works

짧은 클래스 변수는 인스턴스 변수와 관련이 없습니다.

더 명확하게 인스턴스에 대한 검색 범위 내에있게됩니다. 클래스 변수는 실제로 클래스 객체 자체의 인스턴스 변수 입니다. 메타 클래스 자체도 객체이기 때문에 원하는 경우 메타 클래스 변수를 가질 수도 있습니다 . 모든 것은 다른 객체를 만드는 데 사용되는지 여부에 관계없이 객체이므로 단어 클래스의 다른 언어 사용의 의미에 묶이지 마십시오. 파이썬에서 클래스는 실제로 다른 객체를 만드는 방법과 그 동작을 결정하는 데 사용되는 객체 일뿐입니다. 메타 클래스는이 점을 더 자세히 설명하기 위해 클래스를 만드는 클래스입니다.


0

다른 인스턴스가 공유하는 변수를 보호하려면 인스턴스를 작성할 때마다 새 인스턴스 변수를 작성해야합니다. 클래스 내에서 변수를 선언하면 클래스 변수이며 모든 인스턴스가 공유합니다. 예를 들어 현명하게 만들려면 인스턴스 를 참조하여 변수를 다시 초기화 하기 위해 init 메소드를 사용해야합니다

에서 파이썬은 객체와 클래스 Programiz.com에 의해 :

__init__()함수. 이 특수 함수는 해당 클래스의 새 객체가 인스턴스화 될 때마다 호출됩니다.

이 유형의 함수는 객체 지향 프로그래밍 (OOP)에서 생성자라고도합니다. 일반적으로 모든 변수를 초기화하는 데 사용합니다.

예를 들면 다음과 같습니다.

class example:
    list=[] #This is class variable shared by all instance
    def __init__(self):
        self.list = [] #This is instance variable referred to specific instance
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.