Everything in Python is an object, and almost everything has attributes and methods.
파이썬에는 부울, 정수, 배열, 딕셔너리, 함수, 프로그램 등 모든 것이 객체이다. 파이썬에서 object라 불리는 것들은 모두 변수에 할당될 수 있고, 함수의 인자로 넘겨질 수 있는 것들이다. 그러므로 파이썬에 나오는 모든 것들은 object이다.
id()
함수를 이용해 객체의 identity를 확인해보자.
id()
함수는 파이썬 내장함수로, 프로그램이 돌아가는 동안 객체의 고유값(idendity)를 반환한다. 이는 메모리 주소 혹은 객체마다 부여된 유일한 고유값이다.
얕은 복사: 원본 데이터는 그대로 두고, 참조하는 데이터의 id만을 복사하는 것. 원본 객체의 주소를 복사한다.
깊은 복사: 원본 객체의 값을 복사한다.
객체를 직접 설계하기 위해서는 class
키워드를 이용한다.
class Car:
pass
class Car():
pass
#id(Car)는 여러번 호출해도 같은 값이 얻어집니다.
print(id(Car))
print(id(Car))
#id(Car())는 Car()가 호출될 때마다 다른 값이 얻어집니다.
print(id(Car()))
print(id(Car()))
# 두 객체의 type을 살펴봅니다.
print(type(Car))
print(type(Car()))
94105565323968
94105565323968
139862188733440
139862188733392
<class 'type'>
<class '__main__.Car'>
위 코드를 보면 Car
라는 클래스 자체도 객체이고, Car()
를 호출할 때마다 새로운 객체가 생성한다.
이는 둘다 id가 존재하기 때문이다.
하지만, id(Car)
은 동일한 값을 가지지만
id(Car())
는 호출될 때마다 다른 id값을 가진다.
Car
클래스는 type
유형의 객체이다. Car()
가 호출되면 Car
타입의 객체가 생성된다.
인스턴스화: 클래스를 객체로 만들어주는 작업
Instance
: An individual object of a certain class.
mycar = Car()
mycar2 = Car()
print(id(mycar))
print(id(mycar2))
139862188735840
139862188736272
mycar
변수에 Car
클래스의 인스턴스를 할당한다.
인스턴스가 생성될 때마다 객체를 할당받은 변수들에게 다른 id가 부여되는 것을 확인할 수 있다.
인스턴스 방법이 함수를 호출하는 문법과 비슷할 것이다.
이를 구분하는 방법은 PEP8 표기법에 따라 클래스명과 함수명을 다르게 표기한다.
클래스에는 속성과 메서드가 있다.
속성: Class attributes are variables of a class that are shared between all of its instances.
메서드: A class method is a method which is bound to the class and not the object of the class. It is represented by def
keyword
self
as a first argumentLet's take a look
class Car:
color = 'red'
category = 'sports car'
def drive(self):
print("I'm driving")
def accel(self, speed_up, current_speed=10):
self.speed_up = speed_up
self.current_speed = current_speed + speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
mycar.drive()
코드는 인터프리터 내부에서는 Car.drive(mycar)
로 동작한다. self
라는 단어는 클래스를 인스턴스화 한 인스턴스 객체를 가리킨다.
메서드를 호출할 때, 우리는 명시적으로 인자를 넣지 않지만 파이썬 내부적으로는 (= 파이선 인터프리터에서는) 인자 한 개를 사용하고 있고, 그 인자는 파이썬 클래스에 의해 선언된 객체 자신(self
)이다.
mycar = Car()
mycar.drive()
Car.drive(mycar)
I'm driving
I'm driving
코드 구현을 도식적으로 표현하면 다음과 같다.
이런 구현을 self
인자가 가능하게 해주는 것이다!
추가로, 클래스 메서드를 정의할 때 self
인자를 사용하지 않으면 에러가 발생한다.
class Test:
def run1(self):
print("run1")
def run2():
print("run2")
t = Test()
t.run1()
t.run2()
run1
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_47/2409363125.py in <module>
8 t = Test()
9 t.run1()
---> 10 t.run2()
TypeError: run2() takes 0 positional arguments but 1 was given
위와 같은 이유로, 속성으로 사용하고 싶은 변수는 self.
를 써준다.self
인자를 통해 선언된 객체의 값이라는 의미다.
객체 내에서 self
를 사용하면 인스턴스 객체의 고유한 속성을 나타낼 수 있다. 클래스가 아닌 self
, 즉 인스턴스화된 객체 자신의 속성을 의미한다.
참고로, 클래스 메서드 내부에서 self.
가 없이 일반 변수처럼 선언된 변수는 메서드 내부에서만 작동한다.
class Test2:
def run1(self, a):
self.a = float(a) * 10
print(self.a)
def run2(self, b):
b = float(b) + 10
print(self.b)
t = Test2()
t.run1(1)
t.run2(1) #error
10.0
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/tmp/ipykernel_47/1902783235.py in <module>
10 t = Test2()
11 t.run1(1)
---> 12 t.run2(1) #error
/tmp/ipykernel_47/1902783235.py in run2(self, b)
6 def run2(self, b):
7 b = float(b) + 10
----> 8 print(self.b)
9
10 t = Test2()
AttributeError: 'Test2' object has no attribute 'b'
__init__
클래스에 의해 만든 인스턴스 객체의 속성값을 초기화시켜준다.
던더(Double Under)이닛
이라고 발음한다
__init__
메서드 안에 인자를 전달함으로써 인스턴스 객체의 속성을 초기화 할 수 있다.__init__
메서드 안에 정의된 변수는 클래스를 인스턴스화할 때 값을 지정할 수 있다.__init__
함수는 생성자(constructor)라고 한다.__init__
역시 def
키워드로 정의하며, self 인자를 넣어준다.class Car2:
def __init__(self, color, category):
self.color = color
self.category = category
def drive(self):
print("I'm driving")
def accel(self, speed_up, current_speed=10):
self.speed_up = speed_up
self.current_speed = current_speed + self.speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
#인스턴스 객체 선언
car1 = Car()
car2 = Car2('yellow', 'sedan')
car1.color, car2.color
('red', 'yellow')
car1.category, car2.category
('sports car', 'sedan')
이번엔 클래스 내 변수를 선언하는 2가지 방법을 알아보자.
1. 보통 변수와 동일하게 변수명을 쓰고 값을 할당하기
2. __init__
메서드 안에 self.
와 함께 설정하기
class Car:
Manufacture = "India"
def __init__(self, color, category='sedan'):
self.color = color
self.category = category
위 코드에서 Manufacture
같은 변수를 클래스 변수
라고 하고,
self.color
와 같은 변수를 인스턴스 변수
라고 한다.
__init__()
. 안에서 self
를 사용해 선언된 변수를 의미함 기존 클래스의 기능은 유지한 채 maker
속성만 추가된 새로운 클래스 NewCar
를 선언하고 싶다면, 클래스의 상속 기능을 이용하면 된다.
class Car:
Manufacture = "India"
def __init__(self, color='red', category='sedan'):
self.color = color
self.category = category
def drive(self):
print("I'm driving")
def accel(self, speed_up, current_speed=10):
self.speed_up = speed_up
self.current_speed = current_speed + self.speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
상속은 소괄호 안에 상속받을 클래스 이름을 적어주면 된다.
class NewCar(Car):
pass
car = NewCar()
car.drive()
car.accel(10)
I'm driving
speed up 10 driving at 20
class NewCar(Car):
maker = 'Porsche'
car = NewCar()
car.maker
'Porsche'
이렇게, 부모 클래스를 상속받은 뒤 원하는 속성을 추가해주면 된다.
클래스를 잘 상속하기 위한 3가지 사용법을 살펴보자.
1. 메서드 추가하기(add)
2. 메서드 재정의하기(override)
3. 부모 메서드 호출하기(super()
)
자식 클래스에 새로운 메서드
를 추가할 수 있다.
class NewCar(Car):
def fly(self):
print("I'm flying!! This is the new car!!")
자식 클래스의 drive()
메서드에 다른 문구가 출력되게 매서드를 정의해보자.
이렇게 기존에 있는 메서드를 변경하는 것은 메서드 오버라이드(재정의)
라고 한다.
class NewCar(Car):
def fly(self):
print("I'm flying!! This is the new car!!")
def drive(self):
print("I'm driving and can fly")
super()
는 파이썬 내장함수로, 자식 클래스에서 부모 클래스의 메서드를 호출하고 싶을 때 사용한다.
super()
함수 문법
super()함수는 이렇게 사용한다.
def (부모클래스의)메서드이름():
super().메서드이름()
자식 클래스는 부모 클래스의 메서드를 그대로 이어받을 수 있는데, 왜 굳이 메서드를 호출해야 할까? super()
는 어떤 경우에 필요할까?
아래 코드를 보고 어떤 경우에 필요한지 알아보자.
class NewCar(Car):
def __init__(self, color, category, maker):
super().__init__(color, category)
self.maker = maker
def fly(self):
print("I'm flying!! This is the new car!!")
def accel(self, speed_up, level=1, current_speed=10):
self.boost[level] = {1 : 0, 2 : 30, 3 : 50}
self.speed_up = speed_up + self.boost[level]
self.current_speed = current_speed + self.speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
Q. 그런데 왜 이렇게 정의하지 않았을까?
class NewCar(Car):
def __init__(self, color, category, maker):
self.color = color
self.category = category
self.maker = maker
A. 지금은 부모 클래스에서 color
를 이름으로 받지만 나중에 RGB값으로 받도록 바꾸고 싶다고 하자. 이때 만약 메서드를 위 코드와 같이 오버라이드 했다면 상속받은 클래스 하나하나의 초기화 코드를 변경해주어 한다.
이렇게 super()
를 사용해 변수를 초기화하면 부모 클래스만 변경하면 된다!!
즉, 부모 클래스의 변경사항이 그대로 자식 클래스에 반영된다.
class Car:
Manufacture = "India"
def __init__(self, color='red', category='sedan'):
self.color = color
self.category = '2020Y '+ category
class NewCar(Car):
def __init__(self, color, category, maker):
super().__init__(color, category)
self.maker = maker
newcar = NewCar('red','sports car', 'Kia')
print(newcar.category)
2020Y sports car
__init__
get_inputs()
함수를 호출해서 받자n = get_inputs()
mydice = FunnyDice(n)
def main():
n = get_inputs()
mydice = FunnyDice(n)
mydice.throw()
print("행운의 숫자는? {}".format(mydice.getval()))
class FunnyDice:
def __init__(self, n):
self.n = n
def throw(self):
pass
def getval(self):
pass
def setval(self, val):
pass
# funnydice.py
from random import randrange
class FunnyDice:
def __init__(self, n=6):
self.n = n
self.options = list(range(1, n+1))
self.index = randrange(0, self.n)
self.val = self.options[self.index]
def throw(self):
self.index = randrange(0, self.n)
self.val = self.options[self.index]
def getval(self):
return self.val
def setval(self, val):
if val <= self.n:
self.val = val
else:
msg = "주사위에 없는 숫자입니다. 주사위는 1 ~ {0}까지 있습니다. ".format(self.n)
raise ValueError(msg)
def get_inputs():
n = int(input("주사위 면의 개수를 입력하세요: "))
return n
def main():
n = get_inputs()
mydice = FunnyDice(n)
mydice.throw()
print("행운의 숫자는? {0}".format(mydice.getval()))
if __name__ == '__main__':
main()