클라스/메소드/객체/생성자/self 개념 : https://velog.io/write?id=86be4d35-8ff5-4035-b19f-be546fb44ea1
먼저 개념잡고갈게요~!
지역 변수 (Local Variables):클래스 내의 메소드 안에서 self.변수명 형태로 정의되지 않은 변수
함수나 메서드의 실행이 종료되면 그 스코프에서 사용되던 지역 변수는 메모리에서 제거됩니다.
인스턴스 변수 (Instance Variables):
인스턴스 변수는 객체의 생명주기와 연결되어 있습니다. 객체에 대한 모든 참조가 사라지면, 그 객체는 가비지 컬렉션의 대상이 되며, 이후에 메모리에서 제거됩니다. 따라서, 해당 객체의 인스턴스 변수도 메모리에서 제거됩니다.
"객체에 대한 모든 참조가 사라지면"이라는 문장은, 프로그램 내에서 해당 객체를 가리키는 모든 변수나 참조가 없어진 경우를 의미합니다.실제로 언제 메모리에서 제거될지는 가비지 컬렉터의 작동 방식과 실행 타이밍에 따라 다를 수 있습니다.
class Example:
def __init__(self, data):
self.data = data
# 객체 생성
obj = Example("some data")
# 다른 데이터로 obj 참조를 덮어쓰기
obj = "something else"
위 코드에서, 처음 obj는 Example 클래스의 인스턴스를 참조하고 있습니다. 그런데 이후에 obj에 새로운 문자열 값을 할당함으로써, 원래 Example 인스턴스에 대한 참조가 사라지게 됩니다.
이 시점에서, Example 클래스의 인스턴스는 어떠한 변수에도 참조되지 않는 상태가 되며, 가비지 컬렉션의 대상이 됩니다. 한편, obj라는 전역 변수는 여전히 존재하며, 이제는 문자열 "something else"를 참조하게 됩니다.
클래스 변수는 프로그램의 생명주기 동안 유지됩니다. 프로그램이 종료되면, 해당 메모리는 해제됩니다. 그러나, 동적으로 클래스를 생성/제거하는 고급 기술을 사용하지 않는 한, 일반적으로 프로그램이 실행되는 동안 클래스 자체는 메모리에 유지됩니다.
중요한 것은 파이썬의 가비지 컬렉터는 참조되지 않는 객체를 자동으로 메모리에서 제거하는데, 이는 대부분의 경우에 효율적으로 동작합니다. 그러나, 때로는 순환 참조와 같은 일부 상황에서 메모리 누수 문제가 발생할 수 있으므로, 프로그램의 메모리 사용 패턴을 주기적으로 모니터링하는 것이 좋습니다.
순환참조/참조카운팅/메모리누수 관련 : 링크텍스트
클라스 변수와 인스턴스 변수는 무엇인가?
"속성"(= attribute)이라는 표현은 파이썬에서 클래스 내부에 정의된 변수를 가리키는 데 일반적으로 사용되는 용어이다.
클래스 내부의 변수는 "속성" 또는 "변수"로 부를 수 있으며, 이러한 속성은 다시 "인스턴스 속성"과 "클래스 속성"으로 구분될 수 있습니다.
즉, 클라스 변수와 인스턴스 변수라는 것이죠.
>>> class Account:
num_accounts = 0
def __init__(self, name):
self.name = name
Account.num_accounts += 1
def __del__(self):
Account.num_accounts -= 1
>>>
이전의 말했다 시피 클라스 내부의 있는 self.형태를 가진 변수는 인스턴스 변수라고 말했다.
num_accounts처럼 클래스 내부에 선언된 변수를 클래스 변수라고 하며,
self.name과 같이 self가 붙어 있는 변수를 인스턴스 변수라고 한다.
클래스 변수는 Account 클래스의 네임스페이스에 위치하며,
self.name과 같은 인스턴스 변수는 인스턴스의 네임스페이스에 위치하게 된다.
예를 들어보자.
먼저 클래스 인스턴스를 생성해줄 것이며,
생성된 인스턴스들의 계좌소유자 정보가 제대로 저장됐는지 확인하고,
>>> kim = Account("kim")
>>> lee = Account("lee")
>>>
>>> kim.name
'kim'
>>> lee.name
'lee'
>>>
총계좌개설개수를 확인한다.
>>> kim.num_accounts
2
>>> lee.num_accounts
2
>>>
여기서 보면 클라스 변수에서 값을 불러왔다는것을 알 수있다.
클래스 인스턴스의 네임스페이스에서는 num_accounts를 찾았지만 해당 변수가 없어 클래스의 네임스페이스로 이동하여 해당이름을 찾고 그 값이 반환된 것.
이렇게 인스턴스 간에 서로 공유해야하는 값은 클래스 변수를 통해 바인딩해야한다.
파이썬은 인스턴스의 네임스페이스에 없는 이름은 클래스의 네이스페이스에서 찾아보기때문에 이러한 특성을 이용하면 클래스 변수가 모든 인스턴스에 공유될 수 있기 때문이다.
>>> Account.num_accounts
2
>>>
이렇게 클래스 변수에 접근할때는 클래스 이름을 사용하여 접근할 수도 있다.
class Monster():
hp = 100
alive = True
def damage(self, attack):
print(self.hp)
self.hp = self.hp - attack
if self.hp < 0:
self.alive = False
def status_check(self):
if self.alive:
print('살아있다')
else:
print('죽었다')
m = Monster()
m.damage(120) => -20
m2 = Monster()
m2.damage(90) => 10
m.status_check() => 죽었다.
m2.status_check() => 살았다.
class Monster():
hp = 100
alive = True
def damage(self, attack):
Monster.hp = Monster.hp - attack
print(Monster.hp)
if Monster.hp < 0:
self.alive = False
def status_check(self):
if self.alive:
print('살아있다')
else:
print('죽었다')
m = Monster()
m.damage(120) => -20
m2 = Monster()
m2.damage(90) => -20 -90 =-110
m.status_check() => 죽었다.
m2.status_check() => 죽었다.
여기서는 클래스변수를 인스턴스변수로 'self.hp' 해준 이유는 각 생성해주는 인스턴스마다 값이 달라져야하기 때문이다!
self = 생성될 클래스인스턴스의 변수라고 했지?
클라스 변수로 대입될경우 클라스 디폴트 값이 달라지기때문에 디폴트 값이 바뀌어야 할 때아니고는 그렇게 넣어선 안된다.
클라스 변수 => 디폴트값이 계속 변경되어야 할때
인스턴스 변수 => 각 인스턴스마다 값 지정, 클라스 내 가용범위 넓히기 위해서
self로 지정해주면 인스턴스 변수 및 클라스 내 함수의 접근이 가능하다.
네임스페이스란 변수가 객체를 바인딩할 때 그 둘 사이의 관계를 저장하고 있는 공간을 의미하는데, 예를 들어 b = 6이라 할떄 b라는 변수가 6이라는 객체가 저장된 주소를 가지고 있는데 그러한 연결관계가 저장된 공간이 바로 네임스페이스다.
클래스는 새로운 타입(객체)을 정의하기 위해 사용되며, 모듈과 마찬가지로 하나의 네임스페이스를 가지게된다.
만약 Stock이라는 클래스 생성이후 파이썬 내장함수 dir()호출해보면 리스트로 된 반환값을 확인할 수 있는데
>>> dir()
['Stock', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>>
이렇게 입력이 들어가줘야 클라스 호출에 프롬프트에 오류가 발생되지않는다.
클래스가 정의되면 하나의 독립적인 네임스페이스가 생성되고,
Stock 클래스의 네임스페이스를 파이썬 코드로 확인하려면 클래스의 __dict__
속성을 확인하면 된다. >>>Stock.__dict__
여기서 알아야될 관점은 클래스 인스턴스 2개를 같은 클라스로부터 생성했다면,
그 2개의 인스턴스는 각각 클라스와 다른 네임페이스를 가지게되지만 값이 따로 지정되어져있지 않을시 본래의 클라스로 부터 값을 가져올 수 있다라는 점이다.
사진 참조:https://andamiro25.tistory.com/38
예를 들어보면
>>> class Stock:
market = "kospi"
>>>
>>> s1 = Stock()
>>> s2 = Stock()
>>> id(s1)
50572464
>>> id(s2)
50348240
>>>
이렇게 같으 클라스로 부터 새로운 2개의 인스턴스를 생성하였고, id값을 보면 서로 다른 메모리에 저장되어 있다는 것을 확인할 수 있다.
그렇다고해서 인스턴스가 클라스의 네이스페이스도 가져오는 것은 아니라는 의미이다.
인스턴스별로 별도의 네임스페이스를 유지하는데
클라스안에 딕셔너리 값을 넣어줬다고 해서 그 값의 네임스페이스가 새로 생성된 s1, s2까지 가지고 오지 않았다는 뜻이다.
s1, s2을 dir()함수로 반환값을 확인하면 클라스와 마찬가지로 존재하지만
>>> dir()
['Stock', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 's1', 's2']
>>>
__dict__
속성을 통해 확인하면 인스턴스 네임스페이는 현재 비어있다고 확인할 수 있다.
그러면 s1에 {'market: 'kodaq'}이라는 값을 주고 s1.market로 접근하면 값은 kodaq이 나온다. s1 인스턴스의 네임스페이스에 'market':'kosdaq'이라는 키:값 쌍이 존재하기 때문에 가능한것.
그리고 s2.market을 호출하면 현재 s2의 네임스페이스(딕셔너리)에는 아무런 값도 존재하지 않지만 'kospi'라는 문자열이 반환되는 것을 알 수 있다.
왜? 바로 위에서 말했다시피 s2의 네임스페이스에 해당 변수가 존재하지 않으면 s2 인스턴스의 클래스의 네임스페이스로 가서 다시 변수를 찾아 반환해주는것이다.
s2.volume이라는 변수를 찾는데 클래스의 네이스페이에서도 그 변수가 없다면 에러를 반환
=>AttributeError: 'Stock' object has no attribute 'volume'
헷갈리기 쉬운 부분이 이해가 잘되네요👍