티스토리 뷰

Python

Python, 클래스(Class)

hwangyoungjae 2016. 5. 9. 11:07
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

C++ Java처럼 Python Class라는 개념을 이용한 객체지향프로그래밍이 가능하다지금까지 배운 함수나 제어문만으로도 원하는 기능을 구현할수 있지만 추상화를 통하여 공통된 부분을 기본클래스로 작성하고추가적인 기능을 하위클래스에서 확장하는 방식을 통해좀더 효율적이며 간결하게 구현이 가능하다.

 

C++ Java와 같은 객체지향언어를 공부한 사람이라면 상속(Inheritance), 다형성(Polymorphism), 정보은닉(Information Hiding)과 같은 용어를 한번쯤은 들어봤을것이다이러한 용어들은 Class와 자주 어울리는 친구들이다.

 

간단한 예를 통하여 위의 개념들에 대하여 알아보겠다.



Triangle이라는 Class에는

삼각형의 세 꼭지점을 나타내는 pointA, pointB, pointC 그리고

색과 중심점의 위치를 나타내는 color, position의 정보가 있다.

이정보들을 속성 또는 변수라 칭할수 있다.

또한 삼각형의 색을 변경하는 함수로 changecolor()

도형을 회전시키는 rotate()라는 함수가 있다.

이를 method라 칭한다.

 

, changecolor()메소드와 rotate()메소드는 오직 삼각형의 데이터를 변경하는 용도로만 사용되기 때문에 이 속성들과 method Triangle이라는 클래스로 묶어 놓았다고 할수 있다.

 

위의 Triangle이라는 클래스는 일종의 도장과 같은 역할을 한다.

삼각형이 필요할때마다 도장을 찍듯이 인스턴스(Instance)객체를 생성하여 사용하면 된다.

 

기본적으로 인스턴스 객체는 생성이 완료된 직후원본클래스와 동일한 데이터와 메소드를 가지고 있다.

아무런 제한없이 삼각형 내부의 정보에 접근하거나 정보를 변경할수 있다면 삼각형 본연의 특성조차 잃어버릴수 있다이러한 경우를 방지하고자 클래스에서는 데이터와 클래스내부에서만 사용되는 함수를 외부에서 접근할수 없게 한다이러한 방식을 정보은닉(Information Hiding)이라고 한다.

 

작업이 진행됨에 따라 추가적으로 사각형,원등의 도형이 필요하게 되었다각 도형마다 클래스를 작성하여 보니 공통적으로 사용되는 데이터(color,position)와 함수(changecolor,rotate)가 클래스마다 중복된것을 확인하였다이를 최소화 하기 위하여 각 도형클래스에서 공통된 부분을 추출하여 기본클래스로 작성하는 작업을 추상화(Abstraction)라고 한다추상화 결과로 작성된 클래스를 부모클래스(Super Class)라고 하고이를 받아서 각 특성을 추가한 클래스를 자식클래스(Sub Class)라고 한다.

부모클래스로부터 공통된 요소를 물려받은 관계를 '상속관계'라고 하며부모클래스가 둘 이상인경우를 '다중상속'이라 한다.

 

반면에 rotate()함수와 같은 경우는 사용자의 입장에서는 각 도형이 입력된 각도에 맞도록 회전시키는것으로 보이지만개발자의 입장에서는 도형의 중앙점으로부터 삼각형은 3개의 점을사각형은 4개의 점을 이동시켜야만 한다즉 같은 부모클래스를 통하여 상속받은 동일한 멤버함수호출에 대하여 각각의 클래스는 다른 작업을 수행해야하만 한다동일한 인터페이스에 대하여 구체적인 인스턴스마다 다른 동작을 수행하는 특징을 다형성(Polymorphism)이라고 한다.



Class 안에 작성된 코드(함수)를 Method

Class 안에 작성된 데이터()를 Attribute

Class에서 생성된 Instance는 Data는 다르지만 Method는 같다.

Instance에서 method를 사용시 Class에 있는 Method를 호출하여 사용함

 

멤버변수 = 해당클래스내에 있는 변수

멤버메소드 = 해당클래스내에 있는 메소드

 

>> 클래스 선언 <<

일반적으로 클래스는 데이터와 메소드로 구성된다하지만 이것이 반드시 필요한것은 아니다아래의 예제는 데이터와 메소드가 모두 없는 가장 단순한 경우이다이 경우에도 선언과 동시에 클래스객체가 생성된다.

즉 클래스 선언을 통해 새로운 이름공간이 생성되는 것이다.

>>> class myclass: #클래스 정의

             pass

 

>>> dir() #생성된 이름공간의 확인(myclass)

['__builtins__', '__doc__', '__name__', '__package__', 'myclass']

 

이번에는 멤버변수와 멤버메소드를 가지고 있는 클래스를 아래와 같이 정의하였다.

이 클래스는 이름을 표현하는 멤버데이터와 그 이름을 출력하는 메소드를 가지고 있다.

>>> class person:

             name='default name'

             def PRINT(self):

                           print(self.name)

 

>>> p1=person()

>>> p1.PRINT()

default name

클래스를 정의하면 클래스객체가 생성되고 독립적인 이름공간이 만들어진다이렇게 생성된 공간에 멤버변수와 메소드가 존재하게 되는것이다.

이렇게 정의한 클래스를 사용하기 위해서는 일반적으로 인스턴스 객체를 만들어야 한다.

인스턴스 객체 생성은 클래스의 이름을 사용하여 함수를 호출하는 형태를 갖게 되며클래스와 동일하게 인스턴스 객체가 생성되고 독립적인 이름공간이 생성된다기본적으로 인스턴스 객체가 변경되기 전까지는 클래스객체와 동일한 데이터와 메소드를 가리킨다.

>>> class person:

             name='default name'

             def PRINT(self):

                           print(self.name)

 

>>> p1=person()

>>> id(person.name), id(p1.name)

(16178624. 16178624) #instance의 데이터를 변경전 class의 데이터를 참조하고 있다.

>>> p1.name='p1' #instance의 데이터 변경

>>> id(person.name), id(p1.name) #instance의 데이터가 변경된후 다른 이름공간을 참조하고 있는것을 알수 있다.

(16178624, 16176928)

>>>

인스턴스의 데이터가 변경되면클래스의 데이터와 구분하기 위하여 인스턴스이름공간에 변경된 데이터를 저장한다.

반면에 아직 변경되지 않은 데이터와 메소드는 여전히 클래스와 공유하고 있다.

 

클래스와 인스턴스 모두 각 멤버변수와 멤버메소드에 접근하기 위해서는 속성접근자(".")를 사용한다.

일반적으로 속성접근자는 "객체이름.멤버메소두또는 "객체이름.멤버변수형태로 사용된다.

 

또한 파이썬에서는 기본적으로 클래스인스턴스의 모든 멤버변수와 메소드의 접근권한은 public이다즉 외부에서 모든 클래스의 내용을 쉽게 확인/변경이 가능하다.

파이썬의 설계철학은 개발자에게 많은 제약을 가하지 않는 것이기에 기본적인 접근권한은 public으로 하였으며정보은닉을 위하여 이름변경(Naming Mangling)이라는 방법이 제공된다.

 

멤버메서드정의를 보면 첫 인자로 self라는 것이 있다이것은 현재 인스턴스 객체를 가리키는 것으로 C++ Java this키워드와 동일하지만파이썬에서는 특별히 예약어로 지정되어 있지 않다이를 통하여 인스턴스의 이름공간에 접근하기 때문에 클래스공간의 정적메서드나 클래스메서드를 제외하고 명시적으로 메서드의 첫 인자는 인스턴스객체가 된다물론 self가 아닌 다른 표현이 가능하지만 이미 파이썬프로그래밍 세계에서는 self를 관용적으로 사용하고 있다추후 다른사람에 의하여 유지보수가 되는 경우를 고려하면 이러한 표현을 다르는 것이 좋다.

 

-. 바운드메서드언바운드메서드

기본적으로 클래스의 메서드는 클래스객체의 이름공간에 선언된다이러한 이유로 인하여 인스턴스객체가 클래스의 메서드를 호출하면자기이름공간에 대한 정보를 호출하는 메서드에게 넘겨줘야 한다메서드 호출시 암묵적으로 첫 인자로 인스턴스 객체를 넘기는 호출방식을 바운드메서드(Bound Method)호출이라 한다이때에는 메서드 정의시 첫인자가 인스턴스 객체임을 선언하나호출시에는 자동으로 반영되기에 명시적으로 입력하지 않는다반면에 메서드 호출시 명시적으로 첫 인자로 인스턴스 객체를 넘기는 호출방식을 언바운드메서드(Unbound Method) 호출이라 한다이때에는 클래스 객체를 통하여 메서드를 호출하며첫 인자로 인스턴스 객체를 입력하여야 한다.

p1.print() #바운드메서드호출

person.print(p1) #언바운드메서드호출

 

>> 클래스객체와 인스턴스객체의 이름공간 <<

기본적으로 인스턴스객체를 통하여 변수나 함수의 이름을 찾는 경우 아래의 순서로 그 이름을 찾는다.

인스턴스객체영역 -> 클래스객체영역 -> 전역영역

어떤 인스턴스 객체를 통하여 변수에 접근하면인스턴스 객체의 이름공간을 우선 찾고찾지 못하는 경우 클래스 영역전역영역순으로 찾게 되며 그렇게 해도 찾지 못하는 경우 AttributeError예외가 발생한다.

 

>>> class person:

             name='default name'

 

>>> p1=person()

>>> p2=person()

>>> print(p1.name)

default name

>>> print(p2.name)

default name

생성된 두 인스턴스객체는 클래스의 데이터를 참조하고 있음

 

>>> p1.name='kim'

>>> print(p1.name)

kim

>>> print(p2.name)

default name

인스턴스객체 p1의 멤버데이터 name 'kim'으로 변경하면 인스턴스p1의 이름공간에 존재하는 name이라는 변수에 변경된 데이터를 저장한다.

반면 p2의 멤버데이터 name은 변경되지 않았기 때문에 여전히 클래스객체의 데이터를 참조하고 있다.

>>> id(person.name),id(p1.name),id(p2.name)

(16521696, 16688288, 16521696)

 

또한 인스턴스객체 생성후 클래스객체에 새로운데이터를 추가하면 인스턴스객체에서도 클래스객체의 데이터의 접근이 가능하다.

>>> person.title='new titme'

>>> person.title

'new titme'

>>> p1.title

'new titme'

>>> p2.title

'new titme'

 

하지만 인스턴스객체 p1에만 데이터가 추가된경우 p2에서는 접근이 불가하다.

>>> p1.age='20'

>>> person.age

Traceback (most recent call last):

  File "<pyshell#102>", line 1, in <module>

    person.age

AttributeError: type object 'person' has no attribute 'age'

>>> p1.age

'20'

>>> p2.age

Traceback (most recent call last):

  File "<pyshell#104>", line 1, in <module>

    p2.age

AttributeError: 'person' object has no attribute 'age'

인스턴스객체 p1에만 age변수를 추가한후 클래스객체와 인스턴스객체p2에서 접근이 불가능한것을 알수 있다.

 

코딩시 주로 발생하는 실수중 하나가 클래스메서드내에서 인스턴스(self)를 통하지 않고 변수에 접근하는 것이다.

만약 전역영역의 변수와 클래스의 변수의 이름이 동일한경우에러도 발생하지 않기 때문에 규모가 큰 프로그램에서는 이런문제를 찾기가 쉽지 않다.

data='not class member'

class string:

    data=''

    def set(self,msg):

        self.data=msg

    def Print(self):

        print(data) #self를 이용하여 클래스멤버를 접근하지 않는 경우 이름이 동일한 전역 변수에 접근하여 출력함

 

g=string()

g.set('first message')

g.Print()

 

실행결과

not class member

 

위 예제는 전역영역과 클래스영역에 동일한 이름인 data라는 변수가 존재한다.

변수이름이 같아도 다른 영역에 존재하지 때문에 당장은 문제가 되지 않는다하지만 멤버메서드인 Print()내부에서 개발자의 실수로 인스턴스객체 self를 통하지 않고 전역변수 data값을 출력하고 있다그렇기 때문에 의도와는 다르게 출력결과를 전역변수 str의 값이 출력된다.

 

※ __class__

인스턴스객체가 자신을 생성한 클래스 객체를 참조하기 위하여 파이썬에서는 인스턴스객체의 내장속성 '__class__'가 있다클래스 영역에 모든 인스턴스 객체에 공통된 데이터를 참조하기 위하여 아래와 같이 사용한다.

>>> class test:

             data='Default'

 

>>> i2=test()

>>> i1=test()

>>> i1.__class__.data='클래스데이터변경'

>>> i1.data

'클래스데이터변경'

>>> i2.data

'클래스데이터변경'

>>> i2.data='i2데이터만변경'

>>> i1.data

'클래스데이터변경'

>>> i2.data

'i2데이터만변경'

>>> i2.__class__.data

'클래스데이터변경'

위의 코드에서는 인스턴스객체 'i1'에서 내장속성 __class__를 이용하여 클래스 이름공간의 멤버변수 'data'값을 변경하였다그 결과 앞어 생성된 인스턴스객체 'i2' 'data'까지 영향을 받게 되었다.

하지만 인스턴스객체 'i2'의 이름공간에 멤버변수 'data'를 추가하는 경우우선적으로 인스턴스 이름공간을 탐색하기 때문에 인스턴스 이름공간의 데이터를 출력한다물론 인스턴스 객체에 동일 이름의 데이터가 존재하더라도 __class__를 이용하여 클래스 이름공간의 데이터에 접근이 가능하다.

 

>> 클래스객체와 인스턴스객체의 관계 <<

인스턴스객체가 어떤 클래스로부터 생성되었는지 확인하는 방법으로 isinstance()라는 내장함수를 사용할수 있다.

결과는 불린형태로 반환된다.

사용예 : isinstance(인스턴스객체,클래스객체)

 

클래스간의 상속관계가 있는 경우에도 자식클래스의 인스턴스객체는 부모클래스의 인스턴스로 평가된다.

또한 클래스객체 정의시 어떠한 상속을 받지 않더라도버전3이후로는 암묵적으로 object객체를 상속받고 있다.

자료형 또한 object객체에서 파생된다.

>>> class person:

             pass

 

>>> class student:

             pass

 

>>> p,s=person(),student()

>>> isinstance(p,person)

True

>>> isinstance(s,student)

True

>>> isinstance(p,object)

True

>>> isinstance(int,object)

True

>>> isinstance('abc',str)

True

2.2버전이전에는 isinstance()함수가 지원되지 않기 때문에 아래와 같이 확인할수 있다.

>>> type(p) == person

True

 

>> 생성자,소멸자 <<

Java C++와 동일하게 파이썬에서도 클래스생성시 초기화작업을 위한 생성자메서드와

메모리해제등의 종료작업을 위한 소멸자메서드를 지원하고 있다.

생성자메서드는 인스턴스객체가 생성될때 자동으로 호출되며소멸자메서드는 인스턴스객체의 참조카운터가 '0'이 될때 호출된다.

클래스내부적으로 이를 지원하기 위하여 생성자메서드 __init__(), 소멸자메서드는 __del__()로 미리 정의되어 있다.

생성자메서드의 경우 함수호출시 인자를 전달하는 것과 동일하게 인스턴스객체 생성시 초기화할 멤버변수값을 전달할수 있다.

class myclass:

    def __init__(self,value): #생성자메서드

        self.value=value

        print('class is created! value = ',value)

 

    @staticmethod

    def __del__(): #소멸자메서드

        print('class is deleted!')

 

def foo():

    d=myclass(10) #foo()함수 블록안에서만 인스턴스객체d가 존재함

foo()

 

실행결과

class is created! value =  10

class is deleted!

myclass클래스는 생성시 초기값으로 1개의 인자를 받는다클래스의 인스턴스객체는 foo()함수의 내부에 생성되어함수블록을 벗어나게 되면 자동으로 소멸된다따라서 foo()함수만 호출해도 객체의 생성자와 소멸자가 호출되게 된다.

 

명시적으로 del구문을 사용한다고 클래스객체의 소멸자함수(__del__)가 항상 호출되는것은 아니다.

인스턴스객체를 생성한 이후참조카운터가 1개 이상 존재한다면 del구문을 사용하여도 소멸자는 호출되지 않는다.

위에서 정의한 myclass를 이용하여 확인해보도록 하겠다.

>>> c=myclass(10) #인스턴스객체생성-생성자호출(참조카운터:1)

class is created! value =  10

>>> c_copy=c #참조카운터증가:2

>>> del c #참조카운터감소:1

>>> del c_copy #참조카운터감소:0-소멸자호출

class is deleted!

 

 

참조 : 빠르게 활용하는 파이썬프로그래밍

'Python' 카테고리의 다른 글

Python, 클래스(Class) - 상속  (0) 2016.05.09
Python, 클래스(Class) - 연산자중복정의  (0) 2016.05.09
Python, 정적메소드,클래스메소드  (0) 2016.05.09
Python, @property  (0) 2016.05.09
Python,module copy [객체복사]  (0) 2016.04.29
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함