파이썬 중급: 클래스

클래스(Class)

1. 클래스(Class) 정의

  • 클래스(Class)
    • 객체 지향 프로그래밍(Object Oriented Programming, OOP)에서 특정 객체를 생성하기 위해 변수와 메서드를 정의하는 일종의 틀
    • 내부적으로 객체를 정의하기 위한 상태 값을 의미하는 멤버 변수와 클래스의 동작인 메서드(함수)로 구성됨
  • 기본 용어
    • 클래스 객체(Class Object)
      • class 정의문에 의해 지정된 문장들
      • 인스턴스 객체를 만드는 틀을 역할을 함
      • 메서드에 대한 모든 것을 보관함
      • 인스턴스에서 메서드를 호출할 때 클래스의 메서드와 바인딩이 발생해서 처리되는 구조
    • 인스턴스 객체(Instance Object)
      • 클래스 객체에 의해 만들어진 객체
      • __ init __ 메서드 내부에 지정된 속성을 가지는 네임스페이스만 있고 메서드는 클래스에 있는 것을 사용함
    • 클래스 객체 멤버(Class Object Member)
      • 클래스 속성, 클래스 메서드, 정적 메서드 등은 클래스가 직접 호출해서 처리할 수 있는 멤버
      • 인스턴스는 자기 네임스페이스에 지정된 것을 빼면 모든 클래스 멤버에 인스턴스에서 직접 접근 가능
      • 프로퍼티 속성: 메서드를 기반으로 이름으로 접근해서 처리되는 속성
    • 인스턴스 객체 멤버(Instance Object Member)
      • 인스턴스 속성만 인스턴스에서 관리하는 멤버
      • 일반적인 인스턴스 메서드는 클래스 내에서 관리함

1.1 객체의 분류

  • 사물에는 기본 정보를 관리하는 클래스가 필요

class BookInfo : def init(self,title,author,date,publisher,page) : self.title = title self.author = author self.date = date self.publisher = publisher self.page = page

book1 = BookInfo(“서시”,”윤동주”,”1939”,”출판사”,100) book2 = BookInfo(“파이썬”,”달문”,”2017”,”출판사”,900)

import pprint pprint.pprint(book1.dict) pprint.pprint(book2.dict)

  • 책에 대한 메타 정보인 범주 관리

class BookClass(BookInfo) : def init(self,title,author,date,publisher,page,isdn=None) : super().init(title,author,date,publisher,page) self.isdn = isdn

bookclass1 = BookClass(book1.__dict__,isdn=”1111111111111”) bookclass2 = BookClass(book2.dict,isdn=”2222222222222”)

import pprint pprint.pprint(bookclass1.dict) pprint.pprint(bookclass2.dict)

  • 실제 만들어져 있는 객체로써 책을 관리

class BookInv(BookClass) : def init(self,title,author,date,publisher,page,isdn,inventno=None) : super().init(title,author,date,publisher,page,isdn) self.inventno = inventno

bookinv1 = BookInv(bookclass1.__dict__,inventno=3000) bookinv2 = BookInv(bookclass2.dict,inventno=3000)

import pprint pprint.pprint(bookinv1.dict) pprint.pprint(bookinv2.dict)

1.2 객체의 특징

  • object 클래스로 객체의 레퍼런스 확인하기

a = object()

b = object()

print(a is b) print(id(a), id(b))

t = (1,2,3)

ts = tuple(t)

print(t is ts) print(id(t), id(ts))

  • int 클래스에 대한 책임성 확인하기

a = int(10)

b = int(5)

print(a+b) print(a.add(b)) print(a-b) print(a.sub(b))

  • int 클래스 내에서 관리하는 속성과 메소드 확인하기

for i,v in enumerate(dir(int),1) : print(v,end=” “) if i % 5 == 0 : print()

1.3 최상위 클래스 object 이해하기

  • object 클래스 내부의 속성과 메소드 보기

for i,v in enumerate(dir(object),1) : print(v,end=” “) if i % 5 == 0 : print()

print(object.doc) print(object.name) print(object.str(object)) print(object.repr(object))

print(object.eq(object, object)) print(object is object)

o = object()

print(o) print(o.dict)

1.4 파이썬 클래스 생성 및 기본 상속 구조

1.4.1 클래스의 생성과 상속



  • Type 클래스의 insetance 여부 확인

l = [type, object, int, float, str, tuple, list, dict,set]

for i in l : print(isinstance(i,type))

l = [type, object, int, float, str, tuple, list, dict,set]

for i in l : print(issubclass(i,object))

  • Class 내부 속성 __ class __, __ bases __ 확인

l = [‘type’, ‘object’, ‘int’, ‘float’, ‘str’, ‘tuple’, ‘list’, ‘dict’,’set’]

for i in l : print(eval(i+”.class”))

l = [‘type’, ‘object’, ‘int’, ‘float’, ‘str’, ‘tuple’, ‘list’, ‘dict’,’set’]

for i in l : print(eval(i+”.bases”))

1.4.2 클래스와 인스턴스의 관계



  • 내장 클래스와 인스턴스의 관계 확인

l = [‘object’, ‘int’, ‘float’, ‘str’, ‘tuple’, ‘list’, ‘dict’,’set’]

for i in l : print(eval(“isinstance(“+i+”(),”+i+”)”))

l = [‘object’, ‘int’, ‘float’, ‘str’, ‘tuple’, ‘list’, ‘dict’,’set’]

for i in l : print(eval(i+”().class”))

1.5 사용자 정의 클래스

  • 사용자 정의 클래스 기본 확인

class Klass : pass

print(Klass.class) print(Klass.bases)

print(isinstance(Klass, type)) print(issubclass(Klass, object))

import pprint

class Int(int) : pass

a = Int(10) print(type(a),a)

pprint.pprint(Int.dict)

print(Int.class) print(Int.bases)

print(isinstance(Int, type)) print(issubclass(Int, object)) print(issubclass(Int, int))

  • Person 사용자 클래스를 정의 및 속성 확인

class Person : def init(self, name, age) : self.name = name self.age = age

p = Person(“줄리아”,15)

print(p) print(p.dict) print(p.name) print(p.age)

o = set(dir(object)) pc = set(dir(Person))

print(pc-o)

print(Person.module) print(Person.bases) print(Person.class)

1.6 객체 네임스페이스 및 스코프

1.6.1 인스턴스와 클래스 객체 네임스페이스 및 스코프 처리 기준



  • 클래스와 인스턴스 객체간의 네임스페이스 접근

class Klass : name = “Klass attr” def init(self, name) : self.name = name

def getname(self) :
    return self.name

k = Klass(“instance attr”)

print(k.name) print(Klass.name)

k.getclassname = Klass.name print(k.dict) print(k.getclassname)

print(k.getname())

print(Klass.getname(k))

import pprint

pprint.pprint(Klass.dict)

print(k.getname) print(Klass.getname)

2. 생성자(Constructor)와 소멸자(Destructor)

2.1 생성자 이해하기: __ new __

  • 클래스 생성자로 인스턴스 생성하기

import pprint

class AAA : def new(cls) : return object.new(cls)

aaa = AAA()

print(aaa)

pprint.pprint(AAA.dict)

print(isinstance(aaa,AAA))

  • 자기 자신의 클래스만 처리

class OnlyKlass : def new(cls) : return cls

ok = OnlyKlass()

print(ok)

print(type(ok)) print(ok is OnlyKlass)

class OnlyKlass : def new(cls) : return cls

@classmethod
def getname(cls) :
    return cls.name

@classmethod
def setname(cls, name) :
    cls.name = name

import pprint

pprint.pprint(OnlyKlass.dict)

ok = OnlyKlass()

ok.setname(“ class method “) print(ok.getname())

2.2 초기화 메서드 사용하기: __ init __

  • 인스턴스 속성을 초기화

class Pass : pass

p = Pass()

print(p.dict)

p.name = “양영초” p.age = 10

print(p.dict)

class INIT : count = 0 def init(self,name,age) : self.name = name self.age = age self.count += 1

i = INIT()

class INIT : count = 0 def init(self,name,age) : self.name = name self.age = age self.count += 1

i = INIT(“양영중”,15) print(i.dict)

print(i.name) print(i.age) print(i.count)

2.3 소멸자 메서드 사용하기: __ del __

  • 소멸자 정의된 클래스 생성하기

class Counter : count = 0

def __init__(self,name) :
    self.name = name
    Counter.count = Counter.count +1

def __del__(self) :
    Counter.count = Counter.count -1

x = Counter(“ First “) print(x) print(x.dict) print(Counter.count) y = Counter(“ Second “) print(y) print(y.dict) print(Counter.count)

del y print(Counter.count)

  • 약한 참조 이용하기

a = 1 print(id(a)) b = a print(id(b))

del a print(b)

import weakref import gc

class MyObject(object): def my_method(self): print(‘my_method was called!’)

obj = MyObject()

r = weakref.ref(obj) print(type(r), r) s = r() assert isinstance(obj, MyObject) assert s is obj

s.my_method()

obj = 1 gc.collect() print(r) print(s) assert r() is not None

del s assert r() is not None

2.4 생성자 작동 원리 이해하기

  • 생성자, 초기화, 호출연산자를 정의한 클래스

class MDPerson(object) : def new(cls,name,major) : return object.new(cls)

def __init__(self,name, major) :
    self.name = name
    self.major = major

def __call__(cls, name, major) :
    print(" __new__ ")
    self = cls.__new__(cls,name,major)
    print(" __init__ ")
    self.__init__(name,major)
    return self

mdp = MDPerson.call(MDPerson,”이주원”,”quant”)

print(mdp) print(mdp.name) print(mdp.major)

mdp2 = type.call(MDPerson,”이주튜”,”quant”)

print(mdp2) print(mdp2.name) print(mdp2.major)

mdp3 = MDPerson(“이주하”,”quant”)

print(mdp3) print(mdp3.name) print(mdp3.major)

2.5 함수를 이용한 생성자 패턴

  • 함수의 결과로 인스턴스 생성하기

class Person : def init(self,name,age) : self.name = name self.age = age

class Employee(Person): def init(self, name, age, depart,salary) : super().init(name,age) self.depart = depart self.salary = salary

class Employer(Person) : def init(self, name, age, salary) : super().init(name,age) self.salary = salary

def employ(name,age, *,depart=None,salary=None) : if depart is None : return Employer(name,age,salary=salary) else :

    if salary == None :
        salary = 0

    return Employee(name,age,depart=depart,salary=salary)

e = employ(“정찬혁”,31,depart=”빅데이터부”,salary=30000)

print(e) print(type(e))

e = employ(“달문”,52,salary=300000)

print(e) print(type(e))

2.6 인스턴스 네임스페이스 변경하기: __ slots __

  • __ slots __ 사용하기

class Klass : slots = (“name”,) def init(self, name,age) : self.name = name self.age = age

k = Klass(“name”,”age”)

class Klass : slots = (“name”,) def init(self, name) : self.name = name

k = Klass(“name”) print(k)

print(k.name) print(k.dict)

import pprint

pprint.pprint(dir(Klass))

print(Klass.slots) print(Klass.dict[Klass.slots[0]])

print(type(Klass.name))

k.name = “가을이” print(k.name)

Klass.age = 100

k.age

k.job = “Data Engineer”

  • 관행적으로 __ dict __ 속성 조회 로직이 있을 경우

class MyClass : slots = [‘x’,’y’,’dict’]

def __init__(self,x,y) :
    self.x = x
    self.y = y
    self.__dict__ = {}

m = MyClass(5,5) print(m.x, m.y) print(m.dict)

m.dict[‘a’] = 100

print(m.a)

m.b = 100

print(m.dict)

print(type(m), m.slots)

3. 객체 접근 연산(.)

3.1 점(dot) 연산

  • 초기화 처리할 때 속성을 접근

class Person :

def __init__(self, name, age) :
    self.name = name
    self.age  = age

def __setattr__(self, name, value) :
    print(" __setattr__ ", name)
    self.__dict__[name] = value

p = Person(“사람”, 50)

3.2 점 연산자 스페셜 메서드(Special Method) 기본 이해하기

  • __ getattribute __를 이용해서 클래스 내부 검색

class Person :

job = "즐기기"

def __init__(self, name, age) :
    self.name = name
    self.age  = age

def __getattribute__(self, name) :
    print(" attribute name ", name)
    return super().__getattribute__(name)

def __getattr__(self, name) :
    print(" attr name ", name)
    return Person.__dict__[name]

p = Person(“긍정”,55) print(p.name)

p.job

p.getattr(‘job’)

  • attrgetter를 이용해서 속성 접근

class Person :

job = "즐기기"

def __init__(self, name, age) :
    self.name = name
    self.age  = age

import operator

getname = operator.attrgetter(“name”,”age”,”job”)

p = Person(“가을이”,10)

print(getname)

print(getname(p))

4. 메서드 확인하기

4.1 메소드(Method) 이해하기

4.1.1 인스턴스 메서드(instance method) 확인

  • 인스턴스 메소드 정의하기

class Klass_ins : def set(self,name,value) : setattr(self,name,value)

ki = Klass_ins() print(ki) print(isinstance(ki, Klass_ins))

ki.set(“name”,”강감찬”) ki.set(“age”,45)

print(ki.dict)

class Person : def init(self,name,age) : self.name = name self.age = age

p = Person(“서희”,35) print(p.dict)

4,1,2 클래스 메서드 정의

  • 클래스 메서드 정의하기

class Klass_cls : @classmethod def set(cls,name,value) : setattr(cls,name,value)

Klass_cls.set(“name”,”클래스”) Klass_cls.set(“age”,50)

import pprint

pprint.pprint(Klass_cls.dict)

class Person : name = “” age = 0

@classmethod
def set(cls,name,value) :
    setattr(cls,name,value)

@classmethod
def get(cls) :
    return cls.name, cls.age

Person.set(“name”,”클래스”) Person.set(“age”,50)

print(Person.get())

p = Person() print(p.get()) print(p.name)

c = Person() c.name = “가을이” c.age = 55 print(c.get()) print(c.name) print(c.dict)

4.1.3 정적 메서드 정의

  • 정적 메소드 정의하기

class Klass_st : name = “” age = 0 def init(self,name,age) : self.name = name self.age = age

@classmethod
def set(cls,name,value) :
    setattr(cls,name,value)

@staticmethod
def get(obj) :
    return obj.name, obj.age

import pprint

pprint.pprint(Klass_st.dict)

c = Klass_st(“인스턴스”,50)

Klass_st.set(“name”,”클래스”) Klass_st.set(“age”,55)

print(c.get(c)) print(Klass_st.get(Klass_st))

4.2 self / cls 매개변수 이해하기

4.2.1 인스턴스 메서드 정의 이해하기

  • 인스턴스 메소드 self 알아보기

class InsKlass : def init(self,name) : self.name = name

def getname(self) :
    return self.name

print(InsKlass.getname)

ins = InsKlass(“인스턴스”) print(ins.getname)

s = set(dir(InsKlass.getname))

i = set(dir(ins.getname))

print(i - s) print(ins) print(ins.getname.self) print(InsKlass.getname) print(ins.getname.func)

4.2.2 클래스 메서드 내부 확인하기

  • 클래스 메소드 cls 알아보기

class ClsKlass : name = “클래스” def init(self,name) : self.name = name @classmethod def getname(cls) : return cls.name

a = ClsKlass(“인스턴스”)

print(ClsKlass.getname)

s = set(dir(ClsKlass.init))

c = set(dir(ClsKlass.getname))

print(c-s)

print(ClsKlass) print(ClsKlass.getname.self) print(ClsKlass.getname.func)

4.2.3 인스턴스 메서드의 self 매개변수 위치 이해하기

  • self 변수를 미지정해서 인스턴스 메소드 알아보기

class Self : attr = (“name”,”age”) def init(*args) : print(“self argument “,args[0]) print(“self attribute “,args[0].init.self)

    for i in range(1,len(args)) :
        args[0].__dict__[Self.attr[i-1]] = args[i]

def get(*args) :
    print("__self__ attribute ",args[0].get.__self__)
    return args[0].name, args[0].age

s = Self(“Dahl”,22) print(s.dict) print(s)

print(s.get())

class Self_ :

def __init__(self,name,age) :
    self.name = name
    self.age =  age

def get(self) :

    return self.name, self.age

s = Self_(“Dahl”,22) print(s.dict) print(s) print(s.get())

4.3 외부 함수와 메서드를 동시에 사용하는 패턴 이해하기

4.3.1 외부 함수를 내부 인스턴스 메서드로 사용하기

  • 외부 함수를 정의하고 클래스 내부에 할당하기

def getname(self) : return self.name

def getage(self) : return self.age

class Person : def init(self,name,age) : self.name = name self.age = age

getname = getname
getage  = getage

p = Person(“함수”,44)

print(p.getname()) print(getname(p)) print(p.getage()) print(getage(p))

print(p.getname) print(getname) print(p.getname.func is getname) print(p.getage) print(getage) print(p.getage.func is getage)

  • __ init __ 를 함수로 정의하고 클래스 내에 할당할 경우

def init(self,name,age) : self.name = name self.age = age

class Person : init = init

p = Person(“DahlMoon”,22) print(p.dict)

print(p.init.func) print(init)

4.4 플루언트 인터페이스(Fluent Interface, $→$ 메서드 체이닝)

  • Method Chain 처리

class MethodChain : def init(self, content) : self.content = content

def intent(self,space) :
    self.content = " "*space + self.content
    return self

def suffix(self,content) :
    self.content = self.content + "-" + content
    return self

m = MethodChain(“하늘과별과 시”).intent(5).suffix(“윤동주”).content

print(m)

5. 캡슐화(Encapsulation)

5.1 내부 속성이나 메서드 명명 규칙 관행

5.2 보호된 이름: _이름

5.2.1 메서드로 보호 속성 감추기

  • 보호된 이름 사용

class Protected : def init(self,name,age) : self._set(name,age)

def _set(self,name,age) :
    self._name = name
    self._age = age

def getname(self) :
    return self._name
def getage(self) :
    return self._age

p = Protected(“정찬혁”, 31)

print(p.dict)

print(p.getname()) print(p.getage())

print(p._name) print(p._age)

5.3 맹글링(Mangling)을 이용한 정보 은닉

  • 속성이나 메소드에 대한 맹글링 처리

class Mangling : def init(self,name,age) : self.__set(name,age)

def __set(self,name,age) :
    self.__name = name
    self.__age = age

def getname(self) :
    return self.__name
def getage(self) :
    return self.__age

p = Mangling(“정찬혁”, 31)

print(p.dict)

print(p.getname()) print(p.getage())

print(p.__name) print(p.__age)

print(p._Mangling__name) print(p._Mangling__age)

import pprint

pprint.pprint(Mangling.dict)

p.__set(“맹글링”,55)

p._Mangling__set(“맹글링”,55)

print(p._Mangling__name) print(p._Mangling__age)

5.4 Property를 이용한 정보 은닉

  • 프로퍼티로 속성을 숨기기

class PropertyClass : def init(self,name) : self._name = name @property def name(self) : return self._name @name.setter def name(self,value) : self._name = value

import pprint

pprint.pprint(PropertyClass.dict)

p = PropertyClass(“은옥주”)

print(p.name)

p.name = “금옥주” print(p.name)

print(p.dict) print(p._name) p._name = “동옥주” print(p.name)

6. 상속(Inheritance)

6.1 상속

6.1.1 상속 시 초기화 메서드 처리

  • 부모 클래스의 초기화 모듈을 이용

class Parent : def init(self,name,age) : self.name = name self.age = age

class Child(Parent) : pass

import pprint

pprint.pprint(Parent.dict)

import pprint

pprint.pprint(Child.dict)

c = Child(“자식”,33) print(c) print(c.dict)

6.1.2 Super class와 sub class 관계 이해하기

  • 상속관계 확인하기

class GrandParent : def init(self,name,age) : self.name = name self.age = age

class Parent(GrandParent) : pass

class Child(Parent) : pass

print(GrandParent.bases) print(Parent.bases) print(Child.bases)

print(issubclass(Parent, GrandParent)) print(issubclass(Child, Parent)) print(issubclass(Child, GrandParent))

6.1.3 상속에 따른 네임스페이스 검색

  • 상속에 따른 네임스페이스 검색

class A : A_cls = “A 클래스 속성”

class B(A) : pass

class C(A) : pass

print(B.A_cls) print(C.A_cls)

B.A_cls = “B 클래스 속성”

print(B.A_cls) print(C.A_cls)

import pprint

pprint.pprint(A.dict)

import pprint

pprint.pprint(B.dict)

6.2 상속할 때 자식 클래스 초기화 기능 추가

  • __ init_subclass __ 클래스 메소드

help(object.init_subclass)

class Super : def init_subclass(cls,name) : print(type(cls),cls) cls.name = name

class Sub(Super, name=”sub”) : pass

print(Sub.name)

import pprint

pprint.pprint(Super.dict)

import pprint

pprint.pprint(Sub.dict)

6.3 다중 상속(Multiple Inheritance)

6.3.1 다중상속 클래스 읽는 순서

  • 다중 상속 Class 정의 및 읽는 순서 확인하기

class Parent1 : def init(self,name) : print(“ Parent1 “) self.name = name

class Parent2 : def init(self,name) : print(“ Parent2 “) self.name = name

class Child(Parent1, Parent2) : pass

import pprint

pprint.pprint(Child.mro())

c = Child(“다중상속”)

  • 다른 부모 클래스의 __ init __ 메소드를 사용하고 싶을 경우

class Parent1 : def init(self,name) : print(“ Parent1 “) self.name = name

class Parent2 : def init(self,name,age) : print(“ Parent2 “) self.name = name self.age = age

class Child2(Parent1, Parent2) :

def __init__(self,name, age=None) :
    if age is None :
        super().__init__(name)
    else :
        Parent2.__init__(self,name,age)

c1 = Child2(“다중상속”) print(c1.dict)

c2 = Child2(“다중상속”, 33) print(c2.dict)

6.4 super 클래스 이해하기

  • super 클래스 이해하기

class A : A_cls = “ AAA “

class B(A) : A_cls = “ BBB “

print(super(B,B()).A_cls)

class A : def init(self,name) : self.name = name

class B(A) : def init(self,name,age) : super().init(name) self.age = age

b = B(“슈퍼우먼”, 33) print(b.dict)

class A : def init(self,name) : self.name = name

class B : def init(self,name,age) : self.name = name self.age = age

class C(A,B) : def init(self,name,age=None) : super().init(name) if age : C.mro()[2].init(self,name,age)

c = C(“슈퍼우먼”, 33) print(c.dict)

c2 = C(“수퍼맨”) print(c2.dict)

6.5 Mixin 패턴 이해하기

6.5.1 메서드만 처리하는 Mixin 클래스 정의

  • Mixin 클래스 하나를 상속처리

import operator as op

class OpMixin :

def aroper(self,op_code) :
    return {'+':op.add,
            '*':op.mul}[op_code]  \
           (self.x, self.y if type(self.y) not in [str,list, tuple]  \
                           else  self.y if op_code != "*"
                                        else len(self.y))

class Num(OpMixin) : def init(self, x,y) : self.x = x self.y = y

class STR(OpMixin) : def init(self, x,y) : self.x = x self.y = y

class LIST(OpMixin) : def init(self, x,y) : self.x = x self.y = y

n = Num(5,6)

print(n.aroper(“+”)) print(n.aroper(“*”))

s = STR(“Hello”,”World”)

print(s.aroper(“+”)) print(s.aroper(“*”))

l = LIST([1,2,3,4],[6,7])

print(l.aroper(“+”)) print(l.aroper(“*”))

6.5.2 Mixin 다중 상속 시 주의할 사항

  • 여러 Mixin 클래스에 동일한 메소드

class AMixin : def method(self) : return “A Mixin”

class BMixin : def method(self) : return “B Mixin”

class A(AMixin, BMixin) : pass

a = A() print(a.method())

class AB(AMixin, BMixin) : def init(self,code) : self.code = code

def method(self) :
    if self.code == "B" :
        return BMixin.method(self)
    else :
        return AMixin.method(self)

ab = AB(“B”) print(ab.method())

  • Mixin 클래스 다중상속 처리: 다른 메소드 이름

class AMixin : def getname(self) : return self.name

class BMixin : def getage(self) : return self.age

class AB(AMixin, BMixin) : def init(self,name,age) : self.name = name self.age = age

ab = AB(“다중상속”,33) print(ab.getname()) print(ab.getage())

7. 다형성(Polymorphism)

7.1 다형성이란

  • 다형성
    • 프로그래밍 언어의 자료형 내 요소들이 다양한 곳에 내포되어 처리하는 것
    • 특히 객체지향 프로그래밍에서는 상속을 받을 경우 부모와 자식 클래스 내에 메서드들이 동일하게 구현되어 처리하는 것을 말함
    • 프로그래밍에서의 다형성은 그 프로그래밍 언어의 자료형 체계의 성질을 나타냄
      • 프로그래밍 언어의 각 요소들(상수, 변수, 식, 오브젝트, 함수, 메서드 등)이 다양한 자료형에 속하는 것이 허가되는 성질
    • 다형성의 반대 속성: 단형성. 프로그래밍 언어의 각 요소가 한 가지 형태만 가지는 성질
  • 다형성의 실질적인 재정의
    • 오버라이딩: 부모의 기능을 사용하지 않고 자식의 기능으로 특화하는 방법
    • 오버로딩: 동일한 기능을 여러 개로 분리해서 사용하는 방법
      • 파이썬은 이름으로만 관리하므로 동일한 기능을 여러 개로 분리할 수 없지만 특정 모듈을 사용해서 오버로딩이 가능함

7.2 매서드 오버라이딩(Overriding)

  • 부모 클래스의 메소드를 오버라이딩

class Parent1 : def init(self,name) : print(“ Parent1 “) self.name = name def getname(self) : return self.name

class Parent2 : def init(self,age) : print(“ Parent2 “) self.age = age def getage(self) : return self.age

class Child2(Parent1, Parent2) :

def __init__(self,name, age=None) :
    super().__init__(name)
    if age is not None :
        Parent2.__init__(self,age)

def getname(self) :
    return "child " + self.name
def getage(self) :
    return "child " + str(self.age)

c = Child2(“오버라이딩”, 33)

print(c.getname()) print(c.getage())

print(Parent1.getname(c)) print(Parent2.getage(c))

7.3 메서드 오버로딩(Overloading)

  • overload 모듈을 이용

!pip install –upgrade overload

from overload import overload

class A : @overload def method(self) : print(“ no args method “)

@method.add
def method(self, x) :
    print(" one args method "+ x)

@method.add
def method(self, x,y) :
    print(" two args method "+ x,y)

a = A()

a.method() a.method(“hello”) a.method(“hello”,”world”)

import pprint

pprint.pprint(A.dict)

print(type(A.method)) pprint.pprint(A.method.dict)

7.4 연산자 오버로딩(Operator Overloading)

  • 연산자 오버로딩 이해하기

class Person : def init(self,name,age) : self.name = name self.age = age

class Parent(Person) : def init(self,name,age) : print(“ Parent class “) self.name = name self.age = age

class Child(Person) : def init(self,name,age) : print(“ Child class “) self.name = name self.age = age

p = Parent(“서희”,55) c = Child(“서준”,17)

class Person : def init(self,name,age) : print(“ Person class “) self.name = name self.age = age

class Parent(Person) : def init(self,name,age,pa_code) : super().init(name,age) self.pa_code = pa_code

def ischild(self,child) :
    return True if self is child.pa_id else False

class Child(Person) : def init(self,name,age,pa_code,pa_id) : super().init(name,age) self.pa_code = pa_code self.pa_id = pa_id

def isparent(self, parent) :
    return True if parent is self.pa_id else False

p = Parent(“서아빠”,56,”p”) c = Child(‘서준’,19,”c”,p)

print(p.ischild(c)) print(c.isparent(p))

7.5 덕 타이핑(Duck typing) 패턴 이해하기

  • 함수 내에서 인터페이스 처리

class Duck : def say(self) : return “quack quack”

class Person : def say(self) : return “Hello !”

def say(obj) : return obj.say()

d = Duck() p = Person()

print(say(d)) print(say(p))

  • class내에서 인터페이스 제공

class Duck : def say(self) : return “quack quack”

class Person : def say(self) : return “Hello !”

class Say : @staticmethod def say(obj) : return obj.say()

d = Duck() p = Person()

print(Say.say(d)) print(Say.say(p))

8. 메타 클래스(Meta Class)

8.1 메타 클래스와 클래스 정의로 클래스 만들고 비교하기

8.1.1 파이썬에서 메타 클래스로 클래스가 만들어지는 순서

  1. 적절한 클래스 결정
  2. 클래스 네임스페이스 준비
  3. 클래스의 본체 실행
  4. 클래스 객체를 생성하여 반환값으로 제공
  • 메타클래스로 클래스 생성하기

import pprint

namespace = { ‘name’ : “메타클래스로 클래스 생성”} bases = (object,) classname = “Klass”

Klass = type(classname, bases, namespace)

print(type(Klass)) print(Klass) pprint.pprint(Klass.dict)

class Klass : name = “ 클래스 정의문으로 클래스 생성”

print(type(Klass)) print(Klass) pprint.pprint(Klass.dict)

8.1.2 클래스 상속과 메타 클래스의 관계

  • 사용자 메타클래스로 변경하기

class MyMeta(type) : pass

class Klass(metaclass=MyMeta) : pass

print(Klass) print(Klass.class)

class A : def init(self) : self.b = b()

9. 연관관계(Association)

9.1 연관관계

  • 구성관계 (composition)

class Salary: def init(self,pay): self.pay=pay

def get_total(self):
    return (self.pay*12)

class Employee: def init(self,pay,bonus): self.pay=pay self.bonus=bonus self.obj_salary=Salary(self.pay)

def annual_salary(self):
    return "Total: "  +  str(self.obj_salary.get_total()+self.bonus)

obj_emp=Employee(100,10) print (obj_emp.annual_salary())

  • 집합 관계(Aggregation)

class Salary: def init(self,pay): self.pay=pay

def get_total(self):
    return (self.pay*12)

class Employee: def init(self,pay,bonus): self.pay=pay self.bonus=bonus

def annual_salary(self):
    return "Total: "  +  str(self.pay.get_total()+self.bonus)

obj_sal=Salary(100) obj_emp=Employee(obj_sal,10) print (obj_emp.annual_salary())

del obj_emp print(obj_sal)

9.2 위임 패턴 처리

  • 위임 메소드를 사용하기

class Person : def init(self,name,age) : self.name = name self.age = age

class Student : def init(self, name,age,college) : self.person = Person(name,age) self.college = college

s = Student(“연관”,22,”숭실대”) print(s.dict)

class Person : def init(self,name,age) : self.name = name self.age = age

def getname(self) :
    return self.name
def getage(self) :
    return self.age

class Student : def init(self, name,age,college) : self.person = Person(name,age) self.college = college

def getname(self) :
    return self.person.getname()

def getage(self) :
    return self.person.getage()

s = Student(“위임”,22,”숭실대”) print(s.getname()) print(s.getage())