728x90
반응형

<이터레이터(Iterator)>

📍파이썬에서 반복 가능한 객체(클래스)를 표현하는 데 사용되는 인터페이스

📍이터레이터는 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__() 함수가 한 번씩 수행하면서 값을 반환받아서 출력함

- 한번 반환된 후 메모리는 초기화되며, 다음 반복 시 다시 메모리 사용

- 메모리를 효율적으로 활용할 수 있음

- 반복 수행하여 result 값 출력하기

for value in my_iterator :
    print(value)
#2 (__iter__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=0 / self.current_value=1
0
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=1 / self.current_value=2
1
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=2 / self.current_value=3
2
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=3 / self.current_value=4
3
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=4 / self.current_value=5
4
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#5 : StopIteration 예외 발생

반복문 사용하지 않고 이터레이터 실행시키기

### 이터레이터 실행시키기

- 클래스 생성하기

my_iterator = MyIterator()
#1 (__init__) : self=<__main__.MyIterator object at 0x0000014EF601D110> / self.current_value=0

 

### 반복문 사용하지 않고 실행시키기

print(next(my_iterator))
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF5348E50>
#4 : result=0 / self.current_value=1
0

 

### try / except로 예외처리

try : 
    print(next(my_iterator))
    print(next(my_iterator))
    print(next(my_iterator))
    print(next(my_iterator))
except :
    print("이터레이터 종료")

 

- 처음 실행 했을 때 결과

3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=1 / self.current_value=2
1
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=2 / self.current_value=3
2
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=3 / self.current_value=4
3
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=4 / self.current_value=5
4

 

- 두 번 실행했을 때 결과

#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)

 

 

- 반복문을 이용해서 전체 추출하기

for char in string_iter_ :
    print(char)
H
e
l
l
o

 

- 따로따로 실행하기

print(next(string_iter_))
print(next(string_iter_))
print(next(string_iter_))
print(next(string_iter_))
print(next(string_iter_))
H
e
l
l
o

※ 예외처리를 안 했기 때문에 한번 더 실행하면 오류 발생

일반 프로그래밍 방식과 이터레이터 방식의 메모리 비교

### 메모리 확인을 위해 라이브러리 설치 필요

pip install memory-profiler

 

### 주피터 노트북 ver

## 라이브러리 불러오기

from memory_profiler import profile

### 주피터노트북에서는 아래 로드 처리 해야힘
%load_ext memory_profiler

 

## 이터레이터 클래스 생성하기

class SimpleIterator :
    def __init__(self, limit) :
        ### 반복 범위를 지정할 값(반복의 끝값)
        self.limit = limit
        ### 반복 시작값
        self.current = 0
        
    def __iter__(self) :
        return self

    def __next__(self) :
        if self.current < self.limit :
            self.current += 1
            return self.current
        else :
            raise StopIteration

 

## 데코레이터 함수 정의하기

### 이터레이터를 사용하지 않고 메모리 체크하기
@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

 

## 데코레이터 함수 호출하기

limit = 1000000
try:
    %memit no_iterator(limit)

    %memit yes_iterator(limit)
except:
    pass
ERROR: Could not find file C:\Users\user\AppData\Local\Temp\ipykernel_3736\513312623.py
peak memory: 130.37 MiB, increment: 25.96 MiB
ERROR: Could not find file C:\Users\user\AppData\Local\Temp\ipykernel_3736\513312623.py
peak memory: 104.40 MiB, increment: 0.00 MiB

 

### 파이썬 ver

새로운 파일에 기존 주피터 노트북에 적은 코드에서 로드해오는 %load_ext memory_profiler 코드 빼고 작성하기

py 형태로 파일 저장한 뒤  프롬프트에서 실행하기

## 데코레이터 함수 호출하는 코드 수정

if __name__ == "__main__" :
    ### 데코레이터 함수 호출하기
    limit = 1000000

    print("no_iterator ------------------------\n")
    no_iterator(limit)
    
    print("yes_iterator ------------------------\n")
    yes_iterator(limit)

 

## 프롬프트에서 실행시 명령어

python -m memory_profiler 04_iterator_memory_test.py

 


간단실습

### 두개의 숫자(시작값, 종료값) 값을 이용해서, 짝수값만 반환하는 이터레이터 만들기

 

내가 만든 코드

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

 

 

2. 이터레이터 클래스 생성하기

file_path = "./04_example.txt"
file_iter = FileLineIterator(file_path)

 

3. 반복해서 파일 내용 한줄씩 출력하기

for line in file_iter :
    print(line)

 

- 메모장 내용과 주피터 노트북 실행 결과

# 결과
#1 ---------------------
가가가
#1 ---------------------
나나나
#1 ---------------------
다다다
#1 ---------------------
라라라
#1 ---------------------
#4 : 이터레이터 종료

 

 

 

728x90
반응형
728x90
반응형

<데코레이터(Decorator)>

- 대량 데이터 처리할때 사용

- 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

<로그 데코레이터>

def log_decorator(func) :
    print(f"#1 : func = {func}")
    def wrapper(*args, **kwargs) :
        print(f"Logo : {func.__name__} 함수 호출됨")
        rs = func(*args, **kwargs)
        print(f"Logo : {func.__name__} 함수 종료됨")
        return rs
    print("#2-------------")
    return wrapper

 

### 데코레이터 호출 및 처리함수 정의

@log_decorator
def another_function():
    print("집에 가고싶다")
#1 : func = <function another_function at 0x00000113C3148B80>
#2-------------

 

### 처리함수 호출

another_function()
Logo : another_function 함수 호출됨
집에 가고싶다
Logo : another_function 함수 종료됨

<데코레이터 내부함수에서 처리된 결과를 받아보기>

# 함수 총 4개 사용
def check_permission(param_id) :
    print(f"#1: param_id = {param_id}")
    def decorator(func) :
        print(f"#2: func = {func}")
        def wrapper(*args, **kwargs) :
            check_id = "admin"
            print(f"#3: check_id = {check_id}")
            if check_id == param_id :
                args = ["인증성공"]
                print(f"#4: args = {args}")
                return func(*args, **kwargs)

            else :
                print(f"#5: raise 명령으로 강제 오류 발생시킴-----------")
                raise PermissionError("접근 불가")
        print(f"#6 -----------")
        return wrapper
    print(f"#7 -----------")
    return decorator

 

### 데코레이터 호출 및 처리함수 정의

@check_permission(param_id="admin")
def admin_function(re_msg) :
    print(f"인증 결과 : {re_msg}")
#1: param_id = admin
#7 -----------
#2: func = <function admin_function at 0x00000113C398B560>
#6 -----------

 

### 처리함수 호출

admin_function()
#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 함수에 변수를 넣어 사용할 수 있는 것


<실습> 어제 작성한 도서관리 프로그램에 데코레이터를 적용하기

https://mzero.tistory.com/42

 

[Python] 파이썬 도서 관리 프로그램 만들기 실습 2

주제 : 도서 입고/대출/반납 관리를 위한 키오스크 파이썬 프로그램 실습 1. 주요내용 : 실습1에서 작성한 도서 관리 프로그램에 데코레이터를 적용하기 데코레이터 추가한 코드 ### 기능을 수행

mzero.tistory.com

 

728x90
반응형
728x90
반응형

매개변수

 

### 위치 매개변수를 사용하는 함수

def function1(a, b) :
    print(a, b)

function1(1, 2)
1 2

 

### 기본값(default) 정의 매개변수를 사용하는 함수

 

▶ 기본값이 2번째 매개변수로 오는 경우

def function2(a, b=3) :
    print(a, b)

function2(1)
1 3

 

 매개변수에 모두 기본값을 설정한 경우

def function2(a=5, b=3) :
    print(a, b)

function2()
5 3

 

 기본값이 1번째 매개변수로 오는 경우 > 오류발생!

def function2(a=5, b) :
    print(a, b)

function2(1)
 Cell In[12], line 1
    def function2(a=5, b) :
                       ^
SyntaxError: non-default argument follows default argument

 

### 키워드 정의 방식

def function3(a, b) :
    print(a, b)
function3(a=3, b=5)
function3(a=6, b=4)
3 5
4 6

 

### 가변형 매개변수를 사용한 함수1
- 전달할 값이 몇개 인지 모를때 사용하는 방식
- 튜플타입으로 반환

def function4(*args) :
    print(args)
function4(1, 2, 3)
function4(1, 2, 3, 6, 9)
(1, 2, 3)
(1, 2, 3, 6, 9)

 

### 가변 키워드 매개변수를 사용한 함수 2
- 전달할 값이 몇개 인지 모를때 사용하는 방식
- ** 뒤에 변수 이름은 아무거나 상관없음. 그러나 대부부 args 사용
- 딕셔너리 타입으로 반환

def function5(**args) :
    print(args)
function5(a=1, b=2, c=3)
function5(a=1, b=2, c=3, d=6, e=9)

dic = {"a" : 33, "b" : 55}
function5(a=33, b=55) #딕셔너리를 넘기면 a=33, b=55의 형태로 변환되어 처리됨
function5(**dic)
{'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2, 'c': 3, 'd': 6, 'e': 9}
{'a': 33, 'b': 55}
{'a': 33, 'b': 55}
728x90
반응형
728x90
반응형

주제 :  도서 입고/대출/반납 관리를 위한 키오스크 파이썬 프로그램 실습

 

1. 주요내용 : 실습1에서  작성한 도서 관리 프로그램에 데코레이터를 적용하기

 

데코레이터 추가한 코드

### 기능을 수행할 클래스(모듈 or 라이브러리) 정의
class LibraryKiosk :
    ### 클래스 생성자 정의
    # self = 클래스의 모든 변수에 접근할 수 있도록 하는 것. 
    def __init__(self) :
        ### 도서 목록을 저장할 딕셔너리 변수 생성
        # - 도서번호, 도서제목, 도서재고수량 정보 담기
        self.books = {}
        print("클래스 생성")
        
    #--------------------------------------------------------------
    
    ### 도서입고 데코레이터 함수 정의하기|
    def add_book_decorator(func) :
        def wrapper(self, book_id, title, quantity):
            ### 입고할 도서번호가 존재하는지 확인
            # - 존재한다면 수량만 증가시키기
            func(self, book_id, title, quantity)
        return wrapper

    ### 도서대출 데코레이터 함수 정의하기
    def borrow_book_decorator(func):
        def wrapper(self, book_id, title, quantity):
            ### 대출하고자 하는 도서번호에 해당하는 재고수량 확인
            if (book_id in self.books) and (self.books[book_id]['quantity']) > 0 :
                self.books[book_id]['quantity'] -= 1
                print("대출이 완료되었습니다.")
                print(f"도서번호 : {book_id} / 제목 : {self.books[book_id]['title']} / 남은재고수량 : {self.books[book_id]['quantity']}")
            else:
                print("도서가 존재하지 않거나, 대출할 재고가 없습니다.")
        return wrapper

    ### 반납기능 데코레이터 함수 정의하기
    def return_book_decorator(func) :
        def wrapper(self, book_id, title, quantity):
            # - 반납할 도서번호가 도서목록에 있는지 확인
            if book_id in self.books :
                self.books[book_id]["quantity"] += 1
                print(f"도서번호 : {book_id} / 제목 : {self.books[book_id]['title']} / 남은재고수량 : {self.books[book_id]['quantity']}")
                
            # - 해당 도서가 목록에 없다
            else:
                print("해당 도서가 존재하지 않습니다.")
        return wrapper
            
    #--------------------------------------------------------------
    
    ### 도서입고 기능 정의하기
    @add_book_decorator
    def add_book(self, book_id, title, quantity) :
        ### 입고할 도서번호가 존재하는지 확인
        # - 존재한다면 수량만 증가시키기
        if book_id in self.books : # 여기서 books는 key값만 list로 가져옴
            # self.books[book_id]["quantity"] = self.books[book_id]["quantity"] + quantity
            self.books[book_id]["quantity"] += quantity
        # - 존재하지 않는다면
        else :
            # {book_id : {"tilte" : tilte, "quantity" : quantity} }
            # {"001" : {"tilte" : "파이썬 기초", "quantity" : 4} }
            self.books[book_id] = {"title" : title, "quantity" : quantity}
        print(f"도서번호 : {book_id} / 제목 : {title} / 입고수량 : {quantity} 입고성공:)")

    ### 도서 대출 기능 정의하기
    @borrow_book_decorator
    def borrow_book(self, book_id):
        ### 대출하고자 하는 도서번호에 해당하는 재고수량 확인
        if (book_id in self.books) and (self.books[book_id]['quantity']) > 0 :
            self.books[book_id]['quantity'] -= 1
            print("대출이 완료되었습니다.")
            print(f"도서번호 : {book_id} / 제목 : {self.books[book_id]['title']} / 남은재고수량 : {self.books[book_id]['quantity']}")
        else:
            print("도서가 존재하지 않거나, 대출할 재고가 없습니다.")

    ### 반납 기능 정의하기
    @return_book_decorator
    def return_book(self, book_id) :
        # - 반납할 도서번호가 도서목록에 있는지 확인
        if book_id in self.books :
            self.books[book_id]["quantity"] += 1
            print(f"도서번호 : {book_id} / 제목 : {self.books[book_id]['title']} / 남은재고수량 : {self.books[book_id]['quantity']}")
            
        # - 해당 도서가 목록에 없다
        else:
            print("해당 도서가 존재하지 않습니다.")
728x90
반응형
728x90
반응형

<클로저(Closure)>
- 함수 안에 함수를 만들어서 사용하는 방식
- 함수 안에 있는 함수는 바깥쪽 함수에서 참조해서 사용하는 방식으로 접근합니다.
- 함수 안에 함수는 사용이 끝나면 메모리에서 해제되기 때문에 유용하게 사용하면 좋습니다.

 

### 클로저 함수 정의하기

def outer_function(x) :
    print(f"#1 : x = {x}")
    ### 내부 함수 정의 : 실제 실행되는 함수
    def inner_function(y) :
        print(f"#2 : y = {y}")
        s = x + y
        print(f"#3 : s = {s}")
        return s
    print("#4 -------")    
    return inner_function

 

### 클로저 함수 호출하기

- outer만 호출되었기 때문에 inner는 메모리를 받아서 정의만 되고 실행되지 않았다.
- closure_exe는 0x000001DA19EF1800 값을 받은 inner함수 그 자체가 된 것이다.

 - closure_exe는 inner_function 자체를 리턴받은 함수를 의미함

closure_exe = outer_function(10) 

print(closure_exe)
#1 : x = 10
#4 -------
<function outer_function.<locals>.inner_function at 0x000001DA19DE9120>

 

### 내부 함수 호출하기
- 내부 함수는 실행되고 끝나면 소멸된다

result1 = closure_exe(5)
print(result1)
#2 : y = 5
#3 : s = 15
15

 

<클로저를 이용해서 프로그램 만들기>

- 클로저를 이용해서 누적합 계산하기
- 사용함수명 : outer_function2(), inner_function2(num)
- 사용변수 : total(누적된 값을 저장할 변수)
- total은 바깥 함수에 둬야 함.
- total은 지역변수가 아니므로 안쪽 함수에서 total을 가져오려면 nonlocal 선언을 해줘야 함.

- nonlocal : 클로저 구조에서는 상위 변수를 내부 함수에서 사용 못한다. 따라서 nonlocal을 지정해서 정의하면 외부 영역의 변수 사용 가능해진다. (바깥에 있는 total을 쓴다는 의미)    

def outer_function2() : 
    total = 0
    print(f"#1 : total = {total}")
    def inner_function2(num) :
        nonlocal total
        print(f"#2 : total = {total} / num = {num}")
        total += num
        print(f"#3 : total = {total} / num = {num}")
        return total
    print("#4 -------")   
    return inner_function2

 

### 클로저 함수 호출하기

res_fnc = outer_function2() 
print(res_fnc)
#1 : total = 0
#4 -------
<function outer_function2.<locals>.inner_function2 at 0x000001DA1A479580>

 

### 내부 함수 호출

rs_total = res_fnc(5)
print(rs_total)
#2 : total = 0 / num = 5
#3 : total = 5 / num = 5
5
rs_total = res_fnc(8)
print(rs_total)
#2 : total = 5 / num = 8
#3 : total = 13 / num = 8
13

 

<조건부 클로저 함수 프로그래밍>

 - condition의 값이 true이면 true_case함수 정의,  falsedlaus false_case함수 정의

def outer_function3(condition) :
    def true_case() :
        return "true_case 함수가 호출되었습니다."
    def false_case() :
        return "false_case 함수가 호출되었습니다."

    rs = true_case if condition else false_case 
    
    return rs

 

### 상위 함수 호출하기

rs_function = outer_function3(True)
print(rs_function)

rs_function = outer_function3(False)
print(rs_function)
<function outer_function3.<locals>.true_case at 0x00000231AA761A80>
<function outer_function3.<locals>.false_case at 0x00000231AA762160>

 

### 내부 함수 호출하기

rs_msg = rs_function()
print(rs_msg)
false_case 함수가 호출되었습니다.

 

728x90
반응형
728x90
반응형

<유니코드 프로그램>
- 언어를 표준화 시키는 방법
- 유니코드 국제코드 타입 UTP-8
- 유니코드 변환작업 : 인코딩(암호화), 디코딩(복호화)
- 유니코드(Unicode)는 전 세계의 모든 문자를 표현하기 위한 표준 인코딩 체계
- 유니코드 프로그램 : 언어, 문자, 특수기호 등의 표준화

 

### 문자열 결합하기

string1 = "파이썬(python)"
string2 = " 재미있어요"
string = "!!!"
print(string1 + string2 + string3)
파이썬(python) 재미있어요!!!

 

### 문자열을 UTF-8로 인코딩하기

text = "안녕하세요"
encode_text = text.encode("utf-8")
encode_text
b'\xec\x95\x88\xeb\x85\x95\xed\x95\x98\xec\x84\xb8\xec\x9a\x94'

 

### 문자열을 UTF-8로 디코딩하기

decode_text = encode_text.decode("utf-8")
decode_text
'안녕하세요'

 

### 간단 실습

- "안녕하세요" 문자열을 for문을 이용해서 각각 출력하기
- 인덱스 번호를 이용하는 방식 사용

 

만들어 본 코드

# 1
hi = list("안녕하세요")
print(hi)
for i in hi :
    print(i)

# 2
text = "안녕하세요"
for i in range(len(text)):
    print(f"{text[i]}")

 

결과

#1
['안', '녕', '하', '세', '요']






#2





강사님 코드

# 1
###for문 - 값을 이용해서 추출하는 방식
for v in decode_text : 
    print(v)

# 2
### for문 -인덱스 번호를 비용해서 추출하는 방식
for i in range(0, len(decode_text), 1) :
    print(decode_text[i])

 

결과

# 1






# 2




 

### 눈 모양 유니코드 만들기

# 1
char = "\u2603"
print(char)

# 2
# for문을 이용하지 말고, 눈사람 5개 출력해주세요
print(char * 5)
# 1


# 2
☃☃☃☃☃

 

### 십진수 숫자값을 문자로 변환하기

num = 65
print(num, chr(num))
65 A

 

### 문자열에서 특정 문자의 위치 찾기

string = "안녕하세요!!"

# "하" 라는 문자의 위치(인덱스) 찾기
print(string.index("하"))
2

 

### 문자 치환하기

string = "python 기초 문법"

# "python" 영문 문자열을 찾아서 :"파이썬" 한글명으로 수정하기
print(string.replace("python", "파이썬"))
파이썬 기초 문법
728x90
반응형

+ Recent posts