21장. 이터레이터 ,제너레이터, 연산자 오버로딩, 모듈

asda주asda·2022년 2월 12일
1

Python

목록 보기
26/31

이터레이터

for 문을 이용하여 리스트의 요소와 문자열의 요소에 접근이 가능하였다.

test_list =[1,2,3,4,5]
test_str = "안녕하세요"

for i in test_list:
    print(i, end='?')

for i in test_str:
    print(i, end='/')
    
출력 결과:
1?2?3?4?5?
안/////

파이썬에서는 for 루프와 함께 사용이 가능한 여러 종류의 반복 가능한 객체(iterable)가 있으며 이들 객체를 이터레이터(iterator)라고 정의한다. 이터레이션(iteration)이 '반복'을 의미하므로 '반복가능한 객체' 라고 해석할 수 있다.
: list, tuple, range, dictinary, set, string

반복 가능한 객체가 되려면 2가지의 메소드가 정의되어야 한다.

  • __iter__() 는 반복 가능한 객체 자신을 반환한다. 이걸 써야 자신이 반환되도록 할 수 있다.
  • __next__() 는 다음 반복을 위한 값을 반환한다. 만약 더 이상의 값이 없으면 Stopiteration 예외를 발생시켜야한다.

아래는 이터레이터 클래스를 정의한 것이다.

class MyCounter(object):
    # 생성자 정의
    def __init__(self, low , high):
        self.current = low
        self.high = high
    # 이터레이터 객체로서 자신을 반환한다.
    def __iter__(self):
        return self
    def __next__(self):
        # current 가 high 보다 크면 StopIteration 예외를 반환한다.
        if self.current > self.high:
            raise StopIteration
        # current 가 high 보다 작으면 다음 값을 반환한다.
        else:
            self.current += 1
            # self.current 가 증가했으나 그 이전값을 반환해야한다.
            return self.current -1
test = MyCounter(1,10)
print(test)
for i in test:
    print(i, end='')
출력 결과:
<__main__.MyCounter object at 0x00000166531FB400>
12345678910

아래는 일반적인 리스트를 이터레이터로 쓸 수 있게하는 코드의 예시이다.

# StopIteration 예외 발생시키기 실습

list1= [10,20,30]

# iter() 내장함수로 리스트를 이터레이터로 쓸 수 있도록 설정
list1_iter = iter(list1)
# list1 =list1.__iter__() # 이걸 써도 같은 결과이다.

# __next__()를 이용하여 값을 가져오도록 한다.
print("결과값: ",list1_iter.__next__())
print("결과값: ",list1_iter.__next__())
print("결과값: ",list1_iter.__next__())
# 아래코드를 실행하면 더 이상 꺼내올 리스트의 값이 없으므로 StopInteration 예외가 발생한다.
print("결과값: ",list1_iter.__next__())

출력결과:
결과값:  10
결과값:  20
결과값:  30
Traceback (most recent call last):
  File "C:/Users/pc/OneDrive/바탕 화면/프로그래밍 공부/Python", line 14, in <module>
    print("결과값: ",list1_iter.__next__())
StopIteration

제너레이터

제너레이터(generators)는 키워드 yield를 사용하여 함수로부터 반복 가능한 객체를 생성하는 방법이다. 우리는 yield 문장을 사용하여 함수를 제너레이터로 만들 수 있다.

앞의 이터레이터는 클래스를 이용하여 반복가능한 객체를 생성하는 것이며, 제너레이터는 함수를 이용하여 반복 가능한 객체를 생성하는 것이다.

아래의 예는 yield 문장을 사용하여 제너레이터를 생성하고 for문을 이용하여 이터레이터로 사용하는 것이다.

def test_generator_1():
    yield 'first'
    yield 'second'
    yield 'third'

def test_generator_2(low, high):
    while low < high:
        yield low
        low += 1

for i in test_generator_1():
    print(i)

for j in test_generator_2(20, 25):
    print(j , end=' ')
    
출력 결과:
first
second
third
20 21 22 23 24 

yield from

함수 내에서 yield from 뒤에 올 수 있는 것은 반복가능한 객체, 이터레이터, 제너레이터가 올 수 있다.
아래의 경우 list가 왔다.

# yield from 을 사용한 실습
import time

def gen():
    list1 = [10,20,30]
    yield from list1
    # yield와 동일하게 리스트의 요소의 값을 하나씩 보내준다.
    # 하지만 대기상태로 빠지지 않는다. 그렇기에 아래의 2줄은 실행되지 않는다.
    time.sleep(2)
    print('gen()함수 내에서의 list1의 요소의 값: ', list1)

if __name__ == '__main__':
    test_gen = gen()
    n = next(test_gen)
    print("main() 내에서의 n의 값1: ",n)
    n = next(test_gen)
    print("main() 내에서의 n의 값1: ", n)
    n = next(test_gen)
    print("main() 내에서의 n의 값1: ", n)
    # 아래코드에서는 더이상 가져올 것이 없기 때문에
    # StopIteration 예외가 발생함을 알수 있다.
    # n = next(test_gen)
    # print("main() 내에서의 n의 값1: ", n)

출력 결과:
main() 내에서의 n의 값1:  10
main() 내에서의 n의 값1:  20
main() 내에서의 n의 값1:  30

제너레이터와 이터레이터의 차이점

이터레이터와 제너레이터의 차이점은 yield 를 쓰냐 안쓰냐의 차이도 있지만 가장 큰 차이는 이터레이터는 모든 동작을 완료한 후 결과를 한번에 메모리에 적재 시키는 것에 반해, 제너레이터는 각각의 yield에서 한번 실행 시킨 후 대기 상태에 들어가 결과를 반환하고, 이후 다음 코드를 진행하여 또다시 yield를 만날 경우 다시 대기 상태에 들어가 결과를 반환하는 방식이다.

import time
def gen(count):
    start = 1
    while start <= count:
        yield start
        time.sleep(2) # yield start 실행 후 2초간 대기
        start += 1

for i in gen(3):
    print(i, end= "")

출력 결과:
1 2 3

위의 코드에서는 time.sleep 을 통해 값을 먼저 전달한 후, 그 값을 출력한 다음, 다시 함수의 다음 코드를 진행함을 확인할 수 있다.

'1을 출력 후 2초 간 대기, 다시 2를 출력 후 2초간 대기, 다시 3를 출력 후 2초간 대기 그리고 종료' 의 순으로 이루어진다. 즉 yield를 이용해 값을 함수 밖으로 보내고 대기 상태에 들어간 후 실행 순서를 양보하여 함수 밖의 print(i, end =" ")가 출력 후 실행 순서가 함수로 다시 되돌아온다는 것이다.

연산자 오버로딩

연산자 오버로딩이란.

두 문자열 객체를 결합하기 위해서는 + 연산자를 이용하며, 비교를 하기 위해서는 ==, >, < 연산자를 사용한다. 이 연산자들은 실제로는 str 클래스 안에 정의된 메소드이다. 연산자를 메소드로 정의하는 것을 '연산자 오버로딩(operator overloading)'이라한다.
연산자 오버로딩을 이용하면 여러 가지 연산자들을 클래스안의 메소드들로 연결할 수 있다. 그리고 이들 연산자 오버로딩 메소드들은 여러가지로 이용된다. 그렇기에 직접 클래스를 정의할 때 이 연산자 오버로딩 메소드들을 적절하게 정의하여야 한다.

예를 들어 str 객체에서의 연산자 ==를 사용하면 str 클래스의 __eq(self, other)__ 메소드가 호출된다.
연산자 +는 __add(self, other)__이 호출되는 것이다. 그렇기에 필요하다면 클래스의 메소드로 찾아서 수정하여도 된다.

str_1 = '안녕 하세요'
str_2 = '하세요 안녕'
result_str = str_1.__add__(str_2)
print(result_str)

출력 결과:
안녕 하세요하세요 안녕

클래스에서 연산자 정의하기

사용자가 클래스를 정의할 때 +, -, * 와 같은 기본 연산자들의 기능을 정의할 수 있다.
예를 들어서 2차원 좌표평면에서 두 개의 좌표를 연산자 +를 이용하여 합할 수 있다면 편리할 것이다.
기본적으로는 인스턴스 객체의 +연산은 따로 정의하지 않았다면 적용되지 않는다. 하지만 아래와 같이 정의한다면 연산자 +를 사용하여 좌표들의 합을 계산할 수 있다.

class dot(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
	# + 연산자를 위한 메소드
    def __add__(self, other):
        return  dot(self.x + other.x, self.y + other.y)
    # print() 함수를 사용하기 위한 메소드
    def __str__(self):
        return '(%s, %s)' %(self.x, self.y)

dot_1 = dot(1,2)
dot_2 = dot(4,3)
result_dot = dot_1 + dot_2
print(result_dot)

출력 결과:
(5, 5)

class Numbox(object):
    def __init__(self, num):
        self.num = num
    def __add__(self, num):
        self.num += num
    def __sub__(self, num):
        self.num -= num

n = Numbox(40)
# 객체와 int 간에는 연산이 이루어지지않는다.
# 하여 클래스 안에 __add__() 특수 메소드를 재 정의하면 비로소 연산이
# 이루어지고 출력이 되는 것을 볼 수 있다.
# n + 100
n.__add__(100)
print(n.num)

# n - 110
n.__sub__(110)
print(n.num)

# __add__()를 재정의를 한다고 해도 아래와 같이 자리를 바꿔버리면 에러가
# 발생한다. 이때는 + 연산자는 __add__()를 호출하지 않고 __radd__()를 호출한다.
# r이 앞에오면 순서를 바꿀 수 있다.
110 + n

모듈

모듈이란.

모듈(module)이란 함수나 변수 또는 클래스들을 모아놓은 파일이다. 파이썬 프로그램에서는 모듈을 불러와서 사용할 수 있다. 파이썬에서는 기본적으로 똑똑한 사람들이 만들어 놓은 모듈들을 제공해준다.

파이썬 프로그램의 코드가 길어지면 유지 및 보수를 쉽게하기 위하여 여러 개의 파일로 분할 할 수 있다. 또한 파일을 사용하면 한번 작성한 함수를 복사하지 않고 여러 프로그램에서 사용할 수 있다.

모듈을 불러올 때는 2가지 방법을 사용한다.

  • import 모듈
    import를 이용해 불러온 모듈은 '모듈.모듈의 정보' 의 형태로 사용된다. 예를 들어 math 모듈의 pi 객체를 가져오고자 한다면 import math를 입력 한 후 math.pi의 형태를 사용한다. 모듈의 별칭을 만들어서 사용할 수 있는데, import math 를 import math as halo 라고 한다면 기능은 같지만 이름만 다르게 math.pi 의 형태로 사용할 수 있다.

  • from 모듈 import 모듈의 정보
    from 을 이용하여 불러온 모듈은 '모듈의 정보' 형태로 사용한다 가졍 math의 pi를 사용하고자 한다면 from math import pi를 한후 pi를 사용한다. from으로 사용하였을 때 모든 정보를 가져오고자 한다면 모듈의 정보 자리에 *을 넣는다.

# fibo.p 에서 정의된 함수들

# 피보나치 수열 모듈
def fib(n):    	# 피보나치 수열 출력
   a, b = 0, 1
   while b < n:
       print(b, end=' ')
       a, b = b, a+b
   print()

def fib2(n): 	# 피보나치 수열을 리스트로 반환
   result = []
   a, b = 0, 1
   while b < n:
       result.append(b)
       a, b = b, a+b
   return result


# main.py

#import fibo 
from import fibo *

fib(100)
result_list = fib2(100)
print(result_list)

출력 결과:
1 1 2 3 5 8 13 21 34 55 89 
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

유용한 모듈들

프로그래밍에 있어서 중요한 원칙중 하나는 이전에 개발된 코드를 적극적을 재활용하는 것이다.
파이썬에서는 다양한 분야의 전문가들이 작성한 유용한 모듈들이 존재한다. 이는 많은 테스트를 거쳤기에 안정되며 효율적이다. 그렇기에 이를 적극적으로 활용하여 효율적인 코드를 작성할 수 있다.

copy 모듈

파이썬에서는 객체를 복사할 때, 2가지 방법으로 복사할 수 있다.

  • 얕은 복사(shallow copy): 객체의 참조값(주소)을 복사하는 것
  • 깊은 복사(deep copy): 참조값이 복사가 된것이 아닌 내용 자체가 복사되어 새로운 객체를 만드는 것

만일 깊은 복사를 하고자 한다면 copy의 deepcopy 함수를 이용하면 된다.

import copy

colors = ["red", "blue", "green"]
clone = copy.deepcopy(colors)
clone[0] = "white"

print(colors)
print(clone)

출력 결과:
['red', 'blue', 'green']
['white', 'blue', 'green']

random 모듈

random 모듈은 난수를 발생할 때 사용하는 모듈이다. 난수는 다양하게 사용된다. 메이플을 할때 데미지의 범위를 알려주고 그안에서 임의의 데미지가 나오는 것을 예로 들수 있다.

random 모듈은 난수를 발생시키는 다양한 함수들을 지원한다.

randint()

정수 범위의 난수를 생성하려면 randint를 사용한다. randint(1, 6)와 같이 정수 구간을 인수로 주고 범위 내의 난수를 생성해준다. randint(1, 6)의 경우 1,2,3,4,5,6 중 하나를 반환해준다

import random

print(random.randint(1, 6))
print(random.randint(1, 6))

출력 결과:
6
3

random()

0.0 에서 1.0 미만의 난수를 반환해준다. 만일 범위를 늘리고자 한다면 곱하면 된다.

import random

print(random.random()*100)

출력 결과:
81.1618515880431

choice()

주어진 시퀀스의 항목 중 랜덤하게 하나를 반환해준다.

import random

myList = [ "red", "green", "blue" ]
print(random.choice(myList))

출력 결과:
'blue'

shuffle()

리스트의 항목을 랜덤하게 섞는다.

import random

myList = [ [x] for x in range(10) ]
random.shuffle(myList)
print(myList)

출력 결과:
[[3], [2], [7], [9], [8], [1], [4], [6], [0], [5]]	# 매번 달라짐

randrange(start, stop(,step))

range(start, stop, step) 구간으로부터 랜덤하게 요소를 생성한다.

import random
random.randrange(0, 101, 3)

출력 결과:
81

sys 모듈

sys 모듈은 파이썬 인터프리터에 대한 정보를 제공해준다.

  • prefix
    파이썬이 설치된 경로를 출력해준다.
import sys
# 파이썬이 설치된 경로 출력
print(sys.prefix)

출력 결과:
C:\Users\pc\AppData\Local\Programs\Python\Python38
  • path
    모듈을 참조할 때 사용하는 경로를 반환해준다.
import sys
print(sys.path)

출력 결과:
['C:\\Users\\pc\\OneDrive\\바탕 화면\\프로그래밍 공부\\Python', 
'C:\\Users\\pc\\OneDrive\\바탕 화면\\프로그래밍 공부\\Python', 
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python38\\python38.zip', 
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python38\\DLLs', 
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python38\\lib', 
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python38', 
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages']
  • version
    설치된 파이썬의 버전을 알려준다.
import sys
print(sys.version)

출력 결과:
3.8.7 (tags/v3.8.7:6503f05, Dec 21 2020, 17:59:51) [MSC v.1928 64 bit (AMD64)]

calendar 모듈

calendar 모듈을 이용하면 여러 가지 형태의 달력을 출력할 수 있다. 예를 들어서 2016년 8월 달력을 출력하는 코드는 다음과 같다.

import  calendar
test = calendar.month(2019,12)
print(test)

출력 결과:
   December 2019
Mo Tu We Th Fr Sa Su
                   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

0개의 댓글

관련 채용 정보