3-2. Pythonic code

유승우·2022년 5월 2일
0

Pythonic Code


  • Python 에서 주로 많이 쓰는 파이썬스러운 코딩 기법

  • 파이썬 특유의 문법을 활용하여 효율적으로 코드를 표현

  • why Pythonic Code?

    • 남이 만든 코드에 대한 이해도를 높이기 위해 사용
    • 미세하지만 효율적인 코드를 짤 수 있다.

Split & Join


  • split()을 사용해 조금 더 쉽게 데이터를 자를 수 있으며, Join을 통해 데이터를 합칠 수 있다.
items = "zero one two three"
items.split(" ")
>>> ['zero', 'one', 'two', 'three']

# unpacking
example = "temlab.technology.io"
subdomain, domain, tld = example.split(".")
print(subdomain)
>>> 'teamlab'

List comprehension


  • 기존의 List를 사용해 간단하게 다른 List를 만드는 기법
  • 파이썬에서 가장 많이 사용되는 기법 중 하나이며, 일반적으로 for + append 보다 조금 더 효율적이다.
result = [i for i in range(10)]
>>> [0,1,2,3,4,5,6,7,8,9]

result = [i for i in range(10) if i % 2 == 0]
>>> [0,2,4,6,8]

# Nested For loop
word_1 = "Hello"
word_2 = "World"
result = [i+j for i in word_1 for j in word_2] # 이중 for문과 같음

case_1 = ["A","B","C"]
case_2 = ["D","E","A"]
result = [i+j for i in case_1 for j in case_2 if not(i==j)] # 이중 for문에 조건까지 포함

# i와 j가 같지 않으면 i+j, 같을 경우는 i만
result = [i+j if not(i==j) else i for i in case_1 for j in case_2]

# 24글자가 모두 포함되어 있는 문장이라 자주 보인다
words = 'The quick brown fox jumps over the lazy dog'.split()
# list의 각 elemente들을 대문자, 소문자, 길이로 변환하여 two dim list로 변환
stuff = [[w.upper(), w.lower(), len(w)] for w in words]

# 옆으로 쭉 출력되서 보기 힘든 경우 pprint를 사용하면 깔끔하게 보인다
pprint.pprint(stuff)

Two dimensional vs One dimensional


# One dimensional
result = [i+j for i in case_1 for j in case_2]

# Two dimensional
# 뒤의 for문이 일반적인 for loop의 맨 위 for문과 같다
result = [[i+j for i in case_1] for j in case_2]

Enumerate


  • enumerate : ‘열거하다’ 라는 뜻으로 리스트의 원소를 추출할 때 번호를 붙여서 추출
for i , v in enumerate("ABC"):
		print("{0} \t {1}".format(i,v))

Zip


  • 두 개의 리스트의 값을 병렬적으로 추출
alist = ["a1","a2","a3"]
blist = ["b1","b2","b3"]

[[a,b] for a,b in zip(alist,blist)]
>>> [['a1','b1'],['a2','b2'],['a3','b3']]

# 번호와 각 리스트의 값을 병렬적으로 함께 추출 가능
for i , v in enumerate(zip(alist,blist)):
		print(i, v)

Lambda


  • 함수 이름 없이, 함수처럼 쓸 수 있는 익명 함수
  • Python 3부터는 권장하지는 않으나 여전히 많이 쓰임
  • PEP 8에서는 lambda의 사용을 권장하지는 않음 why?
    • 어려운 문법
    • 테스트의 어려움
    • 문서화 docstring 지원 미비
    • 코드 해석의 어려움
    • 이름이 존재하지 않는 함수의 출현
    • 그래도 많이 쓴다....?
# lambda 바로 뒤에는 parameter 값, : 뒤에 return이 되는 값을 작성
f = lambda x, y : x + y

Map


  • 시퀀스형 데이터가 있을 때 함수를 각각 적용하여 매핑해주는 것
  • 실행 시점의 값을 생성하여, 메모리를 효율적으로 사용 → 여러 개의 함수를 쓸 때 복잡하지 않게 코드를 짤 수 있다.
  • 대신 사용자가 이해하기 어려워 질 수 있다.
ex = [1,2,3,4,5]
f = lambda x: x ** 2

list(map(f,ex))
>>> [1,4,9,16,25]

Reduce


  • map 과 달리 리스트에 똑같은 함수를 적용하여 통합
from functools import reduce

# 1과 2를 더한 값 3이 다음번 x가 되고, 3과 4를 더하여 다음 x가 되는 구조
print(reduce(lambda x,y : x+y, [1,2,3,4,5]))
💡 Lambda , map, reduce는 간단한 코드로 다양한 기능을 제공하지만, 코드의 직관성이 떨어져서 권하지는 않는 방식임 💡 하지만 다양한 머신러닝 코드에서는 여전히 많이 사용 중

iterable objects


  • 시퀀스형 자료형에서는 데이터를 순서대로 출력하는 것을 iterable data라 한다.
  • iterable data는 iternext를 사용해 iterable 객체를 iterator object로 사용한다
cities = ["Seoul", "Busan", "Jeju"] # 메모리에 올리기

iter_obj = iter(cities) # 메모리 주소 가져오기
print(next(iter_obj)) # 다음의 메모리주소의 값 가져오기
print(next(iter_obj))
print(next(iter_obj))

Generator


  • iterable object가 모든 개념을 포함하는 거라면, generator는 특수한 형태이다.
  • element가 사용되는 시점에 값을 메모리에 반환하고, 그 전에는 주소만 가지고 있는다.
  • 굉장히 많은 데이터가 있을 때, 메모리에 한번에 올려놓는 것보다는 대기를 하고 있다가 필요할 경우에만 불러 사용하는 것이 좋기 때문에 generator를 사용한다.
# 일반적인 리스트
def general_list(value):
	result = []
	for i in range(value):
			reuslt.append(i)
	return result

# 주소만 가지고 있는 상태
def generator_list(value):
	reuslt = []
	for i in range(value):
			yield i # yield 를 통해 값을 호출

# 값 호출
for a in generator_list(50):
	print(a)

Generator comprehension


  • list comprehension과 유사한 형태로 generator 형태의 list 생성
  • 아직 생성만 해놓고 실제론 값을 안 넣은 상태
gen_ex = (n*n for n in range(500)) # [] 대신 () 사용
print(type(g))

when generator


  • list 타입의 데이터를 반환해주는 함수는 generator로 만드는 것이 좋다.
  • 큰 데이터를 처리할 때는 generator comprehension을 고려
  • 파일 데이터를 처리할 때도 generator를 사용하자

passing arguments


  • 함수에 argument를 넘기는 방법은 다양하다

  • keyword arguments

    • 함수에 입력되는 parameter 변수명을 사용하여 arguments를 넘김
def print_somthing(my_name,your_name):
		print("Hello {0}, My name is {1}".format(your_name,my_name))

print_somthing("Sungchul", "TEAMLAB")
print_somthing(your_name="TEAMLAB",my_name="Sungchul") # 변수명 사용
  • default arguments
    • parameter의 기본값을 사전에 입력해놓고, 입력하지 않을 경우 기본값을 출력
def print_somthing(my_name,your_name="TEAMLAB"): # 기본값 입력
		print("Hello {0}, My name is {1}".format(your_name,my_name))

print_somthing("Sungchul", "TEAMLAB")
print_somthing("Sungchul")
  • variable-length asterisk
    • 함수의 paraemter가 정해져 있지 않을 때 가변인자를 사용

    • 가변인자는 일반적으로 *args를 변수명으로 사용

    • 기존 parameter를 제외하고 나오는 값을 tuple로 저장

      def asterisk_test(a,b,*args):
      		return a+b+sum(args)
      
      print(asterisk_test(1,2,3,4,5)) # 1:a , 2:b ,나머지는 *args에 tuple형태로 들어감
      
      # unpacking도 가능
      def asterisk_test(*args):
      		x, y, z = args
      		return x, y, z
      
      asterisk_test(1,2,3)
  • keyword variable-length
    • parameter 이름을 따로 지정하지 않으면서, 여러 개의 값을 keyword형태로 넣을 수 있다.
    • 일반적으로 **kwargs를 변수명으로 사용
    • 입력된 값은 dict type으로 사용 가능
def kwargs_test_1(**kwargs):
		print(kwargs)

kwargs_test_1(first=3, second=4, third=5) # key, value 형태의 dict type으로 그대로 출력

def kwargs_test_2(one,two=2,*args,**kwargs):
		print(one+two+sum(args)) # 69
		print(args)	#(3,5,6,7,8)
		print(kwargs)

# one:10, two:30, args:(3,5,6,7,8), 나머지 kwargs
# two는 default 값이 있지만 무시
kwargs_test_2(10,30,3,5,6,7,8,first=3, second=4, third=5)

asterisk


  • 단순 곱셈, 제곱연산, 가변 인자 활용, unpacking a container 등 다양하게 사용된다

  • unpacking a container

    • tuple, dict 등 자료형에 들어가 있는 값을 unpacking
    • 함수의 입력값, zip 등에 유용하게 사용

0개의 댓글