파이썬의 클래스와 객체

박나현·2024년 4월 28일

[기록] 1일 1성장

목록 보기
4/5

파이썬에서의 클래스와 객체

클래스와 객체(인스턴스)의 개념

파이썬에서의 클래스란 사용자가 정의한 객체를 사용하기 위한 템플릿이다. 주로 객체(클래스의 인스턴스)에 적용되는 메서드를 포함한다. 파이썬의 list, dict 등의 자료형도 클래스이며 해당 클래스의 인스턴스인 변수들은 전부 객체이다.

클래스에는 속성, 메서드가 존재한다.

  • 생성자(init) init 메서드는 인스턴스를 생성할 때 호출되는 생성자이다. 인스턴스를 초기화하고 순서에 맞춰 각 인수를 self.속성값으로 할당한다. 생성자는 클래스 당 하나만 생성이 가능하다. 속성을 다르게 가지는 생성자 클래스를 생성하면 가장 나중에 작성된 생성자 메서드를 따른다. 즉 오버로딩이 불가능하다.
    class User:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        
        def __init__(self,name,age,email):
            self.name = name
            self.age = age
            self.email = email
    
        def introduce(self):
            print("저는 {0}이고 {1}살입니다. 제 이메일은 {2}입니다. ".format(self.name, self.age, self.email))
        
    # user1 = User("박나현",200)
    user2 = User("박나현",200,"nahowo@naver.com")
    
    # user1.introduce()
    user2.introduce()
  • 클래스 속성/인스턴스 속성 인스턴스 속성은 개별 메서드(생성자 포함)에서 사용된 속성을 의미한다. 클래스 속성은 클래스가 가지는 속성을 의미하며 모든 인스턴스에서 클래스 속성을 공유한다. 아래의 예시에서 value는 클래스 속성, name, age, email은 인스턴스 속성이다.
    class User:
        value = 40
        def __init__(self,name,age,email):
            self.name = name
            self.age = age
            self.email = email
        
    user2 = User("박나현",200,"nahowo@naver.com")
    
    print(user2.value) # 40
    print(User.value) # 40
    클래스 속성은 인스턴스에게도 공유되므로 따로 변경하기 이전에는 모두 같은 값을 가진다.

객체를 입력으로 받는 문제 풀기

1번. Remove Nth Node From End of List

Leetcode에서 입력이 객체로 주어지는 문제를 풀어 보자.

예시로 이 문제를 풀어보자. 연결 리스트의 head가 주어질 때, 뒤에서 n번째 노드를 제거하고 연결 리스트의 head를 반환하는 문제이다.

우선 입력은 다음과 같이 주어진다.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:

ListNode라는 클래스는 val, next라는 인스턴스 속성을 갖는다. 문제에서는 head라는 연결 리스트의 첫번째 원소가 해당 ListNode 클래스의 인스턴스로 주어진다. head.val은 해당 노드의 값, head.next는 head 다음의 ListNode 인스턴스를 가리킨다.

해당 입력이 어떻게 주어지는지 확인했으니 문제를 어떻게 접근할지 생각해 보자.

뒤에서 n번째 노드를 제거해야 하므로 head부터 연결 리스트의 끝까지 순회하는 과정이 필요하다. 노드의 개수를 센 뒤 앞에서 (노드 개수-n+1)번째 노드를 제거하면 된다.

  1. head에서 순회를 시작한다.
  2. 카운트를 위한 변수와 포인터 역할을 할 변수를 생성하고 next≠None인 동안 반복하며 카운트 값을 1씩 증가시킨다.
  3. next가 None이 되면 다시 head로 돌아가 카운트-n+1번째까지 순회한다.
  4. 카운트-n+1번째 노드의 이전 노드.next 값을 카운트-n+1번째 노드의 next값으로 변경한다.
  5. head를 반환한다.

예외 상황을 처리해 보자. n이 만약 1이라면 n-1번째의 next를 n+1의 next로 연결하고 싶어도 제거되는 n번째 원소가 맨 끝 원소이므로 연결할 수 없다. 즉 None으로 연결해줘야 한다.

또한 연결 리스트의 길이가 1이라면 head.next 부터가 None이므로 head.next.next를 접근할 때부터 오류가 발생한다. 따라서 이 경우는 단순히 head를 None으로 설정해주면 된다.

n이 연결 리스트의 길이와 같다면(= 제거해야 하는 원소가 head라면) head를 다음 노드로 이동시키면 된다.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        cnt=0
        pointer=head
        while pointer!=None:
            pointer=pointer.next
            cnt+=1
        if cnt==1:
            head=None
            return head
        num=cnt-n
        if cnt==n:
            head=head.next
            return head
        
        pointer=head
        for i in range(num-1):
            head=head.next
        if n==1:
            head.next=None
        else:
            head.next=head.next.next
        head=pointer
        return head

2번. Reverse Linked List

두 번째로 이 문제를 풀어보자.

단방향 연결 리스트의 head 노드가 주어졌을 때, 거꾸로 뒤집힌 연결 리스트를 반환해 보자.

입력은 위의 문제와 동일하다.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

단방향이므로 head에서 순회하며 돌 때 head.next 속성을 head로 변경하면 된다. 하지만 그러면 다음으로 넘어갈 수 없으니 변경하는 순서에 주의하며 연결 리스트를 뒤집어야 한다.

nxt는 head 다음 노드부터 계속 순방향으로 가리키는 노드여야 한다.

  1. head에서 순회를 시작한다. nxt 변수에 head.next를 연결하고 prv는 None으로 두어 시작한다. head.next는 prv로 변경한다. head를 nxt로 옮긴다.
  2. nxt≠None일 때 다음 과정을 반복한다.
    1. head는 nxt로 옮긴다.
    2. nxt는 nxt.next로 옮긴다.
    3. head.next를 prv로 변경한다.
    4. prv는 head로 옮긴다.
  3. head를 반환한다.

예외 상황은 연결 리스트가 비었을 때이다. head가 None이므로 head.next가 존재하지 않는다.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head==None:
            return None
        nxt = head.next
        prv = None
        head.next = prv
        prv = head
        
        while nxt != None:
            head = nxt
            nxt = nxt.next
            head.next = prv
            prv = head
        return head

느낀점

알고리즘을 파이썬으로 풀면서도 클래스/객체에 대해 잘 몰랐어서 한번 정리해보고 싶었다. leetcode가 아닌 플랫폼에서도 클래스를 사용해 풀이해보고 싶다. leetcode 스터디에서 풀고 있는 Easy/Medium 컬렉션에서 이런 인스턴스를 입력으로 주는 문제가 꽤 많이 나오는데, 입력 형식이 헷갈려서 넘기는 경우가 많았다. 이렇게 한번 공부를 함으로써 이후 문제를 더 편하게 풀 수 있을 것 같다.

profile
의견을 가지고 학습하기, 질문하기, 궁금했던 주제에 대해 학습하는 것을 미루지 않기

0개의 댓글