Development Tip

Python 강좌 11 - Class

MoonLight314 2024. 12. 23. 11:08
728x90

안녕하세요, MoonLight입니다.

이번 Post에서는 Python의 Class에 대해서 알아보도록 하겠습니다.

1. 기본 사항

Python의 Class(class)는 객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 핵심 개념으로, 데이터(속성)와 이를 처리하는 Method(함수)를 함께 묶는 사용자 정의 데이터 구조입니다.

Class는 객체를 생성하기 위한 템플릿(또는 청사진) 역할을 합니다.

 

 

 

1.1. Class 정의

기본 구조

class ClassName:
    # 생성자 Method
    def __init__(self, attribute1, attribute2):
        self.attribute1 = attribute1
        self.attribute2 = attribute2

    # Class Method
    def method(self):
        return f"Attributes are {self.attribute1} and {self.attribute2}"

 

 

1.2. 객체 생성

Class를 정의한 후, 인스턴스를 생성해 사용할 수 있습니다.

class Person:
    def __init__(self, name, age):
        self.name = name # 인스턴스 속성
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

# 객체 생성
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)

# Method 호출
print(person1.greet()) # Hello, my name is Alice and I am 30 years old.
print(person2.greet()) # Hello, my name is Bob and I am 25 years old.

 

1.3. Class의 구성 요소

1.3.1. 속성 (Attributes)

Class 내부에서 데이터(변수)를 저장하는 역할. 속성은 Class 속성인스턴스 속성으로 나뉩니다.

Class 속성 (Class Attribute)

  • Class 자체에 정의된 속성으로, 모든 객체가 공유.
  • Class 이름으로 접근.

인스턴스 속성 (Instance Attribute)

특정 객체(인스턴스)에 속하는 속성으로, 각 객체마다 고유.

class Car:
    wheels = 4 # Class 속성 (모든 객체가 공유)

    def __init__(self, brand, color):
        self.brand = brand # 인스턴스 속성
        self.color = color

# 객체 생성
car1 = Car("Toyota", "Red")
car2 = Car("Honda", "Blue")

print(car1.wheels) # 4 (Class 속성)
print(car1.brand) # Toyota (인스턴스 속성)

1.3.2. Method

Class 내부에서 정의된 함수로, 객체가 동작하도록 만듭니다.

인스턴스 Method

self를 첫 번째 매개변수로 받아야 하며, 인스턴스 속성에 접근 가능.

class Dog:
    def __init__(self, name):
        self.name = name

    def bark(self):
        return f"{self.name} says Woof!"

dog = Dog("Buddy")
print(dog.bark()) # Buddy says Woof!

Class Method

  • @classmethod 데코레이터를 사용하며, cls를 첫 번째 매개변수로 받음.
  • Class 속성에 접근 가능.
  •  
class Circle:
    pi = 3.14159 # Class 속성

    @classmethod
    def area(cls, radius):
        return cls.pi * radius ** 2

print(Circle.area(5)) # 78.53975

정적 Method

  • @staticmethod 데코레이터를 사용하며, Class나 인스턴스와 상관없는 동작 정의.
  • self 또는 cls 매개변수를 받지 않음.
  •  
class Math:
    @staticmethod
    def add(a, b):
        return a + b

print(Math.add(3, 5)) # 8

1.3.3. 생성자와 소멸자

생성자 (__init__)

  • 객체 생성 시 초기화 작업을 수행.
  • self를 통해 인스턴스 속성을 초기화.
  •  
class Person:
    def __init__(self, name):
        self.name = name

person = Person("Alice")
print(person.name) # Alice

소멸자 (__del__)

  • 객체가 메모리에서 제거될 때 호출.
  • 명시적으로 호출할 필요는 없으며, 메모리 관리에 관여.
  •  
class Person:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print(f"{self.name} is being deleted.")

person = Person("Alice")
del person # Alice is being deleted.

 

 

1.4. 상속(Inheritance)

상속(Inheritance)은 객체지향 프로그래밍(OOP)의 중요한 개념 중 하나로, 기존 Class(부모 Class, 상위 Class)를 기반으로 새로운 Class(자식 Class, 하위 Class)를 정의하는 방법입니다.

상속을 통해 코드 재사용성과 확장성을 높일 수 있습니다.

1.4.1. 상속의 기본 개념

부모 Class(Superclass, Base Class)

  • 공통된 속성이나 Method를 정의하는 Class.
  • 자식 Class에 속성과 Method를 물려줍니다.

자식 Class(Subclass, Derived Class)

  • 부모 Class를 상속받아 새롭게 정의된 Class.
  • 부모 Class의 모든 속성과 Method를 상속받으며, 필요에 따라 새로운 기능을 추가하거나 기존 기능을 변경(오버라이딩)할 수 있습니다.

1.4.2. 상속의 사용 방법

Python에서는 Class 정의 시 부모 Class를 괄호 안에 명시하여 상속받습니다.

기본 문법

class Parent: # 부모 Class
    def parent_method(self):
        print("This is a method from the Parent class.")

class Child(Parent): # 자식 Class
    pass # 부모 Class의 모든 것을 상속받음

# 사용
child = Child()
child.parent_method() # 부모 Class의 Method 호출

출력:
This is a method from the Parent class.

1.4.3. 상속의 특징

코드 재사용

공통된 기능은 부모 Class에 정의하고, 자식 Class는 이를 상속받아 사용함으로써 코드 중복을 줄일 수 있습니다.

확장성

자식 Class는 부모 Class의 기능을 확장하거나 수정할 수 있습니다.

다중 상속

Python에서는 한 Class가 여러 부모 Class를 상속받을 수 있습니다.

1.4.4. 오버라이딩(Overriding)

자식 Class에서 부모 Class의 Method를 같은 이름으로 다시 정의하면, 부모 Class의 Method가 무시되고 자식 Class의 Method가 호출됩니다.

예제

class Parent:
    def greet(self):
        print("Hello from Parent!")

class Child(Parent):
    def greet(self): # 부모 Class의 greet Method를 오버라이딩
        print("Hello from Child!")

# 사용
child = Child()
child.greet() # 자식 Class의 Method가 호출됨

출력:
Hello from Child!

1.4.5. super() 키워드

super()는 부모 Class의 Method나 속성을 호출할 때 사용됩니다.

주로 자식 Class에서 부모 Class의 생성자나 Method를 호출하여 기본 기능을 유지하면서 확장할 때 사용됩니다.

예제

class Parent:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, my name is {self.name}.")

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name) # 부모 Class의 생성자 호출
        self.age = age

def greet(self):
    super().greet() # 부모 Class의 greet 호출
        print(f"I am {self.age} years old.")

# 사용
child = Child("Alice", 12)
child.greet()

출력:
Hello, my name is Alice.
I am 12 years old.

1.4.6. 다중 상속(Multiple Inheritance)

Python에서는 한 Class가 여러 부모 Class를 상속받을 수 있습니다.

예제

class A:
    def method_a(self):
        print("Method from class A")

class B:
    def method_b(self):
        print("Method from class B")

class C(A, B): # 다중 상속
    pass

# 사용
c = C()
c.method_a() # A의 Method
c.method_b() # B의 Method

출력:
Method from class A
Method from class B

1.4.7. 상속의 장점

코드 재사용:

부모 Class에 공통 기능을 정의하면 여러 자식 Class에서 재사용 가능.

유지보수 용이:

공통 기능을 부모 Class에 정의하면, 수정 시 모든 자식 Class에 자동으로 반영.

확장성:

자식 Class에서 새로운 기능 추가 및 기존 기능 확장 가능.

1.4.8. 상속의 단점

복잡성 증가:

상속 계층이 깊어지면 코드의 가독성과 관리가 어려워질 수 있음.

다중 상속 문제:

여러 부모 Class에서 같은 이름의 Method를 정의하면 충돌이 발생할 수 있음.

Python은 MRO(Method Resolution Order)를 사용해 이를 해결.

1.4.9. 정리

상속은 객체지향 프로그래밍의 중요한 개념으로, 코드의 재사용성과 확장성을 높이는 데 유용합니다.

Python에서는 단일 상속과 다중 상속을 지원하며, 자식 Class는 부모 Class의 기능을 상속받고, 필요에 따라 오버라이딩하거나 super()를 사용해 부모의 Method를 확장할 수 있습니다.

 

1.5. 특수 Method (Magic Methods)

특수 Method는 객체의 특정 동작을 정의하며, 더블 언더스코어(__)로 감싸여 있습니다.

예제: __str__과 __repr__

  • __str__ : 객체를 사용자에게 보기 좋게 출력.
  • __repr__ : 객체를 개발자가 식별 가능하게 출력.
  •  
class Point:
    def __init__(self, x, y):
        self.x = x
    self.y = y

def __str__(self):
    return f"Point({self.x}, {self.y})"

def __repr__(self):
    return f"Point(x={self.x}, y={self.y})"

p = Point(1, 2)
print(p) # Point(1, 2)
print(repr(p)) # Point(x=1, y=2)

예제: 연산자 오버로딩

__add__ 등을 재정의해 객체 간 연산을 지원.

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def __add__(self, other):
    return Vector(self.x + other.x, self.y + other.y)

def __str__(self):
    return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2
print(v3) # Vector(4, 6)

 

 

1.6. 캡슐화

접근 제한

  • 공개 속성 : 이름 그대로 사용 (예: self.name)
  • 비공개 속성 : 이름 앞에 밑줄 두 개 사용 (__)
  •  
class Person:
    def __init__(self, name):
        self.__name = name # 비공개 속성

def get_name(self):
    return self.__name

def set_name(self, name):
    self.__name = name

person = Person("Alice")
print(person.get_name()) # Alice
person.set_name("Bob")
print(person.get_name()) # Bob

 

1.7. Class 변수

Python의 Class 변수(Class Variable)는 다른 프로그래밍 언어의 static 변수처럼 Class 자체에 종속되며, Class의 모든 인스턴스에서 공유되는 특징이 있습니다.

하지만 Python에는 static 키워드가 없고, Class 변수는 약간 다른 방식으로 동작합니다.

이를 더 자세히 설명하겠습니다.

Class 변수와 인스턴스 변수의 차이

Class 변수(Class Variable)
인스턴스 변수(Instance Variable)
Class 자체에 속하며, 모든 인스턴스에서 공유됩니다.
각 객체(인스턴스)마다 별도로 생성되는 변수입니다.
Class 변수는 한 번 정의되면 모든 인스턴스에서 동일한 값을 갖습니다.
객체별로 다른 값을 가질 수 있습니다.
Class 이름으로 접근하거나, 인스턴스를 통해 접근할 수 있습니다.
self를 사용하여 정의되고 접근됩니다.

예제 코드

class MyClass:
    # Class 변수
    class_variable = "Shared Variable"

    def __init__(self, value):
        # 인스턴스 변수
        self.instance_variable = value

# Class 변수와 인스턴스 변수 비교
obj1 = MyClass("Object 1 Instance Variable")
obj2 = MyClass("Object 2 Instance Variable")

print(obj1.class_variable) # "Shared Variable" (Class 변수)
print(obj2.class_variable) # "Shared Variable" (Class 변수)

print(obj1.instance_variable) # "Object 1 Instance Variable"
print(obj2.instance_variable) # "Object 2 Instance Variable"

# Class 변수를 변경하면 모든 인스턴스에서 영향을 받음
MyClass.class_variable = "Updated Shared Variable"
print(obj1.class_variable) # "Updated Shared Variable"
print(obj2.class_variable) # "Updated Shared Variable"

Class 변수의 동작 방식

Class 변수는 Class 네임스페이스에 저장되며, 모든 인스턴스가 이를 참조합니다. Class 변수에 값을 할당하거나 변경하면, 이는 Class 자체에 영향을 미칩니다.

Class 변수는 static처럼 동작하는가?

다른 언어의 static 변수와 유사하지만 Python에서는 조금 다르게 동작합니다:

공통점

  • Class 변수는 모든 인스턴스에서 공유됩니다.
  • Class 변수는 Class 이름을 통해 접근할 수 있습니다.

차이점

Class 변수의 동적 성격:

  • Python에서는 런타임에 동적으로 Class를 변경할 수 있습니다.
  • 따라서 Class 변수도 프로그램 실행 중에 추가하거나 삭제할 수 있습니다.
MyClass.new_variable = "Dynamic Variable"
print(MyClass.new_variable) # "Dynamic Variable"

 

인스턴스에서 Class 변수 오버라이딩 가능:

특정 인스턴스에서 Class 변수를 오버라이딩하면, 해당 인스턴스에서만 값이 변경되고 다른 인스턴스에는 영향을 주지 않습니다.

obj1.class_variable = "Overridden Variable"
print(obj1.class_variable) # "Overridden Variable" (인스턴스 변수로 변경)
print(obj2.class_variable) # "Updated Shared Variable" (Class 변수)

이 동작은 static 변수와 다릅니다. 다른 언어에서는 Class 변수를 인스턴스에서 변경할 수 없는 경우가 많습니다.

Class Method와 함께 사용

Class 변수는 Class Method(데코레이터 @classmethod)에서 자주 사용됩니다.

예제

class MyClass:
    counter = 0 # Class 변수

    def __init__(self):
        MyClass.counter += 1

    @classmethod
    def get_instance_count(cls):
        return cls.counter

# 객체 생성
obj1 = MyClass()
obj2 = MyClass()

# Class Method를 통해 Class 변수 확인
print(MyClass.get_instance_count()) # 출력: 2

Python의 Class 변수는 다른 언어의 static 변수와 매우 유사하게 동작하며, Class와 인스턴스 모두에서 공유됩니다.

하지만 Python에서는 동적 성격과 인스턴스 오버라이딩 가능성이라는 점에서 차이가 있습니다.

Class 변수는 프로그램 전체에서 공유해야 하는 데이터를 저장하는 데 유용합니다.

@classmethod를 활용하면 더 강력하게 사용할 수 있습니다.

 

2. 예제

 

 

Ex. 1. Class와 객체에 대해 설명해봅시다.

Class와 객체는 Python에서 객체지향 프로그래밍(OOP)의 핵심 개념입니다.

이 둘을 이해하려면 Class가 무엇이고 객체가 무엇인지 구분하는 것이 중요합니다.

1. Class(Class)란?

Class는 객체를 만들기 위한 설계도(또는 틀)입니다.

데이터(속성)와 해당 데이터와 관련된 동작(Method)을 하나의 단위로 묶어서 정의한 것입니다.

Class는 반복적으로 사용할 수 있는 객체를 생성할 수 있게 해줍니다.

특징

속성(Attribute) : Class에서 정의된 변수로, 객체의 상태를 나타냅니다.

Method(Method) : Class에서 정의된 함수로, 객체가 수행할 동작(기능)을 나타냅니다.

Class를 사용하려면 이를 기반으로 객체를 생성해야 합니다.

2. 객체(Object)란?

객체는 Class를 기반으로 만들어진 실제 사용 가능한 인스턴스입니다.

Class가 설계도라면, 객체는 그 설계도로 만든 실제 사물입니다. 객체는 Class에서 정의된 속성(데이터)과 Method(동작)를 가집니다.

특징

같은 Class로부터 만들어진 객체라도, 객체별로 서로 다른 상태(속성)를 가질 수 있습니다.

객체를 생성하는 과정을 인스턴스화(Instantiation)라고 합니다.

2.1. 생성자(Constructor)

- 역할

객체가 생성될 때 자동으로 호출되어, 객체의 초기화를 담당합니다.

주로 객체의 속성(데이터)을 초기화하거나 필요한 자원을 할당하는 데 사용됩니다.

- 특징

Python에서는 생성자를 __init__ Method로 정의합니다.

Class에서 객체가 인스턴스화될 때 호출됩니다.

첫 번째 매개변수는 항상 self이며, 이를 통해 객체 자신을 참조합니다.

2.2. 소멸자(Destructor)

- 역할

객체가 소멸될 때 자동으로 호출되어, 객체가 사용하던 자원을 정리하거나 해제하는 데 사용됩니다.

자주 사용되지는 않지만, 파일 핸들러나 데이터베이스 연결 같은 외부 자원을 관리할 때 유용합니다.

- 특징

Python에서는 소멸자를 __del__ Method로 정의합니다.

객체가 더 이상 참조되지 않고, Python의 **가비지 컬렉터(Garbage Collector)**가 객체를 소멸시킬 때 호출됩니다.

소멸자가 호출되는 시점은 명확하지 않을 수 있습니다(특히, 참조 횟수 관리에 따라).

3. 주의사항

- 소멸자 관련

명시적으로 객체를 삭제하려면 del 키워드를 사용합니다.

Python의 가비지 컬렉터는 참조 횟수가 0이 된 객체만 소멸시키므로, 객체를 소멸시키기 전에 다른 변수에서 참조 중인지 확인해야 합니다.

- 생성자 관련

생성자는 한 번만 호출됩니다. 객체를 다시 초기화하려면 별도의 초기화 Method를 만들어야 합니다.

 

 

Ex. 2. 비어있는 사람 (Human) Class를 "정의" 해보세요.

class Human:
    def __init__(self) -> None:
        pass

 

 

Ex. 3. 사람 (Human) Class의 인스턴스를 "생성" 하고 이를 areum 변수로 바인딩해보세요.

class Human:
    def __init__(self) -> None:
        pass

areum = Human()

 

 

Ex. 4. 사람 (Human) Class에 "응애응애"를 출력하는 생성자를 추가하세요.

>>> areum = Human()

응애응애

class Human:
    def __init__(self) -> None:
        print("응애응애")

areum = Human()

출력:
응애응애

 

Ex. 5. 사람 (Human) Class에 (이름, 나이, 성별)을 받는 생성자를 추가하세요.

>>> areum = Human("아름", 25, "여자")

class Human:
    def __init__(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

areum = Human("아름", 25, "여자")

 

 

Ex. 6. 5에서 생성한 인스턴스의 이름, 나이, 성별을 출력하세요. 인스턴스 변수에 접근하여 값을 출력하면 됩니다.

이름: 조아름, 나이: 25, 성별: 여자

인스턴스 변수에 접근하여 값을 가져오는 예

>>> areum.age

25

class Human:
    def __init__(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

areum = Human("아름", 25, "여자")

print(areum.name,areum.age,areum.gender)

출력:
아름 25 여자

 

Ex. 7. 사람 (Human) Class에서 이름, 나이, 성별을 출력하는 who() 메소드를 추가하세요.

>>> areum.who()

이름: 조아름, 나이: 25, 성별: 여자

class Human:
    def __init__(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

    def who(self):
        print(self.name,self.age,self.gender)

areum = Human("아름", 25, "여자")
areum.who()

출력:
아름 25 여자

 

 

Ex. 8. 사람 (Human) Class에 (이름, 나이, 성별)을 받는 setInfo 메소드를 추가하세요.

>>> areum = Human("모름", 0, "모름")

>>> areum.setInfo("아름", 25, "여자")

class Human:
    def __init__(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

    def who(self):
        print(self.name,self.age,self.gender)

    def setInfo(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

areum = Human("모름", 0, "모름")
areum.setInfo("아름", 25, "여자")
areum.who()

출력:
아름 25 여자

 

 

Ex. 9. 사람 (human) Class에 "나의 죽음을 알리지 말라"를 출력하는 소멸자를 추가하세요.

>>> areum = Human("아름", 25, "여자")

>>> del areum

나의 죽음을 알리지 말라

class Human:
    def __init__(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

    def who(self):
        print(self.name,self.age,self.gender)

    def setInfo(self,name,age,gender) -> None:
        self.name = name
        self.age = age
        self.gender = gender

    def __del__(self) -> None:
        print("나의 죽음을 알리지 말라")

areum = Human("아름", 25, "여자")
del areum

출력:
나의 죽음을 알리지 말라

 

 

Ex. 10. 아래와 같은 에러가 발생한 원인에 대해 설명하세요.

 

class OMG :
    def print() :
        print("Oh my god")

>>> >>> myStock = OMG()
>>> myStock.print()
TypeError Traceback (most recent call last)
<ipython-input-233-c85c04535b22> in <module>()
----> myStock.print()

TypeError: print() takes 0 positional arguments but 1 was given


def print():에서 self를 생략했기 때문에 Error가 발생했습니다.

 

 

 

Ex. 11. 주식 종목에 대한 정보를 저장하는 Stock Class를 정의해보세요. Class는 속성과 Method를 갖고 있지 않습니다.

class Stock:
    def __init__(self):
        pass

 

 

Ex. 12. Stock Class의 객체가 생성될 때 종목명과 종목코드를 입력 받을 수 있도록 생성자를 정의해보세요.

삼성 = Stock("삼성전자", "005930")

class Stock:
    def __init__(self,
        name : str,
        code : str):
        self.name = name
        self.code = code

삼성 = Stock("삼성전자", "005930")

 

 

Ex. 13. 객체에 종목명을 입력할 수 있는 set_name Method를 추가해보세요.

a = Stock(None, None)

a.set_name("삼성전자")

class Stock:
    def __init__(self,
                name : str,
                code : str):
        self.name = name
        self.code = code

    def set_name(self,
                name : str):
        self.name = name

a = Stock(None, None)
a.set_name("삼성전자")

 

 

 

Ex. 14. 객체에 종목코드를 입력할 수 있는 set_code Method를 추가해보세요.

a = Stock(None, None)

a.set_code("005930")

class Stock:
    def __init__(self,
                name : str,
                code : str):
        self.name = name
        self.code = code

    def set_name(self,
                name : str):
        self.name = name

    def set_code(self,
                code : str):
        self.code = code

a = Stock(None, None)
a.set_code("005930")

 

 

Ex. 15. 종목명과 종목코드를 리턴하는 get_name, get_code Method를 추가하세요. 해당 Method를 사용하여 종목명과 종목코드를 얻고 이를 출력해보세요.

class Stock:
    def __init__(self,
                name : str,
                code : str):
        self.name = name
        self.code = code

    def set_name(self,
                name : str):
        self.name = name

    def set_code(self,
                code : str):
        self.code = code

    def get_name(self) -> str:
        return self.name

    def get_code(self) -> str:
        return self.code

삼성 = Stock("삼성전자", "005930")
print(삼성.get_code(), 삼성.get_name() )

출력:
005930 삼성전자

 

 

Ex. 16. 생성자에서 종목명, 종목코드, PER, PBR, 배당수익률을 입력 받을 수 있도록 생성자를 수정하세요. PER, PBR, 배당수익률은 float 타입입니다.

class Stock:
    def __init__(self,
                name : str,
                code : str,
                PER : float,
                PBR : float,
                rate : float
                ):
        self.name = name
        self.code = code
        self.PER = PER
        self.PBR = PBR
        self.rate = rate

    def set_name(self,
                name : str):
        self.name = name

    def set_code(self,
                code : str):
        self.code = code

    def get_name(self) -> str:
        return self.name

    def get_code(self) -> str:
        return self.code

 

 

Ex. 17. 16번에서 정의한 생성자를 통해 다음 정보를 갖는 객체를 생성해보세요.

항목 정보

종목명 삼성전자

종목코드 005930

PER 15.79

PBR 1.33

배당수익률 2.83

class Stock:
    def __init__(self,
                name : str,
                code : str,
                PER : float,
                PBR : float,
                rate : float
                ):
        self.name = name
        self.code = code
        self.PER = PER
        self.PBR = PBR
        self.rate = rate

    def set_name(self,
                name : str):
        self.name = name

    def set_code(self,
                code : str):
        self.code = code

    def get_name(self) -> str:
        return self.name

    def get_code(self) -> str:
        return self.code

s = Stock("삼성전자","005930",15.79,1.33,2.83)

 

 

 

Ex. 18. PER, PBR, 배당수익률은 변경될 수 있는 값입니다. 이 값을 변경할 때 사용하는 set_per, set_pbr, set_dividend Method를 추가하세요.

class Stock:
    def __init__(self,
                name : str,
                code : str,
                PER : float,
                PBR : float,
                dividend : float
                ):
        self.name = name
        self.code = code
        self.PER = PER
        self.PBR = PBR
        self.dividend = dividend

    def set_name(self,
                name : str):
        self.name = name

    def set_code(self,
                code : str):
        self.code = code

    def set_per(self,
                per : float):
        self.PER = per

    def set_pbr(self,
                pbr : float):
        self.PBR = pbr

    def set_dividend(self,
                    dividend : float):
        self.dividend = dividend

    def get_name(self) -> str:
        return self.name

    def get_code(self) -> str:
        return self.code

s = Stock("삼성전자","005930",15.79,1.33,2.83)

 

 

 

 

Ex. 19. 17번에서 생성한 객체에 set_per Method를 호출하여 per 값을 12.75로 수정해보세요.

s.set_per(12.75)

 

 

Ex. 20. 아래의 표를 참조하여 3종목에 대해 객체를 생성하고 이를 파이썬 리스트에 저장하세요. 파이썬 리스트에 저장된 각 종목에 대해 for 루프를 통해 종목코드와 PER을 출력해보세요.

종목 = []

삼성 = Stock("삼성전자", "005930", 15.79, 1.33, 2.83)
현대차 = Stock("현대차", "005380", 8.70, 0.35, 4.27)
LG전자 = Stock("LG전자", "066570", 317.34, 0.69, 1.37)

종목.append(삼성)
종목.append(현대차)
종목.append(LG전자)

for i in 종목:
    print(i.code, i.per)        # i-> Stock 클래스의 객체를 바인딩하기 때문

 

 

 

Ex. 21. 은행에 가서 계좌를 개설하면 은행이름, 예금주, 계좌번호, 잔액이 설정됩니다. Account Class를 생성한 후 생성자를 구현해보세요. 생성자에서는 예금주와 초기 잔액만 입력 받습니다. 은행이름은 SC은행으로 계좌번호는 3자리-2자리-6자리 형태로 랜덤하게 생성됩니다.

은행이름: SC은행

계좌번호: 111-11-111111

import random

class Account:
    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3)      # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2)      # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6)      # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3  # 001-01-000001

kim = Account("김민수", 100)
print(kim.name)
print(kim.balance)
print(kim.bank)
print(kim.account_number)

 

 

 

Ex. 22. Class 변수를 사용해서 Account Class로부터 생성된 계좌 객체의 개수를 저장하세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001

        Account.account_count += 1

kim = Account("Kim", 200)
print(Account.account_count)
kim.account_count = 3
print(kim.account_count)

lee = Account("Lee", 1000)
print(Account.account_count)
print(lee.account_count)

출력:
1
3
2
2

 

 

 

Ex. 23. Account Class로부터 생성된 계좌의 개수를 출력하는 get_account_num() Method를 추가하세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001

        Account.account_count += 1

    @classmethod
    def get_account_num(c):
        print(c.account_count) # Account.account_count

kim = Account("Kim", 100)
lee = Account("Lee", 100)
kim.get_account_num()

출력:
2

 

 

 

Ex. 24. Account Class에 입금을 위한 deposit Method를 추가하세요. 입금은 최소 1원 이상만 가능합니다.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001

        Account.account_count += 1

    @classmethod
    def get_account_num(c):
        print(c.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.balance += amount

 

 

 

Ex. 25. Account Class에 출금을 위한 withdraw Method를 추가하세요. 출금은 계좌의 잔고 이상으로 출금할 수는 없습니다.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        # 3-2-6
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001
        Account.account_count += 1

    @classmethod
    def get_account_num(cls):
        print(cls.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.balance += amount

    def withdraw(self, amount):
        if self.balance > amount:
            self.balance -= amount

k = Account("kim", 100)
k.deposit(100)
k.withdraw(90)
print(k.balance)

출력:
110
​

 

 

 

Ex. 26. Account 인스턴스에 저장된 정보를 출력하는 display_info() Method를 추가하세요. 잔고는 세자리마다 쉼표를 출력하세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        # 3-2-6
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001
        Account.account_count += 1

    @classmethod
    def get_account_num(cls):
        print(cls.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.balance += amount

    def withdraw(self, amount):
        if self.balance > amount:
            self.balance -= amount

    def display_info(self):
        print("은행이름: ", self.bank)
        print("예금주: ", self.name)
        print("계좌번호: ", self.account_number)
        print("잔고: ", f"{self.balance:,}")

p = Account("파이썬", 10000)
p.display_info()

출력:
은행이름: SC은행
예금주: 파이썬
계좌번호: 760-42-611154
잔고: 10,000

:, (숫자 포맷팅)

:,는 숫자를 쉼표로 구분하여 포맷팅하는 옵션입니다.

self.balance가 숫자(정수 또는 실수)일 때, 쉼표를 사용해 3자리 단위로 구분합니다.

 

 

 

 

Ex. 27. 입금 횟수가 5회가 될 때 잔고를 기준으로 1%의 이자가 잔고에 추가되도록 코드를 변경해보세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        # 3-2-6
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001
        Account.account_count += 1

    @classmethod
    def get_account_num(cls):
        print(cls.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.balance += amount

        self.deposit_count += 1

        if self.deposit_count % 5 == 0: # 5, 10, 15
            # 이자 지금
            self.balance = (self.balance * 1.01)

    def withdraw(self, amount):
        if self.balance > amount:
            self.balance -= amount

    def display_info(self):
        print("은행이름: ", self.bank)
        print("예금주: ", self.name)
        print("계좌번호: ", self.account_number)
        print("잔고: ", self.balance)

p = Account("파이썬", 10000)
p.deposit(10000)
p.deposit(10000)
p.deposit(10000)
p.deposit(5000)
p.deposit(5000)
print(p.balance)

출력:
50500.0

 

 

 

 

Ex. 28. Account Class로부터 3개 이상 인스턴스를 생성하고 생성된 인스턴스를 리스트에 저장해보세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        # 3-2-6
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001
        Account.account_count += 1

    @classmethod
    def get_account_num(cls):
        print(cls.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.balance += amount

        self.deposit_count += 1

        if self.deposit_count % 5 == 0: # 5, 10, 15
            # 이자 지금
            self.balance = (self.balance * 1.01)

    def withdraw(self, amount):
        if self.balance > amount:
            self.balance -= amount

    def display_info(self):
        print("은행이름: ", self.bank)
        print("예금주: ", self.name)
        print("계좌번호: ", self.account_number)
        print("잔고: ", self.balance)

data = []
k = Account("KIM", 10000000)
l = Account("LEE", 10000)
p = Account("PARK", 10000)

data.append(k)
data.append(l)
data.append(p)

print(data)

출력:
[<__main__.Account object at 0x0000026123F7A750>, <__main__.Account object at 0x0000026123F7A7B0>, <__main__.Account object at 0x0000026123F7BB00>]

 

 

 

Ex. 29. 반복문을 통해 리스트에 있는 객체를 순회하면서 잔고가 100만원 이상인 고객의 정보만 출력하세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        # 3-2-6
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001
        Account.account_count += 1

    @classmethod
    def get_account_num(cls):
        print(cls.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.balance += amount

        self.deposit_count += 1

        if self.deposit_count % 5 == 0: # 5, 10, 15
            # 이자 지금
            self.balance = (self.balance * 1.01)

    def withdraw(self, amount):
        if self.balance > amount:
            self.balance -= amount

    def display_info(self):
        print("은행이름: ", self.bank)
        print("예금주: ", self.name)
        print("계좌번호: ", self.account_number)
        print("잔고: ", self.balance)

data = []
k = Account("KIM", 10000000)
l = Account("LEE", 10000)
p = Account("PARK", 10000)
data.append(k)
data.append(l)
data.append(p)

for c in data:
    if c.balance >= 1000000:
        c.display_info()

출력:
은행이름: SC은행
예금주: KIM
계좌번호: 064-20-405777
잔고: 10000000

 

 

 

Ex. 30. 입금과 출금 내역이 기록되도록 코드를 업데이트 하세요. 입금 내역과 출금 내역을 출력하는 deposit_history와 withdraw_history Method를 추가하세요.

import random

class Account:
    # class variable
    account_count = 0

    def __init__(self, name, balance):
        self.deposit_count = 0
        self.deposit_log = []
        self.withdraw_log = []

        self.name = name
        self.balance = balance
        self.bank = "SC은행"

        # 3-2-6
        num1 = random.randint(0, 999)
        num2 = random.randint(0, 99)
        num3 = random.randint(0, 999999)

        num1 = str(num1).zfill(3) # 1 -> '1' -> '001'
        num2 = str(num2).zfill(2) # 1 -> '1' -> '01'
        num3 = str(num3).zfill(6) # 1 -> '1' -> '0000001'
        self.account_number = num1 + '-' + num2 + '-' + num3 # 001-01-000001
        Account.account_count += 1

    @classmethod
    def get_account_num(cls):
        print(cls.account_count) # Account.account_count

    def deposit(self, amount):
        if amount >= 1:
            self.deposit_log.append(amount)
            self.balance += amount

            self.deposit_count += 1
                if self.deposit_count % 5 == 0: # 5, 10, 15
                    # 이자 지금
                    self.balance = (self.balance * 1.01)

    def withdraw(self, amount):
        if self.balance > amount:
            self.withdraw_log.append(amount)
            self.balance -= amount

    def display_info(self):
        print("은행이름: ", self.bank)
        print("예금주: ", self.name)
        print("계좌번호: ", self.account_number)
        print("잔고: ", self.balance)

    def withdraw_history(self):
        for amount in self.withdraw_log:
            print(amount)

    def deposit_history(self):
        for amount in self.deposit_log:
            print(amount)

k = Account("Kim", 1000)
k.deposit(100)
k.deposit(200)
k.deposit(300)
k.deposit_history()

k.withdraw(100)
k.withdraw(200)
k.withdraw_history()

출력:
100
200
300
100
200

 

 

 

Ex. 31. 다음 코드가 동작하도록 차 Class를 정의하세요.

>> car = (2, 1000)

>> car.바퀴

2

>> car.가격

1000

class 차:
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

car = 차(2, 1000)
print(car.바퀴)
print(car.가격)

출력:
2
1000

 

 

 

 

Ex. 32. 차 Class를 상속받은 자전차 Class를 정의하세요.

class 자전차(차):
    def __init__(self):
        pass

 

 

Ex. 33. 다음 코드가 동작하도록 자전차 Class를 정의하세요. 단 자전차 Class는 차 Class를 상속받습니다.

>> bicycle = 자전차(2, 100)

>> bicycle.가격

100

class 차:
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

class 자전차(차):
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

bicycle = 자전차(2, 100)
print(bicycle.가격)

출력:
100

 

 

 

Ex. 34. 다음 코드가 동작하도록 자전차 Class를 정의하세요. 단 자전차 Class는 차 Class를 상속받습니다.

>> bicycle = 자전차(2, 100, "시마노")

>> bicycle.구동계

시마노

class 차:
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

class 자전차(차):
    def __init__(self, 바퀴, 가격, 구동계):
        super().__init__(바퀴, 가격)
        #차.__init__(self, 바퀴, 가격)
        self.구동계 = 구동계

bicycle = 자전차(2, 100, "시마노")
print(bicycle.구동계)
print(bicycle.바퀴)

출력:
시마노
2

 

 

Ex. 35. 다음 코드가 동작하도록 차 Class를 상속받는 자동차 Class를 정의하세요.

>> car = 자동차(4, 1000)

>> car.정보()

바퀴수 4

가격 1000

class 차:
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

class 자동차(차):
    def __init__(self, 바퀴, 가격):
        super().__init__(바퀴, 가격)

    def 정보(self):
        print("바퀴수 ", self.바퀴)
        print("가격 ", self.가격)

car = 자동차(4, 1000)
car.정보()

출력:
바퀴수 4
가격 1000

 

 

 

Ex. 36. 다음 코드가 동작하도록 차 Class를 수정하세요.

>> bicycle = 자전차(2, 100, "시마노")

>> bicycle.정보()

바퀴수 2

가격 100

class 차:
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

    def 정보(self):
        print("바퀴수 ", self.바퀴)
        print("가격 ", self.가격)

class 자동차(차):
    def __init__(self, 바퀴, 가격):
        super().__init__(바퀴, 가격)

class 자전차(차):
    def __init__(self, 바퀴, 가격, 구동계):
        super().__init__(바퀴, 가격)
        self.구동계 = 구동계

bicycle = 자전차(2, 100, "시마노")
bicycle.정보()

출력:
바퀴수 2
가격 100

 

 

 

Ex. 37. 자전차의 정보() Method로 구동계 정보까지 출력하도록 수정해보세요.

>> bicycle = 자전차(2, 100, "시마노")

>> bicycle.정보()

바퀴수 2

가격 100

구동계 시마노

class 차:
    def __init__(self, 바퀴, 가격):
        self.바퀴 = 바퀴
        self.가격 = 가격

    def 정보(self):
        print("바퀴수 ", self.바퀴)
        print("가격 ", self.가격)

class 자동차(차):
    def __init__(self, 바퀴, 가격):
        super().__init__(바퀴, 가격)

class 자전차(차):
    def __init__(self, 바퀴, 가격, 구동계):
        super().__init__(바퀴, 가격)
        self.구동계 = 구동계

    def 정보(self):
        super().정보()
        print("구동계 ", self.구동계)

bicycle = 자전차(2, 100, "시마노")
bicycle.정보()

출력:
바퀴수 2
가격 100
구동계 시마노

 

 

 

Ex. 38. 다음 코드의 실행 결과를 예상해보세요.

class 부모:
    def 호출(self):
        print("부모호출")

class 자식(부모):
    def 호출(self):
        print("자식호출")

나 = 자식()
나.호출()

출력:
자식호출


Class 상속 구조:

자식 Class는 부모 Class를 상속받았습니다.

자식 Class에서 호출 Method를 오버라이딩(Overriding) 했습니다. 즉, 부모 Class의 호출 Method를 재정의했습니다.

Method 탐색 순서 (MRO, Method Resolution Order):

Python은 Method를 호출할 때 자식 Class에서 먼저 해당 Method를 탐색합니다.

나.호출()을 실행하면 Python은 자식 Class에서 호출 Method를 찾아 실행합니다.

자식 Class에 호출 Method가 있으므로 부모 Class의 호출 Method는 호출되지 않습니다.

결론:

자식 Class의 호출 Method가 실행되어 print("자식호출")이 출력됩니다.

추가: 부모 Method를 호출하려면?

만약 자식 Class에서 부모 Class의 호출 Method를 실행하고 싶다면 **super()**를 사용할 수 있습니다.


 

 

 

Ex. 39. 다음 코드의 실행 결과를 예상해보세요.

class 부모:
    def __init__(self):
        print("부모생성")

class 자식(부모):
    def __init__(self):
        print("자식생성")

나 = 자식()

출력:
자식생성


자식 Class에서 __init__() Method 정의

자식 Class는 부모 Class를 상속받았지만, __init__() Method를 오버라이딩했기 때문에 부모 Class의 __init__() Method는 자동으로 호출되지 않습니다.

따라서, 자식 Class의 __init__() Method만 실행됩니다.

부모 Class의 __init__() 호출 여부

상속 관계에서는 자식 Class가 생성될 때 부모 Class의 생성자도 호출되길 원한다면, 자식 Class의 __init__()에서 명시적으로 부모의 __init__()를 호출해야 합니다.

수정: 부모의 __init__()를 호출하고 싶을 때

super()를 사용하여 부모 Class의 __init__()를 호출할 수 있습니다:


 

 

Ex. 40. 다음 코드의 실행 결과를 예상해보세요.

class 부모:
    def __init__(self):
        print("부모생성")

class 자식(부모):
    def __init__(self):
        print("자식생성")
        super().__init__()

나 = 자식()

출력:
자식생성
부모생성

도움이 되셨으면 좋겠네요.

다른 강좌도 참고하시면 좋을 것 같습니다. 각 강좌의 Link는 아래를 참고하시기 바랍니다.

 

 

Python 강좌 01 - print()

 

Python 강좌 02 - 변수

 

Python 강좌 03 - 문자열

 

Python 강좌 04 - List

 

Python 강좌 05 - Tuple

 

Python 강좌 06 - Dictionary

 

Python 강좌 07 - 분기문

 

Python 강좌 08 - 반복문

 

Python 강좌 09 - 함수

 

Python 강좌 10 - Module

 

Python 강좌 11 - Class

 

Python 강좌 12 - File & Exception

728x90

'Development Tip' 카테고리의 다른 글

Naver Cloud Platform의 API 인증키 발행방법  (0) 2025.01.06
Python 강좌 12 - File & Exception  (0) 2024.12.23
Python 강좌 10 - Module  (0) 2024.12.23
Python 강좌 09 - 함수  (0) 2024.12.23
Python 강좌 08 - 반복문  (0) 2024.12.23