클래스 class
1. module vs class vs instance(객체, object)
- module은 확장자가 py파일(함수(def), 변수, class등이 포함된 파일)
- class : 새로운 객체(instance, object)를 만드는 설계도
- instance : class로 부터 생성된 객체를 말한다.
2. 클래스 내부의 메서드(함수)선언
- 메서드는 일반함수와 동일하게 선언하지만 다른 점은 반드시 첫 번째인수로 `self`로 정의해야 한다.
class 클래스명():
def 메서드명(self):
# self는 java에서 this와 동일한 역할 즉, 객체 자기자신을 의미한다.
pass
def 함수명():
pass
- 즉, 메서드는 `def 메서드명(self)`의 형태로 선언해야 한다.
- self는 `객체 자기 자신을 참조`하는 것으로 인스턴스(객체) 자기 자진을 가리킨다.
- 정확하게는 `객체 자기 자신이 참조하고 있는 메모리주소`를 의미한다.
- 각각의 인스턴스(객체)는 self를 이용해서 자신에게 접근할 수 있다.
3. 클래스메서드(@classmethod)
- 파이썬에 `@는 데코레이터 decorator`라고 명칭한다. Java에서 annotation의 역할을 수행한다.
- 클래스메서드는 특정 객체에 소속된 것이 아니라 `클래스로 생성된 모든 인스턴스에서 공통으로 사용`되는 메서드이다.
- 클래스메서드는 `첫 번째인수로 cls로 전달되는 메서드`이다.
- 즉, `def 메서드명(cls)`의 형태로 정의되는 메서드이다.
- 클래스메서드는 인스턴스(객체)에 소속된 멤버가 아니라 `클래스에 소속된 멤버로서 class namespace에 존재하는 메서드`이다.
- 클래스메서드에 접근하기 위해서는 `클래스명.메서드()`형태로 접근한다.
- 첫 번째 인수인 cls는 클래스의 인스턴스를 자동으로 전달받는 인자값이다.
4. 생성자(constroctor, `__init__()`)함수
- 파이썬에서는 `객체(인스턴스)가 생성될 때 자동으로 실행되는 메서드로서 생성자메서드`라고 한다.
- 파이썬에서는 Java와는 다르게 `오직 한 개의 생성자메서드만 정의`할 수 있다.
- 만약, `__init__()`가 여러개 선언됐을 경우에는 에러는 발생하지 않지만 마지막에 선언된 메서드가 생성자메서드가 된다.
5. 소멸자(`__de;__()`)함수
- 파이썬에서는 메모리나 기타 자원을 자동으로 관리한다.
- 객체(인스턴스)가 메모리에서 소멸될 때 자동으로 실행되는 메서드이다.
- 만약, 객체가 소멸될 때 특정의 수행작업이 필요할 경우에는 소멸자메서드에 정의해 주면된다.
- 실행할 로직이 없다면 특별히 정의하지않아도 된다.
6. 상속(Inheritance)
- 파이썬은 `다중상속이 가능`하다.
- 상속의 이점은 정보공유와 코드의 재사용이다.
- 자식클래스는 상속을 해준 부모의 속성(필드, 메서드)등을 사용할 수 있다.
- 자식클래스에서 필요한 속성을 추가하거나 부모의 기능(메서드)을 재정의(Override)해서 사용할 수 있다.
7. Override
- 자식클래스(child, sub)에서 부모(parent, super)클래스에 정의된 메서드를 자식클래스에서 재정의하는 것을 오버라이드라고 한다.
8. 다형성(Polymorphism)
- 상속관계내의 다른 클래스들의 인스턴스들이 동일 멤버함수를 호출하는 것에 대해 다르게 실행되는 기능을 말한다.
- 적은 코딩으로 다양한 형태의 인스턴스를 생성할 수 있다.
- 코드의 가독성을 높인다.
# 1. 클래스를 정의하는 방법
class Korean:
# 1) 속성(properties) : 변수(객체멤버)
nation = ''
name = ''
# 2) 생성자
def __init__(self):
print('1. Korean클래스로부터 홍길동객체가 생성되었습니다!')
def __init__(self, nation, name):
print('2. Korean클래스로부터 홍길동객체가 생성되었습니다!')
def __init__(self, nation, name):
self.nation = nation
self.name = name
print(f'3. Korean클래스로부터 {self.name}객체가 생성되었습니다!')
# 3) 메서드
def method1(self):
print('Korean에서 생성된 객체로부터 method1(메서드)이 호출되었습니다!!')
def method2(): # class 내부에서만 사용가능한 함수 즉, 클래스멤버
print('클래스내부에서 method2(함수)가 호출되었습니다!')
method2()
-----------------------------------------------------------
클래스내부에서 method2(함수)가 호출되었습니다!
sohyang = Korean('한국', '소향')
print(type(sohyang), sohyang.nation, sohyang.name)
sohyang.method1()
# sohyang.method2() # 즉, 클래스내부에서만 호출 가능 TypeError: Korean.method2() takes 0 positional arguments but 1 was given,
-------------------------------------------------------------------------
3. Korean클래스로부터 소향객체가 생성되었습니다!
<class '__main__.Korean'> 한국 소향
Korean에서 생성된 객체로부터 method1(메서드)이 호출되었습니다!!
class Korean2():
nation = '대한민국'
name = '홍길동'
# 기본생성자함수 : 정의하지 않아도 자동생성된다.
# def __init__(self):
# pass
def method1(self):
return 'method1'
def method2():
return 'method2'
kim = Korean2()
print(type(kim), kim.nation, kim.name)
print()
steve = Korean2()
steve.nation = '미국'
steve.name = '스티브'
print(type(steve), steve.nation, steve.name)
-----------------------------------------------------------------------------
<class '__main__.Korean2'> 대한민국 홍길동
<class '__main__.Korean2'> 미국 스티브
# 2. 객체를 만드는 방법
# 변수명 = 생성자함ㅅ 즉, 변수명 = 클래스명()
# 변수는 객체변수 or 인스턴스변수
# Korean2()는 생성자함수라고 하고 __init__()를 호출한다.
kor1 = Korean2()
kor2 = Korean2()
# kor1, kor2는 참조변수이기 때문에 객체가 저장되어 있는 메모리주소를 참조한다.
print(f'kor1의 메모리주소 = {id(kor1)}, {kor1.nation}, {kor1.name}')
print(f'kor2의 메모리주소 = {id(kor2)}, {kor2.nation}, {kor2.name}')
print(f'kor1 == kor2의 결과 : {kor1 == kor2}')
----------------------------------------------------------------
kor1의 메모리주소 = 1959981542160, 대한민국, 홍길동
kor2의 메모리주소 = 1959981538192, 대한민국, 홍길동
kor1 == kor2의 결과 : False
# 3. 객체의 속성(변수, 메서드)에 접근하는 법
# 객체변수.속성
print(f'kor1 객체의 속성 : {kor1.nation}, {kor1.name}')
print(f'kor1객체의 {kor1.method1()}이 호출되었습니다!')
# print(f'kor1객체의 {kor1.method2()}는 객체멤버가 아니라 클래스멤버이기 때문에 객체에서 호출할 수 없습니다!')
--------------------------------------------------------------------
kor1 객체의 속성 : 대한민국, 홍길동
kor1객체의 method1이 호출되었습니다!
# 3. 다형성
class Person:
nation = ''
name = ''
# 1) 다형성 - 외부에서 객체의 속성을 정의
sonny = Person()
sonny.nation = "대한민국"
sonny.name = "손흥민"
steve = Person()
steve.nation = '미국인'
steve.name = '스티브'
print(f'한국인, {id(sonny)}, {sonny.nation}, {sonny.name}')
print(f'미국인, {id(steve)}, {steve.nation}, {steve.name}')
----------------------------------------------------------------
한국인, 1959980705936, 대한민국, 손흥민
미국인, 1959979941456, 미국인, 스티브
# 2) 다형성 - 내부에서 객체의 속성을 정의(생성자함수)
class Person2:
# nation = ''
# name = ''
def __init__(self, nation, name):
# 변수를 선언하지하지 않고 self.변수로 속성(변수)를 정의할 수 있다.
self.nation = nation
self.name = name
hong = Person2('대한민국', '홍길동')
paul = Person2('USA', 'paul')
print(f'한국인, {id(hong)}, {hong.nation}, {hong.name}')
print(f'미국인, {id(paul)}, {paul.nation}, {paul.name}')
-----------------------------------------------------------
한국인, 1959982169360, 대한민국, 홍길동
미국인, 1959972572816, USA, paul
생성자함수로 정의한 클래스
# 1. __init__() : 생성자함수 vs 일반함수
class Korean:
def __init__(self):
print('생성자함수가 호출되었습니다!')
# return '문자열'
def greeting(self):
print('메서드가 호출되었습니다!')
kor1 = Korean() # 생성자함수가 호출
kor1.greeting()
print()
# 생성자함수가 아니라 일반함수로 호출하는 경우
kor2 = kor1.__init__(); # __init__()로 호출하면 생성자함수가 아니라 일반함수로 호출되었기 때문에 return이 없기 때문에 None
# kor2.greeting() # kor2는 생성자가 아니라 일반함수(__init__)가 저장되어 있기 때문에 greeting은 호출할 수 없다.
print(type(kor1), kor1)
print(type(kor2), kor2)
----------------------------------------------------------------------
생성자함수가 호출되었습니다!
메서드가 호출되었습니다!
생성자함수가 호출되었습니다!
<class '__main__.Korean'> <__main__.Korean object at 0x000001C857D3C350>
<class 'NoneType'> None
# 2. 클래스멤버변수
class Korean2:
hello = '안녕하세요'
def __init__(self):
# hello = 'Hello Python!!' # hello - 생성자함수에서만 사용가능한 지역변수
self.hello = 'Hello Python!!' # hello - 클래서 전역에서 사용가능한 전역변수
pass
def greeting(self):
print(self.hello)
kor3 = Korean2()
kor3.greeting()
print(kor3.hello)
-------------------------------------------------------------------------
안녕하세요
안녕하세요
# 3. 메서드오버라이드
# 1) object.__str__()
%reset -f
class Person:
nation = '대한민국'
name = str()
age = int()
height = float()
weight = float()
def toString(self): # python에서는 toString메서드가 없다. 사용자가 정의한 메서드
print(f'국적={self.nation}, 이름={self.name}, 나이={self.age}, 신장={self.height}, 체중={self.weight}')
sonny = Person()
sonny.name = "손흥민"
sonny.age = 32
sonny.height = 183.5
sonny.weight = 80.3
print(sonny.toString())
# __str__()은 object에서 상속받은 메서드로서 객체의 정보를 출력해 주는 메서드이다.
# __str__()은 객체가 저장되어 있는 메모리주소를 16진수(0x000001C857C30450)를 리턴한다.
# java의 toString()메서드의 역할을 수행하는 메서드
print(sonny.__str__())
print()
print(dir(Person))
-------------------------------------------------------------------------------
국적=대한민국, 이름=손흥민, 나이=32, 신장=183.5, 체중=80.3
None
<__main__.Person object at 0x000001C858470950>
['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'height', 'name', 'nation', 'toString', 'weight']
%reset -f
# 2) Person.__str__()
class Person:
nation = '대한민국'
name = str()
age = int()
height = float()
weight = float()
def __str__(self): # object에서 상속받은 __str__()을 재정의(overriding)
return f'국적={self.nation}, 이름={self.name}, 나이={self.age}, 신장={self.height}, 체중={self.weight}'
sonny = Person()
sonny.name = "손흥민"
sonny.age = 32
sonny.height = 183.5
sonny.weight = 80.3
print(sonny.__str__())
sohyang = Person()
sohyang.name = '소향'
sohyang.age = 44
sohyang.height = 165.5
sohyang.weight = 58.4
print(sohyang.__str__())
국적=대한민국, 이름=손흥민, 나이=32, 신장=183.5, 체중=80.3
국적=대한민국, 이름=소향, 나이=44, 신장=165.5, 체중=58.4
생성자와 소멸자
# 1. 생성자
# 1) 기본생성자와 중복생성자
# 생성자의 중복을 불허, 하지만 중복선언이 될 경우에는 문법에러는 발생하지 않지만
# 맨 마지막에 선언된 생성자가 그 클래스의 생성자함수가 된다.
%reset -f
class Korean:
nation = '대한민국'
def __init__(self):
print('기본생성자가 호출되었습니다!')
def __init__(self, name, age, height, weight):
self.name = name
self.age = age
self.height = height
self.weight = weight
def __str__(self):
return f'국적={self.nation}, 이름={self.name}, 나이={self.age}, 신장={self.height}, 체중={self.weight}'
# kim = Korean()
sonny = Korean('손흥민', 32, 185.5, 80.5)
sonny.__str__()
'국적=대한민국, 이름=손흥민, 나이=32, 신장=185.5, 체중=80.5'
# 2) 초기값을 가진 생성자
%reset -f
class Korean:
def __init__(self, nation='대한민국', name=None, age=1000, height=None, weight=None):
self.nation = nation
self.name = name
self.age = age
self.height = height
self.weight = weight
def __str__(self):
return f'국적={self.nation}, 이름={self.name}, 나이={self.age}, 신장={self.height}, 체중={self.weight}'
kim = Korean()
print(kim.__str__())
# 주의사항 - 초기값을 설정하는 경우에는 데이터에 맞는 순서대로 호출해야 한다.
# 즉, 초기값을 전달하는 경우, 매개변수의 순서와 동일하게 전달되어야 한다.
sonny = Korean('손흥민', 32, 185.5, 80.5)
print(sonny.__str__())
steve = Korean('미국', 32, 185.5, 80.5, 'Steve')
print(steve.__str__())
# 순서를 사용하지 않고 매개변수의 이름으로 매핑
hong = Korean(name="홍길동", height=160.0, weight=55.9)
print(hong.__str__())
lee = Korean(name="이길동", height=160.0, weight=55.9, age=1)
print(lee.__str__())
국적=대한민국, 이름=None, 나이=1000, 신장=None, 체중=None
국적=손흥민, 이름=32, 나이=185.5, 신장=80.5, 체중=None
국적=미국, 이름=32, 나이=185.5, 신장=80.5, 체중=Steve
국적=대한민국, 이름=홍길동, 나이=1000, 신장=160.0, 체중=55.9
국적=대한민국, 이름=이길동, 나이=1, 신장=160.0, 체중=55.9
# 3) 소멸자
%reset -f
class Korean:
# 생성자
def __init__(self, nation='대한민국', name=None, age=None, height=None, weight=None):
self.nation = nation
self.name = name
self.age = age
self.height = height
self.weight = weight
# 소멸자
def __del__(self):
print('소멸자메서드가 호출되었습니다')
def __str__(self):
return f'국적={self.nation}, 이름={self.name}, 나이={self.age}, 신장={self.height}, 체중={self.weight}'
소멸자메서드가 호출되었습니다
sonny = Korean(name='손흥민', age=32, height=185.5, weight=80.5)
print(sonny) # __str__() 생략시 객체명으로 자동호출
print(id(sonny))
print()
sonny = 100 # 객체소멸자가 호출
print(sonny)
print(id(sonny))
국적=대한민국, 이름=손흥민, 나이=32, 신장=185.5, 체중=80.5
1959981751376
소멸자메서드가 호출되었습니다
100
140737447382920
클래스 내부메서드가 호출되었습니다!!
정적(static)메서드가 호출되었습니다!!
# 1. 객체생성없이 외부에서 메서드호출하기
# _Class.xxx() # 호출불가
_Class.yyy() # 정적메서드 호출가능
_Class.zzz() # 클래스메서드 호출가능
_Class.aaa() # 클래스내부메서드 호출가능
정적(static)메서드가 호출되었습니다!!
클래스(clas)메서드가 호출되었습니다!!
클래스 내부메서드가 호출되었습니다!!
# 2. 객체생성후 객체의 메서드 호출하기
# 객체로 정적메서드(yyy)와, 클래스메서드(zzz)를 접속할 수 있다.
# 이 점이 java처럼 완벽한 은닉화를 구현할 수 없다.
obj = _Class()
obj.xxx() # 호출가능
obj.yyy() # 호출가능
obj.zzz() # 호출가능
# obj.aaa() # 호출불가
객체(인스턴스)메서드가 호출되었습니다!!
정적(static)메서드가 호출되었습니다!!
클래스(clas)메서드가 호출되었습니다!!
# 3. static method 와 일반메서드 재정의
# 동일이름의 메서드가 중복선언 될 경우, 생성자함수처럼 맨 마지막에 정의된 메서드가 적용된다.
%reset -f
class Calc:
@staticmethod
def add(a, b):
return f'static method add() 호출결과 : {a} + {b} = {a+b}'
@staticmethod
def mul(a, b):
"""정적메서드 mul(a,b)"""
return f'static method mul() 호출결과 : {a} * {b} = {a*b}'
def mul(a, b, c):
"""내부메서드"""
return f'inner method mul() 호출결과 : {a} * {b} = {a*b}'
@staticmethod
def mul(a, b, c):
"""정적메서드 mul(a,b,c)"""
return f'static method mul(a,b,c) 호출결과 : {a} * {b} * {c} = {a*b*c}'
print(dir(Calc))
Calc.mul?
Calc.mul(10, 10, 10)
'static method mul(a,b,c) 호출결과 : 10 * 10 * 10 = 1000'
# 4. static variable
%reset -f
class Korean:
# 클래스멤버, 클래스변수, 정확하게는 static변수
nation = "대한민국"
@staticmethod
def toString():
print('정적메서드 toString()이 호출되었습니다!!')
print(f'이 객체의 국적속성의 값은 {Korean.nation}')
# print(f'이 객체의 국적속성의 값은 {nation}') # self.nation와 동일의미
# print(f'이 객체의 국적속성의 값은 {self.name}')
# print(f'이 객체의 국적속성의 값은 {self.age}')
# print(f'이 객체의 국적속성의 값은 {self.addr}')
def __init__(self, name=None, age=None, addr=None):
# 객체(인스턴스, 일반)변수 즉, 객체에 소속된 객체멤버
self.name = name
self.age = age
self.addr = addr
print('생성자메서드 __init__()가 호출되었습니다!')
# 일반메서드
def display(self):
print('일반메서드 display()가 호출되었습니다!!')
print(Korean.nation) # 정적변수
Korean.toString() # 정적메서드
print()
# Korean.name # 객체멤버변수이기 때문에 클래스로 접근불가
sonny = Korean(name="손흥민", addr="영국", age=32)
print(sonny.name, sonny.age, sonny.addr)
sonny.display()
sonny.toString()
print(sonny.__str__())
print(type(sonny))
대한민국
정적메서드 toString()이 호출되었습니다!!
이 객체의 국적속성의 값은 대한민국
생성자메서드 __init__()가 호출되었습니다!
손흥민 32 영국
일반메서드 display()가 호출되었습니다!!
정적메서드 toString()이 호출되었습니다!!
이 객체의 국적속성의 값은 대한민국
<__main__.Korean object at 0x000001C857000C90>
<class '__main__.Korean'>
# 5. class method, class variable(static variable, 즉 공통사용변수)
%reset -f
class Person:
# 클래스멤버 즉, static변수(공통사용변수)
count = 0; # 객체가 생성된 갯수
def __init__(self):
Person.count += 1 # Person객체가 생성될 때마다 static변수인 count가 1씩증가
@classmethod
def print_count(cls): # cls는 Person
print(f'{cls.count}개의 Person객체가 생성되었습니다!!')
@classmethod
def return_id(cls): # cls는 Person
return id(cls)
kim = Person()
print(kim.count)
lee = Person()
print(lee.count)
park = Person()
print(park.count)
print(id(Person), '=', Person.return_id())
Person.print_count()
# 클래스메서드는 정적메서드처럼 인스턴스(객체)생성없이 접근할 수 있다는 점에서는 동일하지만
# 클래스메서드는 메서안에서 클래스의 속성(클래스멤버변수)과 클래스메서드에 접근할 때 사용한다.
# 특히 cls를 사용하면 메서드안에서 현재 클래스의 인스턴스(객체)를 만들수 있다.
# 즉, cls는 현재의 클래스이므로 Person과 동일하다.
1
2
3
1757132573376 = 1757132573376
3개의 Person객체가 생성되었습니다!!
# 6. 생성자를 사용하지 않고 객체를 생성할 수 있다.
%reset -f
class Human:
@classmethod
def create(cls):
p = cls() # cls() 즉 Human()와 동일 즉, 생성자함수(__init__())를 호출한다.
return p
lee = Human()
kim = Human.create()
print('클래스메서드호출과 생성자호출 :', type(lee), '=', type(kim))
클래스메서드호출과 생성자호출 : <class '__main__.Human'> = <class '__main__.Human'>
클래스의 상속
- 파이썬에서 상속이란 특정 클래스를 만들 떄 해당 클래스의 속성(변수와 메서드)를 상속할 수 있도록 하는 기능이다.
- 부모클래스를 상속할 경우에는 부모의 생성자도 상속된다.
- 파이썬에서는 Java와는 다르게 다중상속이 가능하다.
- 문법
class 클래스명(부모클래스1,....): pass
- object 클래스
- 파이썬에서 최상위 부모클래스는 object클래스이다.
- 파이썬에서 생성되는 객체는 모두 object클래스를 부모클래스로 상속한다.
- 따라서, 별도 정의없이도 기본적으로 object를 상속을 받는다.
%reset -f
# 최상위 부모객체 object를 상속
class Calculator1:
pass
class Calculator2():
pass
class Calculator3(object):
pass
print(dir(object))
print()
print(dir(Calculator1))
print()
print(dir(Calculator2))
print()
print(dir(Calculator3))
%reset -f
class Calculator:
def __init__(self, first=None, second=None):
print('부모생성자(Calculator)가 호출되었습니다!!')
self.first = 0 if first is None else first
self.second = 0 if second is None else second
def add(self):
result = self.first + self.second
return result
def sub(self):
return self.first - self.second
def mul(self):
result = self.first * self.second
return result
def div(self):
return self.first / self.second
cal = Calculator(20,10)
print(cal.add(), cal.sub(), cal.mul(), cal.div())
부모생성자(Calculator)가 호출되었습니다!!
30 10 200 2.0
# 제곱근을 계산하는 계산기 만들기
# 1. 상속후에 pow()메서드만 추가
# 생성자는 부모생성자를 호출(super())
class SubCalculator1(Calculator):
def __init__(self, first=None, second=None):
super().__init__(first, second)
# super(first, second) 확인 필요
def pow(self):
return self.first ** self.second
cal1 = SubCalculator1(2, 3)
print(cal1.add(), cal1.sub(), cal1.mul(), cal1.div(), cal1.pow())
부모생성자(Calculator)가 호출되었습니다!!
5 -1 6 0.6666666666666666 8
cal1 = SubCalculator1(2, 1)
print(cal1.add(), cal1.sub(), cal1.mul(), cal1.div(), cal1.pow())
부모생성자(Calculator)가 호출되었습니다!!
3 1 2 2.0 2
class SubCalculator2(SubCalculator1):
# 오버라이딩 - 부모메서드재정의
# ZeroDivisionError: division by zero 해결로직
def div(self):
if self.second == 0:
print('나누기연산에서 분모값은 0일수가 없습니다!!')
return 0
else:
return self.first / self.second
cal2 = SubCalculator2(10, 0)
print(cal2.add(), cal2.sub(), cal2.mul(), cal2.div(), cal2.pow())
부모생성자(Calculator)가 호출되었습니다!!
나누기연산에서 분모값은 0일수가 없습니다!!
10 10 0 0 1
# 2. 부모클래스의 속성 사용하기
%reset -f
class Person:
# hello를 주석처리 번갈아 가면서 확인
hello = 'Hello Python'
def __init__(self):
print('Person 생성자함수 호출!!')
self.hello = "안녕하세요"
class Student(Person):
# __init__를 주석처리 번갈아 가면서 확인
def __init__(self):
print('Student 생성자함수 호출!!')
self.name = "홍길동"
pass
person = Person()
print(Person.hello)
print(person.hello)
print()
student = Student()
print(student.name)
print(student.hello) # Student에는 hello속성이 없기 때문에 부모의 속성 hello의 값을 출력
print(Student.hello)
# Person의 hello에 주석처리를 했을 경우 에러가 발생하는 이유는 hello는 Person클래스에 소속된 변수이기 때문이다.
# Student의 __init__를 주석처리를 했을 경우 Student객체가 생성될 때 부모생성자가 호출(super().__init__())이 된다.
Person 생성자함수 호출!!
Hello Python
안녕하세요
Student 생성자함수 호출!!
홍길동
Hello Python
Hello Python
Person 생성자함수 호출!!
안녕하세요
부모클래스의 속성을 찾는 process
# 4. 부모클래스를 초기화 하지 않아도 되는 경우
# 만약, 자식클래스에서 생성자메서드를 명시적으로 선언하지 않았다면
# 즉, 생략이 되었다면 자동으로 부모클래스의 생성자를 호출하기 때문에
# super()메서드를 사용하지 않아도 된다.
%reset -f
class Person:
def __init__(self):
print('Person 생성자함수 호출!!')
self.hello = "안녕하세요"
class Student(Person):
name = "홍길동"
pass
student = Student()
print(student.name)
print(student.hello)
Person 생성자함수 호출!!
홍길동
안녕하세요
# 4. 명확하게 super()메서드를 사용하기
# super(자식클래스, self)와 같이 선언하면 super는 Student클래스와 객체(self)를 전달해서
# 현재 클래스가 어떤 클래스인지를 명확하게 표시하는 방법이 있다.
%reset -f
%reset -f
class Person:
def __init__(self):
print('Person 생성자함수 호출!!')
self.hello = "안녕하세요"
class Student(Person):
def __init__(self):
super(Student, self).__init__() # 부모생성자를 호출
print('Student 생성자함수 호출!!')
self.name = "홍길동"
student = Student()
print(student.name)
print(student.hello)
Person 생성자함수 호출!!
Student 생성자함수 호출!!
홍길동
안녕하세요
6. 다중상속
# 1. 다중상속 : 전화 > 카메라 > 동영상
class Phone:
def call(self):
print('전화통화를 합니다!')
class Camera:
def photo(self):
print('사진촬영을 합니다!')
class SmartPhone(Phone, Camera):
def photo_call(self):
print('영상통화를 합니다!')
phone = SmartPhone()
phone.call()
phone.photo()
phone.photo_call()
전화통화를 합니다!
사진촬영을 합니다!
영상통화를 합니다!
# 2. 다이아몬드상속
class A:
def greeting(self):
print('안녕하세요? A입니다!')
class B(A):
def greeting(self):
print('안녕하세요? B입니다!')
class C(A):
def greeting(self):
print('안녕하세요? C입니다!')
class D1(B, C):
pass
class D2(C, B):
pass
d1 = D1()
d1.greeting();
d2 = D2()
d2.greeting();
# 상기와 같이 클래스간에 다이아몬드처럼 생겨서 객체지향프로그램언어에서는 이런 상속관계를 다이아몬드상속이라고 한다.
# D1, D2에서 어느 부모의 속성을 상속받을지 애매모호한 상태가 발생하게 된다. 만약, 사람의 생명을 다루는 프로그램이라면
# 심각한 상황을 초래할 수가 있다.
# 이러한 문제를 해결하기 하기 위해수는 파이썬에서 "MRO(Method Resoultion Order) 메서드탐색순서"를 정의하고 있다.
안녕하세요? B입니다!
안녕하세요? C입니다!
# 3. 다이아몬드상속의 문제해결
# MRO를 확인하기 위해서는 클래스명.mro()함수를 이용하면 MRO를 확인할 수 있다.
print(D1.mro())
print(D2.mro())
print()
print(int.mro())
print(str.mro())
print(list.mro())
print(dict.mro())
[<class '__main__.D1'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
[<class '__main__.D2'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
[<class 'int'>, <class 'object'>]
[<class 'str'>, <class 'object'>]
[<class 'list'>, <class 'object'>]
[<class 'dict'>, <class 'object'>]
7. 추상화
- 파이썬에셔는 Java와 같이 추상클래스(abstract class)를 제공한다.
- 추상클래스는 메서드의 목록만 선언 즉, 실행문이 없는 메서드만 가진 클래스이다.
- 추상클래스의 주된 목적은 상속받는 자식클래스에서 메서드의 구현을 강제화하기 위해 사용한다.
- 추상클래스를 만들기 위해서는 import abc처럼 abc(Abstract Base Class)모듈을 로딩해야 한다.
- 그리고, 클래스의 소괄호안에 metaclass=ABCMeta를 지정하고 메서드선언 위에 @abstractmethod를 선언해서 추상메서드로 선언한다.
- 사용법
from abc import * class 추상클래스명(metaclass=ABCMeta): @abstractmethod def 추상메서드명(self): pass
# 1. 추상클래스만들기
%reset -f
from abc import *
class StudentAbstract(metaclass=ABCMeta):
@abstractmethod # 추상메서으로 선언하면 상속받는 클래스에서 반드시 구현해야 한다.
def study(self): pass
class Student(StudentAbstract):
def study(self):
print('공부를 합니다!!')
def goto_school(self):
print('학교를 갑니다!')
student = Student()
student.study() # 구현을 안할 경우 TypeError: Can't instantiate abstract class Student with abstract method study
student.goto_school()
공부를 합니다!!
학교를 갑니다!
# 추상클래스는 객체를 생성할 수 없다.
aaa = Student()
# xxx = StudentAbstract() # TypeError: Can't instantiate abstract class StudentAbstract with abstract method study
8. 클래스멤버변수
- 파이썬에서 클래스멤버변수는 클래스에 의해 생성된 모든 인스턴스(객체)에서 공유된다는 특징이 있다.
class Family:
last_name = "홍"
first_name = str()
hong1 = Family()
hong2 = Family()
print(hong1.last_name, hong2.last_name)
홍 홍
# 동일변수여부확인
print(id(hong1.last_name))
print(id(hong2.last_name))
print(id(Family.last_name))
print()
print(id(hong1.first_name))
print(id(hong2.first_name))
print(id(Family.first_name))
1959988049392
1959988049392
1959988049392
140737447401184
140737447401184
140737447401184
# 공통변수 속성의 문제점
# hong1.last_name = "박"
hong2.last_name = "김"
print(f'Family : {Family.last_name}')
print(f'hong1 : {hong1.last_name}')
print(f'hong2 : {hong2.last_name}')
print()
Family.last_name = "트럼프"
print(f'Family : {Family.last_name}')
print(f'hong1 : {hong1.last_name}') # last_name - Family클래스의 클래스멤버변수
print(f'hong2 : {hong2.last_name}') # last_name - hong2객체의 객체변수
print()
Family : 홍
hong1 : 홍
hong2 : 김
Family : 트럼프
hong1 : 트럼프
hong2 : 김
8. 클래스속성
- 클래스의 속성에는 클래스속성(클래스멤버)과 인스턴스속성(객체멤버) 2가지 종류가 있다.
- 생성자에 의해 정의된 속성은 객체속성이고 class에서 선언된 속성은 클래스속성이다.
# 1. 클래스속성사용하기
%reset -f
class Person:
bag = [] # 클래스멤버, Person으로 생성된 모든 객체에서 공통으로 사용되는 속성
def put_bag(self, stuff):
self.bag.append(stuff)
kim = Person()
lee = Person()
park = Person()
print(kim.bag, lee.bag, park.bag)
print(id(kim.bag), id(lee.bag), id(park.bag))
print()
kim.put_bag('자동차키')
lee.put_bag('핸드폰')
park.put_bag('지갑')
print(kim.bag, lee.bag, park.bag)
print(id(kim.bag), id(lee.bag), id(park.bag))
[] [] []
1959988953216 1959988953216 1959988953216
['자동차키', '핸드폰', '지갑'] ['자동차키', '핸드폰', '지갑'] ['자동차키', '핸드폰', '지갑']
1959988953216 1959988953216 1959988953216
%reset -f
class Person:
bag = [] # 클래스멤버, Person으로 생성된 모든 객체에서 공통으로 사용되는 속성
def put_bag(self, stuff, name):
self.bag.append(stuff)
self.name = name # 객체멤버 Person으로 생성된 객체에서 해당 객체만 사용되는 속성
kim = Person()
lee = Person()
park = Person()
print(kim.bag, lee.bag, park.bag)
print(id(kim.bag), id(lee.bag), id(park.bag))
print()
kim.put_bag('자동차키', '김씨')
lee.put_bag('핸드폰', '이씨')
park.put_bag('지갑', '박씨')
print(kim.bag, lee.bag, park.bag)
print(id(kim.bag), id(lee.bag), id(park.bag))
print(id(kim.name), id(lee.name), id(park.name))
[] [] []
1959978813568 1959978813568 1959978813568
['자동차키', '핸드폰', '지갑'] ['자동차키', '핸드폰', '지갑'] ['자동차키', '핸드폰', '지갑']
1959978813568 1959978813568 1959978813568
1959987786064 1959978957872 1959988054992
# 해당변수가 클래스속성인지 객체속성인지 확인
# __dict__ 속성으로 확인
print(kim.__dict__)
print(lee.__dict__)
print(park.__dict__)
print()
print(Person.__dict__)
{'name': '김씨'}
{'name': '이씨'}
{'name': '박씨'}
{'__module__': '__main__', 'bag': ['자동차키', '핸드폰', '지갑'], 'put_bag': <function Person.put_bag at 0x000001C8581BADE0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# 객체속성으로 사용하기
# 특정변수를 객체에서만 사용하기위해서는 해당변수를 클래스멤버로 선언하는 것이 아니라
# 객체멤버 즉, 생성자함수에서 객체소속 변수로 선언해야 한다.
%reset -f
class Person:
def __init__(self):
self.bag = []
def put_bag(self, stuff, name):
self.bag.append(stuff)
self.name = name
kim = Person()
lee = Person()
park = Person()
print(kim.bag, lee.bag, park.bag)
print(id(kim.bag), id(lee.bag), id(park.bag))
print()
kim.put_bag('자동차키', '김씨')
lee.put_bag('핸드폰', '이씨')
park.put_bag('지갑', '박씨')
print(kim.bag, lee.bag, park.bag)
print(id(kim.bag), id(lee.bag), id(park.bag))
print(kim.name, lee.name, park.name)
print(id(kim.name), id(lee.name), id(park.name))
[] [] []
1959986947648 1959989792576 1959991159936
['자동차키'] ['핸드폰'] ['지갑']
1959986947648 1959989792576 1959991159936
김씨 이씨 박씨
1959987785488 1959987786832 1959987778384
# 캡슐화(은닉화)
# 비공개속성으로 사용하기
# 클래스속성인데 객체속성처럼 비공개속성(비공유속성)으로 선언할 수가 있다.
# 클래스 속성을 비공개속성으로 선언하려몀 "__속성명"으로 선언하면 비공개속성이 된다.
# 비공개속성은 클래스안에서만 접근할 수 있고, 클래스 외부에서는 접근할 수 없다.
# 파이썬에서는 Java처럼 완벽한 비공개, 즉 캡슐(은닉화)하기는 업렵다.
# '__속성__'와 같이 선언된 속성은 캡슐화와는 상관이 없다. 즉, 공개속성이다.
# '__속성'와 같이 선언된 속성이 비공개 속성이다.
%reset -f
class Korean:
country = "대한민국"
__country__ = "Korea"
__country = "Republic Of Korea"
def getCountry(self): # getter
# return '캡슐화(은닉화): 비공개속성은 접근할 수 없다!!'
return Korean.__country
kim = Korean()
print(kim.country)
print(kim.__country__)
# print(kim.__country) AttributeError: 'Korean' object has no attribute '__country'
print(kim.getCountry()) # 비공개된 속성은 getter를 통해서 접근가능
대한민국
Korea
Republic Of Korea
# 파이썬에서는 불완전한 캡슐화(1)
# Korean.__country # AttributeError: type object 'Korean' has no attribute '__country'
Korean.__country = '미국'
print(kim.country)
print(kim.__country__)
print(kim.getCountry())
대한민국
Korea
Republic Of Korea
# 파이썬에서는 불완전한 캡슐화(2)
print(dir(Korean)) # '_Korean__country'
print(kim.__country)
print(kim._Korean__country)
print()
# 언더바2개 즉, 비공개속성으로 선언된 변수의 이름은 내부적으로 "_클래스명__변수명"으로 변경이 되어 저장된다.
# 확인하는 방법은 dir()함수로 확인할 수 있고 클래스와 객체의 각각의 변수를 확인할 수 있다.
print(dir(kim))
['_Korean__country', '__class__', '__country', '__country__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'country', 'getCountry']
미국
Republic Of Korea
['_Korean__country', '__class__', '__country', '__country__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'country', 'getCountry']
# 은닉화(비공개속성)된 변수는 setter를 통해서 변경
%reset -f
class Korean:
country = "대한민국"
__country__ = "Korea"
__country = "Republic Of Korea"
def getCountry(self): # getter
# return '캡슐화(은닉화): 비공개속성은 접근할 수 없다!!'
return self.__country
def setCountry(self, country):
self.__country = country
kor = Korean()
print(kor.getCountry())
kor.setCountry('영국')
print(kor.getCountry())
print(kor._Korean__country)
Republic Of Korea
영국
영국
9. 다형성
class Phone:
def call(self):
print('통화기능')
class FoldePhone(Phone):
def call(self):
print('폴더폰:통화기능')
class CameraPhone(Phone):
def call(self):
print('영상통화기능')
class SmartPhone(Phone):
def call(self):
print('통화,영상통화, 인터넷검색 기능')
p1 = Phone()
p1.call()
p2 = FoldePhone()
p2.call()
p3 = CameraPhone()
p3.call()
p4 = SmartPhone()
p4.call()
통화기능
폴더폰:통화기능
영상통화기능
통화,영상통화, 인터넷검색 기능
'Python' 카테고리의 다른 글
[Python]함수_내장함수 (0) | 2024.04.24 |
---|---|
[Python]17.예외처리_Exception (0) | 2024.04.24 |
[Python]파이썬_Package (0) | 2024.04.12 |
[Python]파이썬_Module (0) | 2024.03.28 |
[Python]파이썬입출력_FileIO (0) | 2024.03.28 |