티스토리 뷰

파이썬을 공부하면서 파이써닉한 코드는 무엇인지 한번 공부하고 가야겠다고 생각했습니다. 

제가 공부한 부분을 정리하고 기록하려 합니다. 

파이썬은 아름다운 하나의 답이 존재한다라는 철학을 가지고 있습니다. 같은 일을 하는 코드가 있을때 거기에는 하나의 정답이 존재한다 라는 철학이죠 그것이 파이써닉입니다. 

어떻게하면 파이써닉한 코드를 작성 할 수 있을지 몇가지분류로 공부해보겠습니다

 

  • comprehension
  • generator
  • f string
  • swap
  • extended  slice
  • slots (yet)
  • kwards (yet)

 


1. comprehension

 

Comprehension이란 iterable한 오브젝트를 생성하기 위한 방법입니다. 축약이라고 해석되며 이 축약은 list, set, dict을 만드는데 사용됩니다. 

간단한 예제로 혹시 0부터 9까지 담고있는 배열을 어떻게 만들어야 할까요 ?

if __name__ == "__main__":
    arr = []
    for i in range(0, 10):
        arr.append(i)
    print(arr)

혹시 이렇게 하고 계시진 않으신가요 ?

 

list comprehension은 이것을 간단하게 해줍니다. 

if __name__ == "__main__":
    arr = [i for i in range(0, 10)]
    print(arr)
    

같은 기능을 하는 코드입니다. 한줄로 축약을 시킬 수 있죠. 이것을 해석하자면 [ 표현식 for i in ... ] 이렇게 표현을 하는데 i에 대해 표현할 수있고 그것은 자유입니다. 

if __name__ == "__main__":
    arr = [0 for _ in range(0, 10)]
    print(arr) # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    arr = [i*i for i in range(0, 10)]
    print(arr) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    data = [100, 200, 300, 400]
    arr = [val / 100 for val in data]
    print(arr) # [1.0, 2.0, 3.0, 4.0]

 for 문에서 _는 value 값을 신경안쓰겠다는 의미 입니다. 이렇든 0으로 초기화 할 수도있고 어떠한 배열에 관련된 배열을 다시 재생성 할 수도 있습니다. 

 

if __name__ == "__main__":
    arr = [i for i in range(0, 10) if i % 2 == 0]
    print(arr) # [0, 2, 4, 6, 8]

    arr = [i if i % 2 == 0 else 0 for i in range(0, 10)]
    print(arr) # [0, 0, 2, 0, 4, 0, 6, 0, 8, 0]

list comprehension을 할때 반복문을 순회하면서 조건에 따라 데이터를 또 변경할 수 있습니다. if문이 안으로 들어올 수 있는데요 첫번째 arr을 보시면 i가 짝수 일때만 배열에 추가하는 코드입니다.

두번째 arr은 else를 사용하는 comprehension인데요 이때는 순서가 조금 바뀝니다 if else가 앞에 위치하게 됩니다.

 

#two demension
if __name__ == "__main__":
    arr = [[0 for _ in range(5)] for _ in range(5)]
    print(arr)

이를 응용하여 2차원 리스트를 만들때도 간단히 만들 수 있습니다. list를 생성하는데 정말 간단해지죠? 파이썬을 사용하신다면 comprehension을 적극적으로 사용하셔야 합니다 ! comprehension은 pythonic 합니다.

 

2. generator

 

두번째는 generator입니다. generator란 iterator를 생성하는 함수입니다. 함수입니다. 만드는 방법은 yeild 키워드를 사용하면 자연스럽게 generator가 됩니다. 

일단은 어떻게 사용하는지 보고 가겠습니다.

 

def test():
    for i in range(7):
        yield i

if __name__ == "__main__":
    arr = test()
    print(arr)
    for val in arr:
        print(val)
        
"""
<generator object test at 0x0362EFB0>
0
1
2
3
4
5
6
"""

test함수는 generator입니다. 0부터 6까지 순회하는 for문을 가지고 있는데요 그안에 yield 키워드가 있는 것을 볼 수 있습니다. 쉽게 생각하면 yield는 return이라고 생각하시면 됩니다 i를 리턴하는 것이죠. 여기서 generator의 특징이 보이는데요 return을 하더라도 그때의 상태가 함수 내부변수에 기록됩니다. 첫번째 yield i 를 한 순간 0을 반환하고 그자리에서 멈춰있습니다. 그다음 또 호출이 되면 또 yeild 가 나올때가지 진행한 후 멈춥니다. 1을 반환하고 멈추게 되겠죠. main 함수에서 보면 for문으로 접근하는데 있어서 list랑 별 다를 것이 없어 보입니다. 하지만 내부적으로는 한번 부를때마다 계산하고 리턴되는것입니다. generator는 완성된 리스트가 아닌 필요에 따라 값을 계산해서 반환하는 함수 입니다. 

그래서 arr을 print해보면 generator object라고 나오죠 만약 리스트라면 그 안에 리스트 값이 나왔을 텐데 말이죠.

또한 generator는 한번 순회하면 다시 사용하지 못하는 단점이 있습니다. 

 

generator를 사용하는 이유는 일단 메모리를 절약할 수 있고 필요에 따라 계산하는 특성 덕분에 무한한 순서가 있는 객체를 모델링 할 수 있습니다. 예를 들면 피보나치수를 만드는데 이용할 수 있습니다. 

def combinations(iterable, r):
    # combinations('ABCD', 2) --> AB AC AD BC BD CD
    # combinations(range(4), 3) --> 012 013 023 123
    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return
    indices = list(range(r))
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)

파이썬 내장함수에서도 generator를 많이 이용합니다. 

또한 

if __name__ == "__main__":
    arr = (i for i in range(5))
    print(arr) # <generator object <genexpr> at 0x032CFFB0>

comprehension으로도 generator를 생성 할 수 있습니다. [ ] 이 아니라 ( ) 으로 감싸주면됩니다. generator expression이라고 하는것 같습니다.

 

3. f string

 

파이썬에서 문자열을 표현하는 방법은 여러가지가 있습니다. 그중에서 가장 최근에 나오고 깔끔한 표현방법이 f string입니다. 파이썬 3.6버젼에서 처음으로 나온것으로 알고 있습니다. 사용법은 쉽습니다.

f"", f'' 를 사용하면 되고 변수를 표현하고 싶을때는 중괄호를 통해 사용하시면 됩니다. 

 

if __name__ == "__main__":
    data = ["john", 12]
    print(f"i'm {data[0]} and {data[1]} years old")
    # i'm john and 12 years old

 

4. swap

 

이전에 프로그래밍 언어를 배우신 분들이라면 특히 C언어를 배우신 분들이라면 swap을 하려 하시면 

a = 1000
b = 10

temp = a
a = b
b = temp

print(a, b)

이러한 방법으로 코딩 하셨을 겁니다. 하지만 파이썬에서는 이렇게 하지 않습니다.

if __name__ == "__main__":
    a = 1000
    b = 10
    
    a, b = b, a
    print(a,b)

간단하게  swap 할 수 있습니다.

 

4. extended slice

파이썬은 리스트를 슬라이싱하는 부분이 굉장히 편하게 되어있는데요 이것을 더 편하게 해주는 놈입니다. 사실 slice에 있는 기능입니다. 예전에 추가된 내용이죠 근데 모르고 지나치는 분들이 많습니다. 

 

if __name__ == "__main__":
    arr = ['a', 'b', 'c', 'd', 'e']
    print(arr[:])
    print(arr[1:])
    print(arr[:1])
    print(arr[1:3])
    """
    ['a', 'b', 'c', 'd', 'e']
    ['b', 'c', 'd', 'e']
    ['a']
    ['b', 'c']
    """

 

보통 slice를 이렇게 사용하십니다. arr[start:end] 로 많이 사용하시죠 하지만 여기에 하나 숨겨져 있는게 있습니다 .

arr[start:end:step] 스텝이라는 부분입니다. 

if __name__ == "__main__":
    arr = ['a', 'b', 'c', 'd', 'e']
    print(arr[::2])
    print(arr[1::2])
    print(arr[1:5:3])
    """
    ['a', 'c', 'e']
    ['b', 'd']
    ['b', 'e']
    """

앞에 start -> end로 가는데 몇칸씩 넘어가며 접근할꺼냐 정의하는것이 step입니다.  혹시 이것을 사용할까 생각하시나요 ?

 

만약 arr을 뒤에서부터 e d c b a 순으로 접근하려고 하신다면 어떻게 하실것인가요? 내장함수인 reversed함수를 써서 리스트를 역순으로 만들어 놓고 접근 할 수도 있을 것입니다. reversed 했을때 iterator를 반환하므로 list로 다시 캐스팅 하는것도 귀찮죠. 하지만 extended slice를 사용한다면 쉽게 이를 구현할 수 있습니다.

 

if __name__ == "__main__":
    arr = ['a', 'b', 'c', 'd', 'e']
    print(list(reversed(arr)))
    print(arr[::-1])
    """
    ['e', 'd', 'c', 'b', 'a']
    ['e', 'd', 'c', 'b', 'a']
    """
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함