📍이터레이터는 init() 함수와next() 함수를 이용하여 반복(Iterator)을 수행함
📍__iter__() 메서드
iter 메서드는 이터레이터 객체 자체를 반환한다. 이터레이터 객체는 __iter__() 메서드를 가지고 있어야 한다. 이는 파이썬에서 "순회 가능한(iterable)" 객체의 특징이다.
📍__next__() 메서드
next 메서드는 다음 요소를 반환한다. 만약 더 이상 반환할 요소가 없으면 StopIteration 예외를 발생시켜 순회를 종료시킨다. 이 메서드가 호출될 때마다, 다음 요소를 반환하고 내부 상태를 업데이트하여 다음 호출 시에 이전 요소의 다음 요소를 반환할 수 있도록 한다.
반복문 사용하여 이터레이터 사용하기
### 클래스 정의하기
- 아래 코드는 MyIterator 클래스를 정의하고 객체를 생성한 후, for 루프를 사용하여 이터레이터를 순회하고 있다. 이터레이터 객체를 생성하면 __init__ 메서드가 호출되고, 그 후에 __iter__ 메서드가 호출되어 이터레이터 객체를 반환한다. 그리고 for 루프에서 각 단계마다 __next__ 메서드가 호출되어 반복이 수행된다. 반복이 끝나면 StopIteration 예외가 발생하여 순회가 종료된다
### 클래스 정의하기
class MyIterator :
### 클래스 생성자 정의하기
def __init__(self) :
self.current_value = 0
print(f"#1 (__init__) : self={self} / self.current_value={self.current_value}")
### 자신의 클래스를 반환하는 iter 함수 정의
def __iter__(self) :
print(f"#2 (__iter__) : self={self}")
return self
### 반복을 수행하는 next 함수 정의
def __next__(self) :
print(f"#3 (__next__) : self={self}")
### current_valu의 값이 5보다 작을 때까지 반복 수행
if self.current_value < 5 :
# - 반환할 변수에 current_value의 현재값 저장
result = self.current_value
# - result 값 반환
self.current_value += 1
print(f"#4 : result={result} / self.current_value={self.current_value}")
# - return 값 반환
return result
else :
print("#5 : StopIteration 예외 발생")
### 이터레이터는 반복이 끝나면 종료시켜야함
# - 종료시키는 방법은 강제로 오류 발생시킴
raise StopIteration
### 이터레이터 실행시키기
- 클래스 생성하기
my_iterator = MyIterator()
#1 (__init__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510> / self.current_value=0
### 반복문 실행시키기
- 이터레이터 기능은 반복문(for or while)을 사용해야만 작동하는 기능임
- 최초 __iter__() 함수를 호출하고, 출력 시 __next__() 함수가 한 번씩 수행하면서 값을 반환받아서 출력함
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF601D110> #5 : StopIteration 예외 발생 이터레이터 종료
간단 실습
### 이터레이터 클래스 생성해서 Hello 각 단어 출력하기
- 클래스 이름 : StringIterator
- 임의 문자열을 받아서 처리한다.
- 임의 문자열은 외부에서 클래스 생성 시 넣어준다.
내가 만든 코드
class StringIterator:
def __init__(self, word):
self.word = word
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.word):
result = self.word[self.index]
self.index += 1
return result
else:
raise StopIteration
- 코드실행
string_iter = StringIterator("안녕하세요")
for v in string_iter:
print(v)
안 녕 하 세 요
강사님 코드
- p_text 지역변수, 매개변수로써의 의미
- self.text 는 멤버 변수
- 둘이 저장되는 위치가 다름
class StringIterator_:
### 클래스 생성자
def __init__(self, p_text) :
# - next 함수에서 1씩 증가시키면서 반복 조건에 사용할 변수
self.index = 0
# - 받아온 문자열
self.text = p_text
### 반복수행을 위한 iter 함수 정의
def __iter__(self):
return self
### 한건 한건 처리를 위한 next 함수 정의
def __next__(self) :
if self.index < len(self.text) :
### 문자열에서 문자 하나씩 추출하기
result = self.text[self.index]
self.index += 1
return result
else:
raise StopIteration
- 코드 실행
### 이터레이터 기능 사용하기
# 반복문을 이용해서 추출하기
# - 클래스 생성하기
msg = "Hello"
string_iter_ = StringIterator_(msg)
### 이터레이터를 사용하지 않고 메모리 체크하기
@profile
def no_iterator(limit) :
data = [i for i in range(1, limit+1)]
### 이터레이터를 사용해서 메모리 체크하기
@profile
def yes_iterator(limit) :
data = SimpleIterator(limit)
for item in data :
### 반복만 처리하고 별도 출력은 안함
pass
class EvenNumberIterator :
def __init__(self, start, end) :
self.start = start
self.end = end
def __iter__(self) :
return self
def __next__(self) :
if self.start <= self.end and self.start % 2 == 0 :
self.start % 2 == 0
result = self.start
self.start += 2
return result
else :
self.start += 1
raise StopIteration
- 코드 실행
num_iterator = EvenNumberIterator(1, 20)
for v in num_iterator :
print(v)
2 4 6 8 10 12 14 16 18 20
강사님 코드
class EvenNumberIterator :
def __init__(self, start, end) :
self.start = start
self.end = end
def __iter__(self) :
return self
def __next__(self) :
# - 사용하는 값은 self.start 값만 사용
for i in range(self.start, self.end, 1) :
### self.start가 짝수인지 체크
if self.start % 2 == 0:
### 반환할 변수에 저장
result = self.start
# - self.start값은 1 증가
self.start += 1
# - 반환하기 : 반환하면 for문은 종료됨
return result
### 짝수가 아니면
else :
# - 1증가만 시키고 반복을 계속 수행
self.start += 1
### for문을 이용한 경우에는, 이터레이터 반복 종류 후 마지막에 아래 추가
raise StopIteration
- 코드 실행
even_iter = EvenNumberIterator(1, 10)
for even in even_iter :
print(even)
2 4 6 8
외부 함수를 이용해서 짝수값 추출하는 이터레이터 만들기
1. 이터레이터 클래스 생성하기
class EvenNumberIterator :
### 클래스 생성자 정의
def __init__(self, start, end, func):
# - 시작값
self.start = start
# - 종료값
self.end = end
# - 외부함수
self.func = func
### 반복을 위한 이터레이터 함수 정의
def __iter__(self):
return self
### 반복 결과값을 처리할 함수 정의
def __next__(self):
### 시작부터 종료까지 while 반복
while self.start <= self.end :
### 외부함수로 짝수 or 홀수 체크
# - 짝수면 True, 홀수면 False
if self.func(self.start) :
result = self.start
self.start += 1
return result
else :
self.start += 1
### 이터레이터 종료하기
raise StopIteration
2. 짝수와 홀수를 판별하는 외부함수 정의하기
def is_even(num) :
return num % 2 == 0
3. 이터레이터 클래스 생성하기
- 함수 이름을 그대로 변수로 넣으면 함수 자체를 실행
even_iter = EvenNumberIterator(1, 10, is_even)
### 이터레이터 반복 수행하기
for v in even_iter :
print(v)
텍스트 파일의 내용을 한줄씩 반환하는 이터레이터 만들기
1. 이터레이터 클래스 생성하기
- 파일명은 클래스가 받아서 처리
- readline() : 파일 정보중에 줄 하나 가지고 오는 함수
>> 최초 이후부터는 다음 줄이 있는지 자동으로 체크 후 가지고 온다
>> 다음 줄이 없으면 안가지고 온다.
class FileLineIterator :
def __init__(self, file_path) :
# 파일 경로를 인스턴스 변수에 저장
self.file_path = file_path
# 파일 열기
self.file = open(file_path, "r", encoding = "utf-8")
def __iter__(self):
return self
def __next__(self):
print("#1 ---------------------")
# 파일에서 한 줄 읽어오기
line = self.file.readline()
# 읽어온 줄이 있으면 반환하고, 없으면 종료
if line:
# strip은 문자열의 양 끝에서 공백 문자(공백, 탭, 개행문자 등)을 제거하는 역할
return line.strip()
else :
# 파일 닫기와 StopIteration 예외 발생
print("#4 : 이터레이터 종료")
self.file.close()
raise StopIteration
DEPTNO로 조인을 하고 DNAME과 JOB으로 CUBE를 실행했다. CUBE는 전체합계와 각 칼럼별로 부분합계를 출력한다.
3. 다음의 SQL문에 대한 설명으로 올바르지 않은 것은?
가. 실제 데이터 DEPTNO SAL -------------------- 10 10 1000 10 2000 20 20 500
나. SELECT문 SELECT DEPTNO, SUM(NVL(SAL,0)) FROM DEPT GROUP BY DEPTNO;
1. SELECT문에 WHERE 조건이 없으므로 연산에 참여하는 총 행 수는 5개이다. 2. DEPTNO 10의 합계는 3000이고 20의 합계는 500이다. 3. NVL(SAL, 0)문에서 NVL은 NULL에 대한 합계오류를 예방한다. 4. 부서별 합계를 계산할 때 NULL값을 만나면 0으로 치환한다. ✏️
ALL 연산자는 서브쿼리(Subquery) 값 모두가 조건에 만족하면 True를 반환한다.
5. 자신의 속성이 없어도 다른 속성을 이용하여 결과를 도출할 수 있는 특징을 가진 속성의 이름은? 1. 설계 속성(Designed Attribute) 2. 기본 속성(Basic Attribute) 3. 파생 속성(Derived Attribute) 4. 관계 속성(Associative Attribute) ✏️
파생 속성(Derived Attribute)은 다른 속성을 이용하여 계산된 속성으로 자신의 고유값을 갖지 않고 파생, 유추되어 재산정될 수 있는 속성이다.
6. 엔터티에 대한 개념 중 엔터티 정의의 공통점 3가지가 아닌 것은? 1. 데이터베이스 내에서 변별 가능한 객체이다. 2. 엔터티는 사람, 장소, 물건, 사건, 개념 등의 명사에 해당된다. 3. 저장되기 위한 어떤 것(Thing)이다. 4. 업무상 관리가 필요한 관심사에 해당된다. ✏️
1. SELECT eno, ename, count(*) FROM employee e, dependent d WHERE e.eno = d.eno and count(*) >= 2 GROUP BY d.eno; 2. SELECT e.eno, e.ename, count(*) FROM employee e, dependent d WHERE EXISTS (SELECT * FROM dependent GROUP BY eno HAVING count(*) >= 2) GROUP BY e.eno, e.ename; 3. SELECT e.eno, e.ename, t.cnt FROM employee e, (SELECT eno, count(*) as cnt FROM dependent GROUP BY eno HAVING count (*) >= 2) t WHERE e.eno = t.eno; 4. SELECT e.eno, e.ename, count(*) FROM employee e, dependent d WHERE e.eno = d.eno GROUP BY e.eno, e.ename HAVING count(*) >= 3; ✏️
1. SELECT deptno FROM Dept WHERE deptno NOT IN (SELECT deptno FROM Emp); 2. SELECT deptno FROM Dept a WHERE NOT EXISTS (SELECT * FROM Emp b WHERE a.deptno =b.deptno); 3. SELECT b.deptno FROM Emp a RIGHT OUTER JOIN Dept b ON a.deptno = b.deptno WHERE empno IS NULL; 4. SELECT deptno FROM Dept WHERE deptno < > ANY (SELECT deptno FROM Emp); ✏️
4번 ANY()실행 시 하나라도 조건값을 만족하면 결과를 도출하기 때문에 모든 deptno 값이 도출 된다.
9. 실행 계획에 대한 설명으로 적절하지 않은 것은? 1. 실행 계획은 SQL문의 처리를 위한 절차와 방법이 표현된다. 2. 실행 계획이 다르면 결과도 달라질 수 있다. 3. 실행 계획은 액세스 기법, 조인 순서, 조인 방법 등으로 구성된다. 4. 최적화 정보는 실행 계획의 단계별 예상 비용을 표시한 것이다
정답 : 2 동일 SQL문에 대해 실행 계획이 다르다고 결과가 달라지지는 않는다. 그러나 실행 계획의 차이로 성능이 달라질 수 있다.
10. 사원 테이블에 사원번호는 기본키로 설정되어 있다. SQL문으로 사원번호 1번을 검색하는데 사원 테이블에는 하나의 ROW만 저장되어 있다. 이때 유리한 스캔 방식은 무엇으로 판단되는가? 1. Unique Index Scan 2. Non-Unique Index Scan 3. Index Full Scan 4. Table Full Scan ✏️
하나의 데이터(행)를 읽기 위해서는 인덱스를 사용하지 않고 테이블을 FULL SCAN하는 것이 효율적이다. 즉, 검색되는 행이 1건이므로 굳이 인덱스를 읽지 않고 바로 테이블을 검색해야 한다.
11. Case문에서 ELSE를 생략하면 어떤 현상이 발생되는가? 1. ELSE를 생략하고 작성하면 실행 시 ELSE 조건이 참이 되며 오류가 발생한다. 2. ELSE 조건이 만족하게 되면 공집합이 리턴 된다. 3. ELSE 조건을 만족하게 되면 무시된다. 4. ELSE 조건이 만족하게 되면 NULL이 된다. ✏️
CASE문은 IF~THEN~ELSE를 구현할 수 있는 SQL문이다. 즉, 어떤 조건이 참이면 A를 실행하고 그렇지 않으면 B를 실행하라는 것이다. CASE문에서 ELSE 조건을 생략하면 NULL이 되돌려진다.
12. 다음 모델의 배송 엔터티에서 고객의 정보를 찾을 때, 성능 향상과 SQL 문장을 단순화하는 가장 적절한 반정규화 방법은 무엇인가? (단, 주문목록 엔터티에서는 고객의 주식별자를 상속받기를 원하지 않음, 배송 엔터티에서는 고객 엔터티의 모든 속성을 참조하기를 원함) 1. 고객과 배송 엔터티의 관계를 추가(1:M관계)하는 관계 반정규화 2. 배송과 고객의 엔터티를 통합하는 반정규화 3. 배송 엔터티와 주문목록 엔터티 관계를 식별자 관계로 수정 4. 고객의 모든 정보를 모두 배송 엔터티의 속성으로 반정규화 ✏️
LIKE 검색 문자열 앞뒤에 모두 ‘%’ 기호를 붙였으므로 정상적인 Index Range Scan이 불가능하다.
14. 두 개 릴레이션 Student와 Department가 있을 때, 질의문 “SELECT * FROM Student s, Department d WHERE s.dept > 100;”을 수행하려고 한다. 이 질의 수행으로 생성되는 결과 릴레이션의 차수(Degree)와 카디널리티(Cardinality)는 각각 얼마인가? (단, 릴레이션 Student의 애트리뷰트 ‘소속(dept)’은 릴레이션 Department의 애트리뷰트 ‘코드(dno)’를 외부키로 참조한다)
차수(Degree)와 카디널리티(Cardinality)를 구하는 문제로 차수는 결과 릴레이션의 칼럼 수이다. 그래서 Student와 dept 테이블을 조인하여 모든 컬럼을 출력하기 때문에 (SELECT *) 각각 Student 테이블에서 5개 , dept 테이블에서 3개 , 총 8개의 컬럼(=차수)을 가지게 된다. 카디널리티는 선택된 행들의 개수이다. Where문을 보면 결과 릴레이션에서 Student 릴레이션의 dept값이 100보다 큰 것만 조회한다. 위 조건에 부합하는 결과 행수는 9건이므로 카디널리티는 9가 된다. (Student와 Dept를 카티션곱 조인하면 15개의 행이 나오고, 이중 where 조건에 부합하는 대상은 9건이다)
15. Subquery의 종류 중에서 Subquery가 Mainquery의 제공자 역할을 하고 Mainquery의 값이 Subquery에 주입되지 않는 유형은 무엇인가? 1. Filter형 Subquery 2. Early Filter형 Subquery 3. Associative Subquery 4. Access Subquery ✏️
데이터 모델링의 세 가지 관점 1. 데이터 관점 : 업무가 어떤 데이터와 관련이 있는지 또는 데이터 간의 관계는 무엇인지에 대해서 모델링 하는 방법(What, Data) 2. 프로세스 관점 : 업무가 실제로 하고 있는 일은 무엇인지 또는 무엇을 해야 하는지를 모델링 하는 방법(How, Process) 3. 데이터와 프로세스의 상관 관점 : 업무가 처리하는 일의 방법에 따라 데이터는 어떻게 영향을 받고 있는지 모델링하는 방법(Interaction)
- wrapper는 한번 사용 하면 없어지므로 대용량 데이터에 대해 return으로 결과 값만 받아와서 사용 - 함수의 재사용성을 확장한 개념의 방법 - 자바의 오버라이드와 비슷한 개념
<함수 실행 시간 확인하는 데코레이터 프로그래밍 작성>
### 데코레이터 함수 정의하기 - func : 실제 처리할 함수 받아오는 매개 변수 - 외부에 있는 처리하고하 하는 함수가 func로 들어와야 함
- *args : 함수에 임의의 개수의 위치 인자를 전달할 때 사용한다. 함수 정의에서 *args는 튜플로 위치 인자들을 받는다. 이를 통해 함수는 몇 개의 위치 인자가 전달되든지 처리할 수 있다.
- **kwargs : 함수에 임의의 개수의 키워드 인자를 전달할 때 사용한다. 함수 정의에서 **kwargs는 딕셔너리로 키워드 인자들을 받는다. 이를 통해 함수는 몇 개의 키워드 인자가 전달되든지 처리할 수 있다.
def timer_decorator(func) :
print(f"#1 : func = {func}")
### 실제 실행될 함수 정의(함수 이름은 자유롭게)
# - func로 받은 함수를 아래 함수로 재정의하게 됨
def wrapper(*args, **kwargs) :
# - 시작시간
start_time = time.time()
print(f"#2 : start_time = {start_time}")
### 실제 처리할 함수 : func
rs = func(*args, **kwargs)
print(f"#3 : rs = {rs}")
# - 종료시간
end_time = time.time()
print(f"#4 : end_time = {end_time}")
return rs
print("#5---------------")
return wrapper
### 데코레이터 호출 및 처리할 함수 정의하기
- 데코레이터 호출은 @로 사용함
- 데코레이터 함수 호출 시 및에 정의된 함수(exe_function)가 자동으로 전달 됨 - 매개변수 func은 메모리로 exe_function의 주소값을 이용 - 데코레이터 함수가 리턴한 wrapper함수는 exe_function이 된다. 즉, wrapper가 exe_function로 재정의 된다. - 맨 처음 함수는 3개. 주소도 3개 그러나 재정의 할 경우 exe_function의 주소는 wrapper의 주소로 바뀜
# (재정의 되는 시점)
@timer_decorator
def exe_function() :
print("실제 처리할 함수")
### 2초간 대기 : 데코레이터가 처리되는 시간을 벌어주기 위해서
time.sleep(2)
#1 : func = <function exe_function at 0x00000113C2EE1F80> #5---------------
### 실제 처리하기
exe_function()
#2 : start_time = 1699930626.0731795 실제 처리할 함수 #3 : rs = None #4 : end_time = 1699930628.0737495
#3: check_id = admin #4: args = ['인증성공'] 인증 결과 : 인증성공
<영문 소문자 2개를 매개변수로 받아서, 대문자로 변환하는 데코레이터 프로그램>
- 함수이름 자유롭게 생성
- 데코레이터 함수 생성해서 대문자로 출력하기
작성해본 코드
def change_lang(func) :
def lang_decorator(char1, char2) :
char1_upper = char1.upper()
char2_upper = char2.upper()
result = func(char1_upper, char2_upper)
return result
return lang_decorator
@change_lang
def change_function(char1, char2) :
print(f"첫 번째 문자: {char1}")
print(f"두 번째 문자: {char2}")
change_function('abcd', 'efgh')
첫 번째 문자: ABCD 두 번째 문자: EFGH
강사님 코드
def decorator_upper(func) :
# args : 값 여러개 받음
# kwargs : 리스트, 딕셔너리 받음
### 인자로 넘어온 2개의 소문자는 args 매개변수가 받게됨
def wrapper(*args, **kwargs) :
### 인자로 넘겨받은 2개의 소문자를 대문자로 처리하는 영역
args = [arg.upper() for arg in args]
### 대문자로 변환된 값 2개를 args에 리스트 타입으로 담아서 넘기면 됨
return func(*args, **kwargs)
return wrapper
### 데코레이터 호출 및 처리 함수 정의
@decorator_upper
def getUpper(param1, param2) :
print(f"대문자로 변환 : {param1} / {param2}")
### 처리하는 함수 호출 : 소문자 2개를 인자로 넘겨주기
getUpper("abcd", "efgh")
대문자로 변환 : ABCD / EFGH
데코레이터 실행 과정 이해하기
@decorator_upper가 실행 되면 getUpper함수가 같이 실행되면서
decorator_upper 주소 만들어지고 wrapper 주소 만들어짐 getUpper의 주소도 동시에 만들어졌지만 decorator_upper 호출되는 순간 getUpper 를 변수인 func에 넣게 됨 func는 getUpper의 주소를 갖는 변수가 된다 그때 return wrapper 하면 getUpper 가 바라보는 주소를 wrapper주소로 바꿔버림 wrapper 안에서 호출되는 func은 getUpper 의 주소를 받아서 갖고 있기 때문에 wrapper 에서 func의 원본인 getUpper 함수에 변수를 넣어 사용할 수 있는 것