Ipython: IPythonShellEmbed๊ฐ€ ์ง€์—ญ ๋ณ€์ˆ˜๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•จ

์— ๋งŒ๋“  2010๋…„ 07์›” 19์ผ  ยท  18์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: ipython/ipython

๋ฌธ์ œ

์ž„๋ฒ ๋””๋“œ IPython ์…ธ์€ ์ง€์—ญ ๋ณ€์ˆ˜๋ฅผ ์ถ”์ ํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค

์ตœ์†Œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค:

class Foo(object):
    """ Container-like object """
    def __setattr__(self, obj, val):
        self.__dict__[obj] = val

    def __getattr__(self, obj, val):
        return self.__dict__[obj]

f = Foo()
f.indices = set([1,2,3,4,5])
f.values = {}
for x in f.indices:
    f.values[x] = x

def bar(foo):
    import IPython
    IPython.Shell.IPShellEmbed()()
    return sum(foo.values[x] for x in foo.indices)

print bar(f)

์˜ค๋ฅ˜๋ฅผ ๋ณด๋ ค๋ฉด ๋จผ์ € Python(๋˜๋Š” IPython)์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์ƒ์„ฑ๋œ ์…ธ์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ์ตœ์ข… ์ธ์‡„ ๋ฌธ์€ '15'๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์ง€๋งŒ ์ด๋ฒˆ์—๋Š” ์ƒ์„ฑ๋œ ์…ธ์— sum(foo.values[x] for x in foo.indices) ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

" NameError: ์ „์—ญ ์ด๋ฆ„ 'foo'๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด ๋ฌธ์ œ๋ฅผ ์กฐ๊ธˆ ์กฐ์‚ฌํ•œ ๊ฒฐ๊ณผ ํ™•์‹คํžˆ ๊ณ ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Python 2์™€ 3 ๋ชจ๋‘์— ๋Œ€ํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์ง€์ €๋ถ„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ).

ChainMap ์†”๋ฃจ์…˜์€ ์ ์ ˆํ•œ IPython์— ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ eval/exec์—์„œ๋Š” ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ dict ์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์•ฝ๊ฐ„์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. class MyChainMap(ChainMap, dict): pass ์ƒ์„ฑํ•˜๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ํด๋กœ์ € ์…€์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ณ  ํŒŒ์ด์ฌ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ๋‚ด๋ณด๋‚ด๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๋‹ค๋ฅธ ์ „๋žต์„ ๊ธฐ๋ฐ˜์œผ๋กœ Python 3.5+ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ด€๋ จ ํŒŒ์ผ์€ ์—ฌ๊ธฐ ๋‚ด xdbg ๋ฐ๋ชจ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. get_ipython().run_ast_nodes ๋ฅผ ๋Œ€์ฒดํ•˜์—ฌ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ํ•œ, ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์€ ํด๋กœ์ € ์ฒ˜๋ฆฌ์—์„œ๋งŒ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. xdbg ๊ฐ€ ์ผ๋ถ€ ๋ณ€์ˆ˜์— ๋Œ€ํ•ด ๋‹ซํžŒ ๋ฒ”์œ„์— ํฌํ•จ๋˜๋ฉด ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ณ€์ˆ˜์— ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์•ก์„ธ์Šคํ•˜๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋Œ€ํ™”ํ˜• ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—์„œ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ๋‚˜๋จธ์ง€ ๋กœ์ปฌ ๋ฒ”์œ„๊ฐ€ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘๋˜๋„๋ก ํ—ˆ์šฉํ•˜๋ฉด์„œ ํ•„์š”ํ•œ ๋ชจ๋“  ๋กœ์ปฌ ๋ณ€์ˆ˜๋ฅผ ๋‹ซ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  18 ๋Œ“๊ธ€

ํ , 'foo'๋Š” ์–ด๋””์—์„œ ์ •์˜ํ•˜์…จ๋‚˜์š”?

ํ•จ์ˆ˜ ํ‘œ์‹œ์ค„ ์™ธ๋ถ€์—์„œ foo ํ˜ธ์ถœ์„ ์‹คํ–‰ํ•˜๋ฉด foo๊ฐ€ ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(๊ทธ๋ฆฌ๊ณ  ๋ถ„๋ช…ํžˆ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

๋Œ€์‹  "sum(f.values[x] for x in f.indices)"๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์‹œ 15๊ฐ€ ๋ฉ๋‹ˆ๋‹ค...

์•„์ฃผ ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ foo _inside_ ์ƒ์„ฑ๋œ IPython ์…ธ์˜ ์‚ฌ์šฉ์„ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ๋œ IPython ์…ธ์€ foo ๊ฐ€ ๋กœ์ปฌ ๋ณ€์ˆ˜์ธ ํ•จ์ˆ˜ ์ •์˜ ๋‚ด๋ถ€์—์„œ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

#62์™€ ๊ฐ™์€ ๋ฌธ์ œ์ผ๊นŒ์š”?

์•„๋‹ˆ์š”, @takluyver : ์ด๊ฒƒ์€ ๋ณ„๋„์˜ ๋ฌธ์ œ์ด๋ฉฐ ์‹ค์ œ๋กœ ์ž„๋ฒ ๋”ฉ ์ฝ”๋“œ์˜ ์‹ค์ œ ๋ฒ„๊ทธ์ž…๋‹ˆ๋‹ค. ์ตœ๊ทผ์— ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ๋Œ€ํ•œ ์ž‘์—…์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ํ˜„์žฌ ์ž„๋ฒ ๋”ฉ API๋กœ ์‹คํ–‰ํ•  ์˜ˆ์ œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

class Foo(object):
    """ Container-like object """
    def __setattr__(self, obj, val):
        self.__dict__[obj] = val

    def __getattr__(self, obj, val):
        return self.__dict__[obj]

f = Foo()
f.indices = set([1,2,3,4,5])
f.values = {}
for x in f.indices:
    f.values[x] = x

def bar(foo):
    import IPython
    IPython.embed()
    return sum(foo.values[x] for x in foo.indices)

print bar(f)

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒ์„ฑ๋˜๊ณ  ํฌํ•จ๋œ IPython์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

In [1]: sum(foo.values[x] for x in foo.indices)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/home/fperez/tmp/junk/ipython/foo.py in <module>()
----> 1 sum(foo.values[x] for x in foo.indices)

/home/fperez/tmp/junk/ipython/foo.py in <genexpr>((x,))
----> 1 sum(foo.values[x] for x in foo.indices)

NameError: global name 'foo' is not defined

ํฌํ•จ ํ˜ธ์ถœ user_ns=locals() ๋ช…์‹œ์ ์œผ๋กœ ์ „๋‹ฌํ•˜๋”๋ผ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์ง€๋งŒ ์ด ๊ฒฝ์šฐ ์ข…๋ฃŒ ์‹œ ์ถ”๊ฐ€ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "/home/fperez/usr/lib/python2.7/site-packages/IPython/core/interactiveshell.py", line 2702, in atexit_operations
    self.reset(new_session=False)
  File "/home/fperez/usr/lib/python2.7/site-packages/IPython/core/interactiveshell.py", line 1100, in reset
    self.displayhook.flush()
  File "/home/fperez/usr/lib/python2.7/site-packages/IPython/core/displayhook.py", line 319, in flush
    self.shell.user_ns['_oh'].clear()
KeyError: '_oh'

์šฐ๋ฆฌ ์ž„๋ฒ ๋”ฉ ๊ธฐ๊ณ„์˜ ์ƒํƒœ๊ฐ€ ์ƒ๋‹นํžˆ ์ข‹์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค...

์ด๊ฒƒ์„ ๋‚˜ ์ž์‹ ์—๊ฒŒ ํ• ๋‹นํ•˜์—ฌ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ถˆํ–‰ํžˆ๋„ ์ด๊ฒƒ์ด Python ์ž์ฒด์˜ ํ•œ๊ณ„๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋™์ ์œผ๋กœ ์ปดํŒŒ์ผ๋œ ์ฝ”๋“œ๋Š” ํด๋กœ์ €๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ์—ฌ๊ธฐ์—์„œ ์ƒ์„ฑ๊ธฐ ํ‘œํ˜„์‹์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ตœ์†Œํ•œ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์ž…๋‹ˆ๋‹ค.

def f():
   x = 1
   exec "def g(): print x\ng()"

f()

๋‹ค์Œ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Traceback (most recent call last):
  File "scopetest.py", line 5, in <module>
    f()
  File "scopetest.py", line 3, in f
    exec "def g(): print x\ng()"
  File "<string>", line 2, in <module>
  File "<string>", line 1, in g
NameError: global name 'x' is not defined

IPython์—์„œ ์—ฌ์ „ํžˆ ์ง€์—ญ ๋ณ€์ˆ˜๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์–ด์ง„ ์˜ˆ์ œ์—์„œ ์ผ๋ฐ˜ print foo ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ƒˆ ๋ฒ”์œ„๋ฅผ ๋‹ซ์„ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

IPython์ด ์ „์—ญ ๋ณ€์ˆ˜๋กœ ํฌํ•จ๋œ ์ง€์—ญ ๋ฐ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก Python 3.3์˜ collections.ChainMap ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ง€๋‚œ 2๋…„ ๋™์•ˆ ์ด๊ฒƒ์— ๋Œ€ํ•œ ๋…ธ์ด์ฆˆ๊ฐ€ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋†’์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฏ€๋กœ ๊ทธ์— ๋”ฐ๋ผ ํƒœ๊ทธ๋ฅผ ๋‹ค์‹œ ์ง€์ •ํ•˜๊ณ  1.0 ์ดํ›„์— ์–ธ์  ๊ฐ€๋Š” ์ด์— ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์ฐฌ์„ฑ ํˆฌํ‘œ๊ฐ€ ํ—ˆ์šฉ๋ฉ๋‹ˆ๊นŒ? :+1: ? ์ด๊ฒƒ์€ ๋‚˜์—๊ฒŒ๋„ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ์š”์ฒญํ•˜๋Š” ๊ฒฝ์šฐ ๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์— ๋Œ€ํ•œ ์ˆ˜์ •์ด Python 3์—์„œ๋งŒ ์ž‘๋™ํ•œ๋‹ค๋ฉด 100% ๊ดœ์ฐฎ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Python 3์—์„œ๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์‹œ ์—ด์–ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋™์ผํ•œ ๋ฌธ์ œ:+1: Python 2์™€ 3 ๋ชจ๋‘์—์„œ.

Python 2์™€ 3 ๋ชจ๋‘์—์„œ ์ด ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋งค์ผ ์ €์—๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ์กฐ๊ธˆ ์กฐ์‚ฌํ•œ ๊ฒฐ๊ณผ ํ™•์‹คํžˆ ๊ณ ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Python 2์™€ 3 ๋ชจ๋‘์— ๋Œ€ํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์ง€์ €๋ถ„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ).

ChainMap ์†”๋ฃจ์…˜์€ ์ ์ ˆํ•œ IPython์— ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ eval/exec์—์„œ๋Š” ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ dict ์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์•ฝ๊ฐ„์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. class MyChainMap(ChainMap, dict): pass ์ƒ์„ฑํ•˜๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ํด๋กœ์ € ์…€์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ณ  ํŒŒ์ด์ฌ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ๋‚ด๋ณด๋‚ด๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๋‹ค๋ฅธ ์ „๋žต์„ ๊ธฐ๋ฐ˜์œผ๋กœ Python 3.5+ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ด€๋ จ ํŒŒ์ผ์€ ์—ฌ๊ธฐ ๋‚ด xdbg ๋ฐ๋ชจ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. get_ipython().run_ast_nodes ๋ฅผ ๋Œ€์ฒดํ•˜์—ฌ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ํ•œ, ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์€ ํด๋กœ์ € ์ฒ˜๋ฆฌ์—์„œ๋งŒ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. xdbg ๊ฐ€ ์ผ๋ถ€ ๋ณ€์ˆ˜์— ๋Œ€ํ•ด ๋‹ซํžŒ ๋ฒ”์œ„์— ํฌํ•จ๋˜๋ฉด ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ณ€์ˆ˜์— ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์•ก์„ธ์Šคํ•˜๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋Œ€ํ™”ํ˜• ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—์„œ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ๋‚˜๋จธ์ง€ ๋กœ์ปฌ ๋ฒ”์œ„๊ฐ€ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘๋˜๋„๋ก ํ—ˆ์šฉํ•˜๋ฉด์„œ ํ•„์š”ํ•œ ๋ชจ๋“  ๋กœ์ปฌ ๋ณ€์ˆ˜๋ฅผ ๋‹ซ์Šต๋‹ˆ๋‹ค.

IPython 6.0 ์ด์ƒ ๋ฒ„์ „์€ Python 3์—์„œ๋งŒ ์ž‘๋™ํ•˜๋ฏ€๋กœ @nikitakit ๋˜๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ํ…Œ์ŠคํŠธ ์‚ฌ๋ก€์™€ ์ด์— ๋Œ€ํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์ด ํฌํ•จ๋œ pull ์š”์ฒญ์„ ์—ด๊ณ  ์‹ถ๋‹ค๋ฉด ํ™˜์˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์—ฌ๊ธฐ์— ๋งˆ์ง€๋ง‰์œผ๋กœ ๋Œ“๊ธ€์„ ๋‹ฌ๊ณ  1๋…„์ด ์ง€๋‚ฌ๊ณ  ์ด ๊ธฐ๊ฐ„ ๋™์•ˆ ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ์•ฝ๊ฐ„ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค.

๋Œ€ํ™”์‹์œผ๋กœ ์ง€์—ญ ๋ฒ”์œ„๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ๊ฒƒ์€ ๋‚˜์—๊ฒŒ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ์œผ๋กœ ๋‚จ์•„ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ ์ง€์—ญ ๋ณ€์ˆ˜ ๋ฐ ์ž„๋ฒ ๋”ฉ ๊ธฐ๊ณ„์™€ ๊ด€๋ จํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ƒํ˜ธ ๊ด€๋ จ๋œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํฌํ•จ๋œ ์…ธ ๋‚ด์—์„œ ๋กœ์ปฌ ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

>>> import IPython
>>> def test():
...     x = 5
...     IPython.embed()
...     print('x is', x)
...
>>> test()
Python 3.5.1 |Continuum Analytics, Inc.| (default, Dec  7 2015, 11:24:55)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: x
Out[1]: 5

In [2]: x = 6

In [3]:
Do you really want to exit ([y]/n)? y

x is 5

ChainMap ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ด ์ƒํ™ฉ์—์„œ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ์งˆ๋ฌธ์€ ์ž„๋ฒ ๋””๋“œ ์‰˜ ๋‚ด๋ถ€์— ์ •์˜๋œ ํด๋กœ์ €๊ฐ€ ์ „์—ญ ๋ฒ”์œ„๋กœ ์œ ์ถœ๋  ๋•Œ ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€์ž…๋‹ˆ๋‹ค. ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋˜ ์ž„๋ฒ ๋””๋“œ IPython ์…ธ์—์„œ IPython.get_my_x = lambda: x ํ–‰์„ ๋”ฐ๋ผ ๋ฌด์–ธ๊ฐ€๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค. ChainMap ๊ธฐ๋ฐ˜ ์†”๋ฃจ์…˜์€ ์ด ์ƒํ™ฉ์—์„œ ์„œ๋กœ ๋…๋ฆฝ์ ์œผ๋กœ ์กด์žฌํ•˜๋Š” x ์˜ ๋‘ ๊ฐœ์˜ ๋™์‹œ ๋ณต์‚ฌ๋ณธ์„ ์ž ์žฌ์ ์œผ๋กœ ๋„์ž…ํ•˜๋Š” ๋Œ€์‹  NameError ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค(ํ•˜๋‚˜๋Š” ChainMap , ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ํŒŒ์ด์ฌ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์ง€์—ญ ๋ณ€์ˆ˜/ํด๋กœ์ € ์…€).

์ƒํ™ฉ์˜ ๋ณต์žก์„ฑ์„ ๊ฐ์•ˆํ•  ๋•Œ ๋‚˜๋Š” ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋ณด๋‹ค ํฌ๊ด„์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์— ๋…ธ๋ ฅ์„ ์ง‘์ค‘ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ณธ์งˆ์ ์œผ๋กœ IPython๊ณผ ํ†ตํ•ฉ๋˜๋Š” ๋””๋ฒ„๊ฑฐ์ธ xdbg ์˜ ๊ฐœ๋ฐœ๋กœ %break ). ์ค‘๋‹จ์ ์ด embed ํ•จ์ˆ˜๋ฅผ ์ œ์ž๋ฆฌ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  ์™ธ๋ถ€์—์„œ ์„ค์ •๋œ๋‹ค๋Š” ์‚ฌ์‹ค๋กœ ์ธํ•ด ๋กœ์ปฌ ๋ณ€์ˆ˜๋กœ ์ด๋Ÿฌํ•œ ๋งŽ์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ํ˜„์žฌ ์ฝ”์–ด IPython์— ๋Œ€ํ•œ ์ข์€ ํƒ€๊ฒŸ ๋ฒ„๊ทธ ์ˆ˜์ •์„ ํ’€ ์š”์ฒญ(pull-request)ํ•  ๊ณ„ํš์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋กœ์ปฌ ์ฝ”๋“œ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•ด ๋””๋ฒ„๊ฑฐ์—์„œ ์˜๊ฐ์„ ๋ฐ›์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด IPython ์‚ฌ์šฉ์ž์™€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜๋Š”์ง€ ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. IPython(์ง€๊ธˆ์€ Jupyter๋„ ํฌํ•จ)์€ Python์—์„œ ๋Œ€ํ™”ํ˜• ์ฝ”๋”ฉ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์ณค์ง€๋งŒ ํด๋ž˜์Šค/ํ•จ์ˆ˜/๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌด๊ฑฐ์šด ์บก์Šํ™”์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ๋ฐฉ์‹์—์„œ ์—ฌ์ „ํžˆ ๊ฐœ์„ ํ•ด์•ผ ํ•  ์‚ฌํ•ญ์ด ๋งŽ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฒ„๊ทธ๋กœ ์ธํ•ด ์—ฌ๋Ÿฌ ๋ฒˆ ํ™”์ƒ์„ ์ž…์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ

[mcgibbon<strong i="6">@xps13</strong>:~]$ cat test.py 
x = 1
def func():    
    x = 2
    import IPython
    IPython.embed()

if __name__ == "__main__":
    func()
[mcgibbon<strong i="9">@xps13</strong>:~]$ python test.py 
Python 3.7.6 (default, Dec 18 2019, 19:23:55) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: [x for _ in range(2)]                                                                                                                                                                                                                 
Out[1]: [1, 1]  # lol whoops. looked up in wrong scope.

๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ํ•ฉ์ฐฝ๋‹จ์— ์ œ ๋ชฉ์†Œ๋ฆฌ๋ฅผ ๋”ํ•˜๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค.

https://bugs.python.org/issue13557 ๊ณผ ๋ช…์‹œ์ ์œผ๋กœ locals() ๋ฐ globals()๋ฅผ exec์— ์ „๋‹ฌํ•˜๋ฉด @takluyver ์˜ ๋ฒ„๊ทธ ์žฌํ˜„์ด ์ˆ˜์ •๋œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ๋กœ์ปฌ ๋ฐ ๊ธ€๋กœ๋ฒŒ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์ „๋‹ฌํ•˜์—ฌ IPython.

x = 1

def func():    
    x = 2
    exec("def g():\n print(x)\ng()")  # prints 1 :(
    exec("def g():\n print(x)\ng()", locals(), globals())  # prints 2 yay!


if __name__ == "__main__":
    func()
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰