클래스
학생들의 시험점수에 대해 합, 평균, 중간값, 최빈값, 그리고 표준편차를 계산하고자 합니다. 이러한 계산들을 다른 그룹에 적용하고 추후에 계속 사용하기 위해서 함수(function)로 작성할 것입니다. 다음은 각 항목을 계산하기 위한 함수들입니다.
def sum(x): #합 re=0 for i in x: re += i return(re) def mean(x): #평균 re=0 for i in x: re += i return(re/len(x)) def median(x): #중간값 value=sorted(x) n=int(len(value)/2) if n % 2 ==0: med=(value[n-1]+value[n])/2 else: med=value[n] return(med) def mode(x): #최빈값 re={} for i in x: re[i]=x.count(i) v=list(re.values()) k=list(re.keys()) return(k[v.index(max(v))]) def std(x): #표준편차 n=len(x) mu=mean(x) sd0=0 for i in x: sd0 += (i-mu)**2 sd=(sd0/n)**(1/2) return(sd)
numpy.random 모듈의 randint() 함수를 사용하여 [50, 100] 사이의 정수 100개를 무작위로 생성하였습니다.
np.random.seed(0) x=np.random.randint(50, 101, 100) x=list(x) x[:10]
[94, 97, 50, 53, 53, 89, 59, 69, 71, 100]
print(f"합: {sum(x)}\n평균: {mean(x)}\n중간값: {median(x)}\n최빈값: {mode(x)}\n표준편차: {std(x)}")
합: 7253 평균: 72.53 중간값: 70.5 최빈값: 50 표준편차: 14.739372442543136
위에서 작성한 함수는 같은 유형의 결과를 얻기 위한 다른 자료에도 사용할 수 있습니다. 다음과 같이 이 함수들을 하나의 함수로 그룹화할 수 있습니다.
def simpleStatic(x): tot=sum(x) mu=mean(x) med=median(x) mod=mode(x) sd=std(x) return([tot, mu, med, mod, sd]) simpleStatic(x)
[7253, 72.53, 70.5, 50, 14.739372442543136]
데이터에 대해 합, 평균, 중간값, 최빈값, 그리고 표준편차을 계산하기 위해 위 함수 simpleStatic()을 사용할 수 있지만 이 함수를 사용하기 위해서는 5개의 함수들을 인터프러터에 부착하여야 합니다. 이 단점은 위의 5개 함수를 중첩함수로 포함하고 있는 외부함수(객체)를 사용하는 것으로 해결됩니다. 이와 유사한 개념인 클래스(Class)라는 코딩 방법을 사용할 수 있습니다. 클래스는 키워드 class를 사용하며 기본 구조는 식 1과 같습니다.
class 클래스이름(다른 클래스이름):
속 성 #클래스의 상태를 나타내는 특성
메소드 #클래스내에서 이루어지는 동작 | (식 1) |
식 1의 속성(attribute)은 클래스의 상태를 나타내는 것이며 메소드(method, 멤버함수)는 클래스 내에서만 작동하는 함수입니다. 이 식의 클래스 이름 직후에 위치하는 괄호에는 다른 클래스 이름을 입력할 수 있습니다. 현재의 클래스가 그 객체의 속성과 메소드를 호출하여 적용하기 위한 장치입니다. 이것을 상속(inheritance)이라합니다. 이 장치는 생략 할 수 있습니다.
위 함수 simplStatic()과 동일한 이름의 클래스를 작성합니다. 먼저 데이터의 합과 평균을 계산할 메서드만을 생성합니다. 메서드 역시 함수이므로 키워드 def로서 정의합니다.
class simpleStatistic: def sum(self, x): re=0 for i in x: re += i return(re) def mean(self, x): re=0 for i in x: re += i return(re/len(x))
클래스도 객체이므로 식 2와 같이 다른 객체에 할당할 수 있습니다.
객체이름 = 클래스이름() | (식 2) |
다음 코드의 객체 math와 같이 클래스를 참조하는 객체는 클래스의 모든 부분과 연결된 것이므로 속성, 메소드를 사용할 수 있습니다. 이렇게 클래스와 연결된 객체를 인스턴스(instance)라고 합니다. 인스턴스는 생성과 동시에 클래스의 모든 부분을 참조할 수 있으므로 식 3과 같이 클래스의 모든 속성과 메소드를 dot 연산자(.)를 사용하여 연결할 수 있습니다.
객체이름.속성 | (식 3) |
객체이름.메소드() |
math=simpleStatistic() math.sum(x)
7253
math.mean(x)
72.53
키워드 class로 클래스임을 선언하면 다음과 같은 다양한 내장 속성과 내장 메소드들이 부여됩니다.
print(dir(simpleStatistic))
['__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__', 'mean', 'sum']
위 결과와 같이 클래스 생성시 부여되는 속성과 메서드들은 새로 작성되는 메소드들에 전달되어야 합니다. 이러한 연결은 각 메소드의 첫 번째 인수인 self에 의해 이루어집니다. 즉, self는 클래스가 소유하는 모든 특성과 연결되는 객체(인수)입니다. 그러므로 클래스 특성들 뿐 아니라 각 메소드 실행으로 생성되는 결과 역시 self라는 인수에 저장할 수 있습니다. 이 인수는 메소드 실행시 자동으로 연결되는 것으로서 메소드 호출시 별도로 전달할 수 없습니다(식 4). 물론 이 인수의 이름은 self라는 용어로 고정된 것은 아닙니다.
객체.메소드(self, 인수들): | 에러 발생 | (식 4) |
객체.메소드(인수들): | 실행 |
클래스의 메소드 mean()
은 이전 메소드 sum()
과 같은 코드를 이용했습니다. 위 결과와 같이 mean()와 sum() 메소드 역시 simpleStatistic 클래스가 소유하는 특성들로 인수 self와 연결되어 있습니다. 그러므로 그 메소드는 self.sum()와 같이 dot 연산자
를 사용하여 mean() 메소드 내에서 호출할 수 있습니다. 위 mean() 메서드의 코드들은 다음과 같이 대체할 수 있습니다.
⋮ def mean(self, x): mu=self.sum(x) return(mu/len(x))
위 클래스의 모든 메소드는 self외에 객체 x를 인수로 사용합니다. self는 클래스 문법에 의해 메소드의 첫번째 인수로 전달하는 것이 필수 사항이지만 그외 x와 같은 인수는 조정이 가능합니다. 즉, 클래스의 시작과 동시에 공통인수 또는 필수적인 부분을 호출하도록 할 수 있습니다. 이것은 다음 코드와 같이 클래스에 자동으로 부여되는 내장 속성인 __init__
를 활성화시키는 것으로 달성할 수 있으며 생성자(constructor)라고 합니다. 이 생성자는 클래스에서 다른 메소드들보다 먼저 시작되는 곳에 위치하며 실행결과는 인수 self를 통해 다른 메소드들에 전달됩니다.
⋮ def __init__(self, obj): self.obj=obj ⋮
위에서 언급한 부분들을 고려하여 클래스 sipleStatistics는 다음과 같이 수정됩니다.
class simpleStatistic: subject="math" #① def __init__(self, obj): #② self.obj=obj #③ def sum(self): #④ total=0 for i in self.obj: total +=i self.total=total return(self.total) def mean(self): #⑤ tot=self.sum() self.mu=tot/len(self.obj) return(self.mu)
math=simpleStatistic(x) math.subject
'math'
math.sum()
7253
math.mean()
72.53
위 코드의 각 부분은 다음과 같습니다.
- ① 클래스 속성(식 3)
- ② 생성자(constructor)에 클래스와 연결할 객체를 인수로 전달합니다.
- 생성자는 내장 메소드인
__init__()
를 사용하여 클래스를 초기화합니다. 즉, 최초로 인수를 전달받습니다. - 클래스의 메소드는 첫 번째 인수로 전달되는 self로 모든 클래스 내용들과 연결됩니다.
- 생성자의 self 인수는 클래스 생성시 부여되는 많은 내장 속성, 내장 메소드를 포함하며 클래스에서 작성된 속성(①)과 메서드들을 연결합니다.
- 인수 self는 인스턴스를 클래스와 연결하는 장치입니다.
- self 인수의 명칭은 다른 단어로 대체할 수 있습니다.
- 생성자는 내장 메소드인
- ③ 생성자 메소드에서 전달한 obj를 self에 포함시킵니다.
- ④ 데이터의 합을 계산하는 메소드로서 첫번째 인수인 self를 통해 필요한 구성요소들과 연결됩니다.
- ⑤ 평균을 계산하는 메서드로서 인수 self를 통해 sum()를 사용합니다. 이 메서드의 경우 self를 통해 데이터와 연결되어 있으므로 별도의 인수가 필요하지 않습니다. (식 4).
위에서 생성한 인스턴스 math의 동작은 클래스 simpleStatistic에 의해 조정됩니다. 즉, 인스턴스는 그 작동 범위가 클래스에 의해 결정됩니다.
위 클래스 simpleStatistic에 중간값, 최빈값, 표준편차를 계산할 메소드를 추가합니다.
class simpleStatistic: """데이터의 합계, 평균, 중간값, 최빈값, 그리고 표준편차를 계산합니다.""" subject="math" def __init__(self, obj): self.obj=obj def sum(self): total=0 for i in self.obj: total +=i self.total=total return(self.total) def mean(self): tot=self.sum() self.mu=tot/len(self.obj) return(self.mu) def median(self): value=sorted(self.obj) n=int(len(value)/2) if n % 2 ==0: med=(value[n-1]+value[n])/2 else: med=value[n] self.med=med return(self.med) def mode(self): re={} for i in self.obj: re[i]=self.obj.count(i) v=list(re.values()) k=list(re.keys()) self.mod=k[v.index(max(v))] return(self.mod) def std(self, df=0): n=len(self.obj)-df mu=self.mean() sd=0 for i in self.obj: sd += (i-mu)**2 self.sd=(sd/n)**(1/2) return(self.sd)
math=simpleStatistic(x) math.subject
'math'
print(f"합계: {math.sum()}\n평균: {math.mean()}\n중간값: {math.median()}\n최빈값: {math.mode()}\n표준편차: {math.std()}")
합계: 7253 평균: 72.53 중간값: 70.5 최빈값: 50 표준편차: 14.739372442543136
댓글
댓글 쓰기