YUDA't

'Hidden features of Python'


언젠가 스택오버플로에서 찾았는데, 파이썬에서 잘 알려져 있지 않은 기능을 공유하는 글이다. (다만 스택오버플로의 취지에는 맞지 않는 질문이라 지금은 close 됨)

한 번에 읽기에는 좀 길어서 북마크해뒀었는데 가끔 보면서 흥미로운 게 있으면 적어놨다.




Function argument unpacking


별표(*;asterisk) 연산자를 사용하여 리스트나 딕셔너리를 언패킹할 수 있다.

>>> a = [1, 2, 3, 4, 5]
>>> print(*a)
1 2 3 4 5

>>> b = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> print(*b)
a b c d

오와!

했지만 손이 안 가서 쓰지 않는다.

아웃!




C 스타일 코딩을 가능케 하는 모듈


from __future__ import braces

스택오버플로에서 from __past__ import braces를 잘못 적은 것 아니냐는 댓글을 봤다ㅋㅋㅋㅋ

이전 버전에서는 어땠는지는 모르지만 Python3에서 이걸 import하면 웃긴 에러가 뜬다. 귀도가 가장 좋아하는 에러라고 한다.

>>> from __future__ import braces
  File "", line 1
SyntaxError: not a chance




데코레이터


파이썬을 써온 사람이라면 대부분 알고 있을 기능이지만 데코레이터를 소개하는 튜토리얼은 거의 없어서 이 목록에 들어간 듯 하다. 사실 나도 직접 만들어 본 적은 거의 없다. 데코레이터는 함수는 다른 함수를 감싸 일정한 기능을 추가하는 데 사용되며, 보통 그 '다른' 함수 바로 위에 @ 사인을 붙여 정의된다.

def print_args(function):
    def wrapper():
        print('this is decorator!')
        return function()

    return wrapper

@print_args
def write():
    print('this is original function!')

write()


this is decorator!

this is original function!






가변성 있는 값은 함수 인자로 사용하지 말 것.


def foo(x=[]):
    x.append(1)
    print(x)
	
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]


이건 흥미롭다기보단 파이썬을 쓰는 사람이라면 한 번 봐줘야 할 내용이다.

위의 코드를 실행하면 인자 x의 기본값으로 []가 할당되어 있어 계속 [1]이 출력될 듯 하지만 실제로는 [1], [1, 1], [1, 1, 1] 이렇게 늘어나는 리스트를 보여준다. 이런 결과가 나타나는 까닭은 위 코드에서 리스트 x가 foo 함수가 정의될 때 '딱 한 번' 초기화되었기 때문이다. 그 이후로 foo()가 불릴 때엔 새로운 x가 아니라 그 object를 불러오게 된다!

즉 초기화는 변수가 아닌 객체에 일어나는 일이기 때문에 가변성 있는 객체를 함수 인자로 사용할 경우 이런 문제가 발생한다. 이를 방지하고 싶다면 다음과 같이 수정하면 된다.


def foo(x=None):
    if x is None:
	x = []
    x.append(1)
    print(x)
	
>>> foo()
[1]
>>> foo()
[1]




enumerate!


내가 좋아하는 내장 함수다. 길게 설명할 필요 없이 예시를 보자.

a = ['a', 'b', 'c', 'd', 'e']
for idx, item in enumerate(a):
	print(idx, item)
0 a
1 b
2 c
3 d
4 e




for else

for 루프에서도 else를 쓸 수 있다! break로 루프가 깨지지 않는 이상, for 루프 가장 마지막에 실행된다.


for i in [2, 1, 0]:
    if i == 0:
        break
else:
    print("i was never 0")	# for 루프가 break 되어 출력되지 않는다.

for i in [3, 2, 1]:
    if i == 0:
        break
else:
    print("i was never 0")	# for 루프가 break 되지 않아 출력된다.

자매품으로 try except else가 있는데, 이건 for else보다는 많이 알려져 있는 듯하다.

왜냐하면 내가 쓰니까!!!




[::-1]


문자나 리스트를 뒤집는 아주 간단한 슬라이스 문법이다.

실무에서 써본 적은 없고 해커랭크에선 유용했다.

>>> a = [1, 2, 3, 4, 5]
>>> a[::-1]
[5, 4, 3, 2, 1]




Dictionaries have a get() method


a = {'a': 1, 'b': 2, 'c': 3}
>>> a['d']
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'd'

>>> a.get('d')			# 키에 'd'가 없어 아무런 일도 일어나지 않는다.
>>> a['d'] = a.get('d', 4)	# 'd'가 있다면 가져오고, 없다면 값 4를 세팅한다.
>>> a
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

이건 코세라 파이썬 강의에서 배웠던 건데 업무에서도 가끔 써먹었다. 꽤 유용하지만 튜토리얼에서는 거의 안 가르치기 때문에 숨겨진 기능이라 한 듯하다.




import this


파이썬 인터프리터에 import this를 치면 The Zen of Python이란 글이 나온다.


The Zen of Python, by Tim Peters


Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren't special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you're Dutch.

Now is better than never.

Although never is often better than right now.

If the implementation is hard to explain, it's a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea -- let's do more of those!


뭔가 숨겨진 이스터에그가 더 있을 것 같아 dir(this)로 무슨 함수가 있나 찾아봤다.

>>> dir(this)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'c', 'd', 'i', 's']

잉 그랬더니 'c', 'd', 'i', 's'라는 게 나와서 그냥 하나씩 다 실행해봤는데....

>>> this.c
97
>>> this.d
{'A': 'N', 'B': 'O', 'C': 'P', 'D': 'Q', 'E': 'R', 'F': 'S', 'G': 'T', 'H': 'U', 'I': 'V', 'J': 'W', 'K': 'X', 'L': 'Y', 'M': 'Z', 'N': 'A', 'O': 'B', 'P': 'C', 'Q': 'D', 'R': 'E', 'S': 'F', 'T': 'G', 'U': 'H', 'V': 'I', 'W': 'J', 'X': 'K', 'Y': 'L', 'Z': 'M', 'a': 'n', 'b': 'o', 'c': 'p', 'd': 'q', 'e': 'r', 'f': 's', 'g': 't', 'h': 'u', 'i': 'v', 'j': 'w', 'k': 'x', 'l': 'y', 'm': 'z', 'n': 'a', 'o': 'b', 'p': 'c', 'q': 'd', 'r': 'e', 's': 'f', 't': 'g', 'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k', 'y': 'l', 'z': 'm'}
>>> this.i
25

c와 i 함수는 숫자가, d는 딕셔너리가, s는 다음과 같은 글이 나왔다.


Gur Mra bs Clguba, ol Gvz Crgref


Ornhgvshy vf orggre guna htyl.

Rkcyvpvg vf orggre guna vzcyvpvg.

Fvzcyr vf orggre guna pbzcyrk.

Pbzcyrk vf orggre guna pbzcyvpngrq.

Syng vf orggre guna arfgrq.

Fcnefr vf orggre guna qrafr.

Ernqnovyvgl pbhagf.

Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.

Nygubhtu cenpgvpnyvgl orngf chevgl.

Reebef fubhyq arire cnff fvyragyl.

Hayrff rkcyvpvgyl fvyraprq.

Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.

Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.

Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.

Abj vf orggre guna arire.

Nygubhtu arire vf bsgra orggre guna *evtug* abj.

Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.

Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.

Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!


이건 머시여 구글 번역기 돌려봤더니 스코틀랜드 게일어(???)로 총을 쏴라 총을 쏴라 죄송합니다 이런 게 번역돼서 소름돋았는데 그냥 this.d 기준으로 알파벳만 바꾼 글이었다. 왜 이런 짓을




여담으로, import antigravity를 치면 xkcd.com 만화가 나온다.