본문 바로가기

Kakao Cloud School/Study : Python Algorithm interview

[파이썬 알고리즘 인터뷰] 2부 3장 파이썬

인덴트

파이썬의 인덴트는 공백 4칸을 원칙으로 하고 있음

 

* 파이썬 PEP

- 파이썬의 개발은 파이썬 개발 제안서(PEP) 프로세스를 통해 진행됨

- PEP 프로세스 : 새로운 기능을 제안하고 커뮤니티의 의견을 수렴하여 파이썬의 디자인 결정을 문서화하는 주요 개발 프로세스임

- 대표적인 파이썬 스타일 가이드인 PEP8이 있음

네이밍 컨벤션

파이썬의 변수명 네이밍 컨벤션은 각 단어를 밑줄로 구분하여 표기하는 스네이크 케이스를 따름

스네이크 케이스

- 각 단어를 언더스코프(_)로 구분, 일반적으로 소문자로 표기(경우에 따라서는 대문자로 표기)

- 파이썬의 PEP 8을 통해 

- 스네이크 표기법 코딩에 대한 질문시, 파이썬의 PEP 8 철학에 따라 스네이크 코딩 지향

리스트 컴프리헨션

- 기존 리스트를 기반으로 새로운 리스트를 만들어내는 구문

- 대체로 표현식은 2개를 넘지 않아야 하는 것이 좋음

 

[n * 2 for n in range(1,10+1) if n % 2 == 1]
>> 출력 결과 [2, 6, 10, 14, 18]
a = {}
for key, value in original.items():
	a[key] = value

# 리스트 컴프리헨션

>> a = {key: value for key, vlaue in origin.items()}

제너레이터

- 루프의 반복 동작을 제어할 수 있는 루틴 형태

- 임의의 조건으로 숫자 1억 개를 만들어내 계산하는 프로그램을 작성할 시, 제너레이터를 생성한 후 필요할 때 언제든 숫자를 만들어낼 수 있음

- yield 구문을 사용하여 제너레이터 리턴할 수 있음

yield 키워드

- 기존 함수의 return문(값을 리턴하고 함수 종료)과 달리,

- yield문은 제너레이터가 여기까지 실행 중이던 값을 내보낸다(양보한다)는 의미로,

- 중간값을 리턴한 다음 함수는 종료되지 않고 계속해서 맨 끝에 도달할 때까지 실행

 

- yield가 호출되면 암시적으로 return이 호출되며, 한번 더 실행되면 실행되었던 'yield' 다음 코드가 실행됨

 

def get_natural_number():
	n = 0
    while True:
    	n += 1
        yield n

 

- next()를 이용하여 값을 추출하면 됨

 

g = get_natural_number()
for _ in range(0,100):
	print(next(g))
>> 1
>> 2
>> 3
...
>>98
>>99
>>100

 

- 여러 타입의 값을 하나의 함수에서 생성하는 것도 가능

 

def generator():
	yield 1
    yield 'string'
    yield True
    
>> g = generator()
>> g

>> next(g)

range

- 제너레이터를 활용하는 대표적인 함수 range( )

- 생성 조건만 정해두고 나중에 필요할 때 생성해서 꺼내 쓸 수 있음

 

a = [n for n in range(1000000)
b = range(1000000)

len(a)
>> 1000000
len(b)
>> 1000000
len(a) == len(b)
>> True

# 변수 a에는 생성된 값이 담겨 있고
# 변수 b에는 생성해야 한다는 조건만 존재
# 둘 사이의 메모리 점유율 비교

b
>> range(0,1000000)
type(b) 
>> <class 'range'>

 

- 위 코드를 보면 변수 a에는 생성된 값이 담겨 있고,

- 변수 b에는 생성해야 한다는 조건만 존재함 

 

- 둘 사이의 점유율을 비교해 보았을 때 ( sys.getsizeof(a) => 8697646 , sys.getsizeof(b) => 48 )

- 똑같이 숫자 100만 개를 가지고 있으나 range 클래스를 이용하는 b 변수의 메모리 점유율이 훨씬 더 작음

- b 변수를 보았을 때, 100만 개가 아닌 1억 개라도 b 변수의 메모리 점유율은 동일함 (왜? 생성 조건만 보관하고 있으므로)

- 또한 인덱스 접근 시 바로 생성하도록 구현되어 있기 때문에 리스트와 거의 동일하게 사용 가능함 (b[999] => 999)

enumerate

- '열거하다' 라는 뜻의 함수

- 여러 가지 자료형(list, set, tuple 등)을 인덱스를 포함한 enumerate 객체로 리턴함

 

a = [1,2,3,2,45,2,5]
>> [1,2,3,2,45,2,5]

enumerate(a)
>> <enumerate object at 0x1010f83f0>

# 리스트 결과물 출력 -> 인덱스 자동 부여
list(enumerate(a))
>>[(0,1),(1,2),(2,3),(3,2),(4,45),(5,2),(6,5)]

 

- list( )로 결과를 추출할 수 있으며, 인덱스를 자동으로 부여해줘서 매우 편리함

 

# a = ['a1', 'b1', 'c3']가 있을 때 이 리스트의 인덱스와 값을 함께 출력

# 방법 1
for i in range(len(a)):
	print(i, a[i])
    
# 방법 2
i = 0
for v in a:
	print(i, v)
    i += 1

# 방법 3
for i, v in enumerate(a):
	print(i, v)

// 나눗셈 연산자, divmod( )

- 몫과 나머지를 동시에 구할 수 있는 함수

 

divmod(5, 3)

>> (1, 2)

Print

- print( ) 함수는 항상 줄바꿈함

 

# 콤마로 구분하여 값 출력
print('A1', 'B1')
>> A1 B1

# seq 파라미터로 구분자를 콤마(,)로 지정
print('A1', 'B1', seq=',')
>> A1,B1

 

- 긴 루프의 값을 반복적으로 출력하면 디버깅하기 어려움

- 이를 해결하기 위해 end 파라미터를 공백으로 처리하여 줄바꿈을 하지 않도록 제한할 수 있음

end 파라미터, join ( )

# end 파라미터 사용
print('aa' end='')
print('bb')

--------------------
aa bb


# join 함수 사용
a = ['A', 'B']
print(' '.join(a))

--------------------
A B

.format( )

# idx 값에 1을 더해서 fruit와 함께 출력
print('{0} : {1}'.format(idx+1, fruit))

>> 2 : APPLE

# 인덱스 생략 ver
print('{} : {}'.format(idx+1, fruit))

>> 2 : APPLE

f-string

- 변수를 뒤에 별도로 부여할 필요 없이 마치 템플릿을 사용하듯 인라인으로 삽입 가능

 

print(f'{idx+1} : {fruit}')
>> 2 : APPLE

Pass

- 널 연산으로 아무것도 하지 않은 기능

- 아무 역할을 하지 않은 pass를 지정하면, 앞서 발생한 인텐트 오류와 같은 불필요한 오류를 방지할 수 있음

- 목업 인터페이스부터 구현한 다음 추후 구현을 진행할 수 있게 함

locals

- 로컬 심볼 테이블 딕셔너리를 가져오는 메소드(업데이트 가능)

- 로컬에 선언된 모든 변수를 조회할 수 있는 강력한 명령이므로 디버깅에 많은 도움이 됨

 

- 로컬 스코프에 제한해 정보를 조회할 수 있기 때문에

- 클래스의 특정 메소드 내부에서나 함수 내부의 로컬 정보를 조회해 잘못 선언한 부분이 없는지 확인하는 용도로 활용

- 변수명을 일일히 찾아낼 필요 없이 로컬 스코프에 정의된 모든 변수를 출력할 수 있음

 

...
import pprint
pprint.pprint(locals())
...

 

- pprint로 출력하게 되면 보기 좋게 줄바꿈 처리를 해주기 때문에 가독성이 좋음