파이썬 코드를 짤 때, 언제 self 를 써야하고, self의 적용범위가 어디까지인지 이런 개념을 완벽히 잘 모르고 그냥 감으로 self 를 짠 경우가 있을 것이다.
나도 디버깅을 하면서 self 를 붙일지 말지 찾아내곤 하는데, self 에 대해서 자세하고 정확한 예시 등으로 개념을 바로 잡아보자!
Python 에서 클래스를 정의하고 객체지향 프로그래밍을 할 때, 'self' 는 클래스의 인스턴스를 가리키는 변수이다.
클래스의 메소드는 첫 번째 매개변수로 항상 "self"를 받아, 그 인스턴스의 속성과 다른 메소드에 접근할 수 있게 하는 것이다.
※ 'self'의 역할과 사용법
1. 인스턴스 참조
'self'는 클래스의 현재 인스턴스를 참조한다. 클래스 내부에서 'self'를 사용하면, 그 클래스의 다른 메소드나 속성에 접근할 수 있다.
2. 속성 정의 및 접근
클래스의 인스턴스가 생성될 때, 'self'를 사용해 인스턴스 변수를 초기화한다. 예를 들어, 'self.name = name' 같은 코드는 인스턴스 'name' 이라는 속성을 추가한다.
3. 메소드 정의 및 호출
클래스 내부에서 메소드를 정의할 때, 첫 번째 매개변수로 'self' 를 사용한다. 이를 통해, 해당 메소드가 특정 인스턴스에 속함을 나타내고, 호출할 때는 'self.methodName()' 형식으로 사용한다.
4. 각 인스턴스마다 독립적
'self' 는 각 인스턴스마다 독립적이다. 즉, 각 인스턴스는 자신만의 'self' 를 가지고 있으며 서로 다른 상태를 유지할 수 있다.
※ 'self'의 범위는 어디까지?
→ 'self' 는 클래스 내부에서만 유효하다. 클래스의 인스턴스 메소드 내에서만 'self'를 사용할 수 있다.
→ 각 인스턴스는 자신만의 'self'를 가지고 있어, 각각의 인스턴스는 독립적인 상태와 행동을 가진다.
※ 클래스 레벨 변수 vs 인스턴스 레벨 변수
1. 클래스 레벨 변수
→ 클래스 정의 내에서 정의되며, 모든 인스턴스에 대해 공유된다. 클래스 레벨 변수는 클래스 자체에 속하며, 모든 인스턴스에서 동일한 값을 가진다.
2. 인스턴스 레벨 변수
→ '__int__' 메소드 내에서 'self' 를 사용하여 정의된다. 이 변수들은 각 인스턴스마다 독립적인 값을 가진다.
그럼, 아래 문제를 예시로 잘못된 코드와 맞는 코드가 각각 어떤 것인지 맞춰보면서 이해를 확실히 해보자!
https://leetcode.com/problems/range-sum-of-bst/description/?envType=daily-question&envId=2024-01-08
(1)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
self.answer = 0
def rangeSumBST(self, root, low, high):
"""
:type root: TreeNode
:type low: int
:type high: int
:rtype: int
"""
if not root:
return
if low <= root.val <= high:
self.answer += root.val
self.rangeSumBST(root.left,low,high)
self.rangeSumBST(root.right,low,high)
return self.answer
맞았을까 틀렸을까? (접은글 확인)
틀렸다(X)
→ 이 코드에서 self.answer = 0을 클래스 레벨에서 정의하는 부분에 문제가 있다.
Python에서 클래스 레벨의 변수와 인스턴스 레벨의 변수는 다르게 동작하는데, 여기서 self.answer는 인스턴스 변수로 사용되어야 하며, 이를 클래스 레벨에서 정의하면 문제가 발생한다
(2)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
answer = 0
def rangeSumBST(self, root, low, high):
"""
:type root: TreeNode
:type low: int
:type high: int
:rtype: int
"""
if not root:
return
if low <= root.val <= high:
self.answer += root.val
self.rangeSumBST(root.left,low,high)
self.rangeSumBST(root.right,low,high)
return self.answer
맞았을까 틀렸을까? (접은글 확인)
맞았다(O)
→ 이 코드에서는 Solution 클래스 내에서 'answer'를 클래스 레벨 변수로 정의했고, 'rangeSumBST' 메소드를 재귀적으로 잘 호출하고 있다.
'answer' 가 클래스 레벨 변수로 정의되었기 때문에, 'Solution' 클래스의 모든 인스턴스가 이 값을 공유한다.
하지만, 각 인스턴스마다 독립적인 'answer' 값을 유지하고 싶다면, '__init__' 메소드 내에서 self.answer = 0'으로 초기화 하는 것이 더 안전하다.
(3)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def __init__(self):
self.answer = 0
def rangeSumBST(self, root, low, high):
"""
:type root: TreeNode
:type low: int
:type high: int
:rtype: int
"""
if not root:
return
if low <= root.val <= high:
self.answer += root.val
self.rangeSumBST(root.left,low,high)
self.rangeSumBST(root.right,low,high)
return self.answer
맞았을까 틀렸을까? (접은글 확인)
맞았다(O)
→ 이 코드에서는 Solution 클래스 내에서 'answer'를 인스턴스 변수로 초기화했다. 'rangeSumBST' 메소드를 재귀적으로 잘 호출하고 있다.
이 방식은 각 'Solution' 인스턴스가 자신만의 'answer' 값을 유지하므로, 다른 인스턴스와의 상호 작용에서 발생할 수 있는 문제를 방지한다.
(4)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
answer = 0
def rangeSumBST(self, root, low, high):
"""
:type root: TreeNode
:type low: int
:type high: int
:rtype: int
"""
if not root:
return
if low <= root.val <= high:
answer += root.val
self.rangeSumBST(root.left,low,high)
self.rangeSumBST(root.right,low,high)
return answer
맞았을까 틀렸을까? (접은글 확인)
틀렸다(X)
→ 이 코드에서는 'answer' 변수의 사용 방식과 'return'문에서 반환하는 값이 틀렸다.
'answer += root.val' 에서 'answer'는 현재 로컬 변수로 해석되는데, 이 코드에서는 'answer'은 로컬 변수로 선언되지 않았다.
클래스 레벨의 'answer'를 참조하려면 'self.answer'를 사용해야 한다.
'return answer' 에서도 'answer'는 로컬 변수로 해석되지만, 이 변수는 함수 내에서 정의된 적이 없다. 즉, 올바른 값을 반환하려면 'self.answer' 로 적어줘야 한다.
즉, python 에서 'self' 는 클래스의 인스턴스 메소드 내에서 해당 인스턴스를 참조하는 데 사용되는 키워드이다.
클래스 레벨의 변수를 참조하기 위해서는 메소드 내에서 'self' 를 씀으로써 접근할 수 있고, 메소드 앞에도 'self'를 씀으로써 접근할 수 있다.
'컴퓨터 공부 > 🐍 Python' 카테고리의 다른 글
정규식을 사용해 여러개의 구분자로 split 하는 방법 (0) | 2024.09.21 |
---|---|
리스트 원소 중에서 가장 길이가 긴(최대길이) 원소 찾기 (0) | 2024.09.20 |
Remove Non-alphanumeric Characters in Python (0) | 2023.12.13 |
for문에 else문이 딸랑? (2) | 2023.12.02 |
list(map(int, input().strip().split())) 와 [map(int, input().strip().split())] 의 차이 (2) | 2023.11.21 |