컴퓨터 공부/🐍 Python

What is "self" in Python?

letzgorats 2024. 1. 8. 12:30

 

파이썬 코드를 짤 때, 언제 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

 

Range Sum of BST - LeetCode

Can you solve this real interview question? Range Sum of BST - Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high].   Example 1: [https://assets.l

leetcode.com

 

(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'를 씀으로써 접근할 수 있다.


반응형