# [Python] 클래스

## 클래스

---

### 클래스를 사용하는 이유

```javascript
# calculator.py
result = 0

def add(num):
    global result
    result += num  # 결괏값(result)에 입력값(num) 더하기
    return result  # 결괏값 리턴

print(add(3))
print(add(4))

```

- add함수에서 결과값을 유지하기 위해 전역변수를 사용

```javascript
# calculator2.py
result1 = 0
result2 = 0

def add1(num):  # 계산기1
    global result1
    result1 += num
    return result1

def add2(num):  # 계산기2
    global result2
    result2 += num
    return result2

print(add1(3))
print(add1(4))
print(add2(3))
print(add2(7))

```

- 결괏값을 두 개 저장하기 위해서는 전역 변수가 하나 더 있어야 한다

- 계산기 1의 결과가 계산기 2에 영향을 미치지 않기 위해선 전역변수가 점점 더 늘어날 것이다

```javascript
# calculator3.py
class Calculator:
    def __init__(self):
        self.result = 0

    def add(self, num):
        self.result += num
        return self.result

cal1 = Calculator()
cal2 = Calculator()

print(cal1.add(3))
print(cal1.add(4))
print(cal2.add(3))
print(cal2.add(7))
```

- Calculator 클래스로 만든 별 개의 계산기는 각각의 역할을 독립적으로 수행한다

- 함수만 사용할 때보다 간단하게 프로그램을 작성할 수 있다

### 클래스와 객체

![Image](https://upload.cafenono.com/image/slashpagePost/20250127/234020_5F3P9ngcIDqRZMLXXD?q=80&s=1280x180&t=outside&f=webp)

- **클래스 class**: 똑같은 무언가를 계속만들어 낼 수 있는 설계 도면 (과자 틀)

- **객체 object**: 클래스로 만든 피조물 (과자 틀로 찍어 낸 과자)

    - 동일한 클래스로 만든 객체들은 서로 영향을 미치지 않는다

    - **인스턴스 instance**는 클래스로 만든 객체로 관계 위주로 설명할 때 사용한다

### 클래스 구조

```javascript
def 함수_이름(매개변수):
    수행할_문장
    ...
```

```javascript
class FourCal:
    base = 1
    def setdata(self, first, second): # 메서드의 매개변수
        self.first = first              # 메서드의 수행문
        self.second = second            # 메서드의 수행문
    def add(self):
        result = self.first + self.second
        return result
    def div(self):
        result = self.first / self.second
        return result

a = FourCal()            # 객체 생성
a.setdata(4, 2)          # first=4, seoncd=2
FourCal.setdata(b, 3, 7) # 메서드 호출 다른 방법
a.add() # 6
a.base # 1
```

![Image](https://upload.cafenono.com/image/slashpagePost/20250127/234034_wUpr8OR30O4WVK6l5H?q=80&s=1280x180&t=outside&f=webp)

- 구현된 함수는 **메서드 method**라고 한다

- setdata 메서드는 self, first, second 3개의 입력값을 받는다

    - `self`: 파이썬 메서드의 첫 번째 매개변수 이름은 관례적으로 self를 사용

    - `setdata(4, 2)` → `a.first=4` `a.second = 2`

- 서로 다른 객체인 a, b는 독립적이기 때문에 서로 영향을 미치지 않는

- `객체.메서드()` 의 형태로 메서드를 실행할 수 있다

- `클래스_이름.클래스 변수`로 **클래스 변수**를 사용할 수 있다
- → 객체의 클래스 변수 값을 바꿔도 클래스의 변수 값은 바뀌지 않음

- `self`에는 객체 a가 자동으로 입력된다

**생성자 constructor**

```javascript
class FourCal:
    def __init__(self, first, second):
        self.first = first
        self.second = second
    ...

a = FourCal(4, 2)
```

- 객체가 생성될 때 자동으로 호출되는 메서드를 의미

- `__init__`를 사용하면 이 메서드는 생성자가 된다

- 위의 `__init__` 메서드는 `setdata` 메서드와 동일하지만, 생성자로 인식되어 생성 시점에 자동으로 호출된다

**예시**

```javascript
class Person:
    def __init__(self):
        self.name = input('이름')
        self.age = int(input('나이:'))
    def disp(self):
        print(self.name + ':' + self.age)

li = []
for i in range(3):
    li.append(Person())
for i in li:
    i.disp()
```

### 클래스 상속

```javascript
class 클래스_이름(상속할_클래스_이름)
```

```javascript
class MoreFourCal(FourCal):
    def __init__(self):
        super().__init__()
        self.third
    def pow(self):
        result = self.first ** self.second
        return result

a = MoreFourCal(4, 2, 5)
```

- 상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경할 때 사용

    - `self`: 현재 객체, 인스턴스 변수가 메서드를 참조할 때 사용

    - `super()`: 부모 클래스의 메서드를 호출할 때 사용, 부모 클래스의 `__init__` 실행 시 유용

- 기존 클래스는 그대로 놔둔 채 클래스의 기능을 확장할 때 주로 사용

**상속과 다형성**

```javascript
class Point:
    def __init__(self):
        self.x = int(input('x='))
        self.y = int(input('y='))
    def disp(self):
        pass

class Rect(Point):
    def __init__(self):
        super().__init__()
        self.w = int(input('w='))
        self.h = int(input('h='))
    def disp(self):
        print('사각형', self.x, self.y, self.w, self.h)

class Circle(Point):
    def __init__(self):
        super().__init__()
        self.r = int(input('r='))
    def disp(self):
        print('원', self.x, self.y, self.r)

def main():
    li = list()
    while True:
        s = input('1. 사각형, 2. 원, 3. 조회, 4. 종료:')
        if s == '1':
            li.append(React())
        if s == '2':
            li.append(Circle())
        if s == '3':
            for i in li:
                i.disp()
        if s == '4':
            break;
```

- **상속**: 한 클래스가 다른 클래스의 속성과 메서드를 물려 받음 → 재사용성↑, 관계명

    - `Rect`와 `Circle` 클래스는 `Point` 클래스를 상속받아 `x`, `y` 좌표를 상속

- **다형성**: 하나의 메서드가 여러 형태로 동작

    - 파이썬에서는 주로 메서드 오버라이딩을 통해 다형성 구현

    - `disp()` 메서드는 `Point` 클래스에서 정의되지 않고, 자식 클래스에서 다르게 구현

### 메서드 오버라이딩

```javascript
class SafeFourCal(FourCal):
    def div(self):
        if self.second == 0: # 나누는 값이 0인 경우 0을 리턴
            return 0
        else:
            return self.first / self.second
```

- div 메서드를 동일한 이름으로 다시 작성

### 접근 제한

```javascript
class Sj:
    def __init__(self):
        self.kor
        self.__eng # 네임 맹글링 적용

obj = MyClass(90, 80)
print(obj.kor)      # 정상 출력
# print(obj.__eng)  # AttributeError
print(obj._Sj__eng) # 네임 맹글링된 이름으로 접근 가능
```

- `self.kor`: 외부에서 자유롭게 접근하거 수정할 수 있는 일반적인 인스턴스 변수

- `self.__eng`: 네임 맹글링을 통해 클래스 외부에서 직접 접근이 어려운 비공개 변수

    - 주로 외부에서 실수로 수정되지 않도로 보호하려는 목적

    - 변수의 이름을 변경하는 방식으로 접근을 어렵게 만든다

## 객체지향 프로그래밍 심화

---

### 클래스 메서드 (@classmethod)

- 클래스에 적용하는 메서드

- 첫 번째 인자로 클래스 자체를 받는다 (일반적으로 `cls`로 명명)

```javascript
class MyClass:
    count = 0
    
    @classmethod
    def increment(cls):
        cls.count += 1

MyClass.increment()
print9MyClass.count) # 출력: 1
```

### 스태틱 메서드 (@staticmethod)

- 클래스나 인스턴스와는 독립적으로 작동하는 메서드

- `self`나 `cls`와 같은 특별한 첫 번째 인자를 받지 않음

```javascript
class MyClass:
    @staticmethod
    def my_method(x, y):
        return x + y

print(MyClass.my_method(5, 3)) # 출력: 8
```

```javascript
class CompletionsList:
    def __init__(self):
        self.subject_list = []
    
    def show(self):
        print(self.subject_list)
    
    def append(Self, subject):
        self.subject_list.append(subject)
    
    @staticmethod
    def academic_warning(subject):
        '''
        내부에서 클래스 변수, 인스턴스 변수 수정하는 것이 가능하지 않습니다
        '''
        return abs(1.5 + subject['grades'])

c = CompletionList()

subject1 = {'name': 'Python', 'grades': 2.5}
subject2 = {'name': 'HTML/CSS', 'grades': 3.5}

c.append(subject1)
c.append(subject2)
c.show()

print(c.academic_warning(subject1))
```

### 속성 접근자 Property

- `@Property` 데코레이터를 사용하여 클래스의 메서드를 속성처럼 접근할 수 있도록

- 객체의 내부 상태를 보호, 특정 속성에 대한 접근을 제어

```javascript
class Person:
    def __init__(self first_name, last_name):
        self._first_name = first_name
        self._last_name = last_name
    
    @property
    def full_name(self):
        return f"{self._first_name} {self._last_name}"
    
hojun = Person("lee", "hojun")
hojun._first_name
hojun._last_name
hojun.full_name
hojun.full_name()
```

- `full_name` 속성에 접근하면 `_first_name`과 `_last_name`을 합친 문자열을 반환

- `@property` 데코레이터가 없다면 full_name() 메서드를 직접 호출해야한다

- setter를 정의할 수도 있다 → 유효성 검사나 추가적인 연산 수행도 가능

For the site tree, see the [root Markdown](https://slashpage.com/yerim-devnote.md).
