Ipython: Variabel global tidak ditentukan dalam penggunaan interaktif shell ipython tertanam

Dibuat pada 10 Mei 2010  ·  39Komentar  ·  Sumber: ipython/ipython

Bug Launchpad asli 399627: https://bugs.launchpad.net/ipython/+bug/399627
Dilaporkan oleh: h-fangohr (Hans Fangohr).

Kesalahan dapat direproduksi sebagai berikut:

  1. Mulai Python, dan mulai sesi ipython tertanam. Mengikuti manual ipython, kami lakukan
fangohr<strong i="11">@eta</strong>:~$ python
Python 2.4.3 (#1, Jun  8 2009, 14:09:06) 
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from IPython.Shell import IPShellEmbed
>>> ipshell=IPShellEmbed()
>>> ipshell()

Dalam sesi ipython yang baru saja dimulai, variabel global terkadang tidak terlihat. Dua contohnya adalah:

Contoh 1:

In [1]: a=1

In [2]: def f(x):
   ...:     return a*x
   ...: 

In [3]: f(2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/fangohr/<ipython console> 

/home/fangohr/<ipython console> in f(x)

NameError: global name 'a' is not defined

Contoh 2:

In [4]: b=1

In [5]: (lambda :b)()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/fangohr/<ipython console> 

/home/fangohr/<ipython console> in <lambda>()

NameError: global name 'b' is not defined

Tidak ada kesalahan jika "b = 1; (lambda: b) ()" dimasukkan ke dalam skrip, dan skrip ini dijalankan menggunakan perintah "run" ipython.

Tidak ada kesalahan jika "b = 1; (lambda: b) ()" dijalankan secara interaktif setelah memulai ipython.

Satu-satunya cara munculnya kesalahan adalah jika shell IPython yang disematkan dimulai dari prompt Python DAN perintah dijalankan secara interaktif pada prompt.

Saya menemukan kesalahan yang sama saat mencoba ipython-0.9.1 (dengan python2.4).

Bug tersebut dilaporkan oleh Olivier Klein ke tim nmag (http://nmag.soton.ac.uk).

bug

Komentar yang paling membantu

Seperti disebutkan di https://github.com/inducer/pudb/issues/103 , solusi sementara saat menggunakan shell tertanam adalah: globals().update(locals()) (hanya setelah menentukan variabel global lokal).

Semua 39 komentar

[LP komentar 1 oleh: Fernando Perez, pada 2010-04-25 23: 36: 38.673176 + 00: 00]

Oke, saya bisa memastikan masalahnya (bahkan di bagasi), tapi sulit. Saya hanya untuk sekarang memastikan bug ini dikonfirmasi jadi kami terus melacaknya, tetapi saya tidak yakin bagaimana cara memperbaikinya.

Masalahnya adalah di shell yang disematkan, kami mencoba memperbarui namespace global untuk menggunakan cakupan sekitarnya (itulah inti dari shell yang disematkan, untuk dapat melihat apa yang ada di sekitar Anda). Tetapi ini kemudian menyebabkan python gagal menyelesaikan namespace interaktif ipython ketika hal-hal bersarang (seperti fungsi lokal) didefinisikan. Lihat metode mainloop () dari shell yang disematkan untuk mengetahui detailnya.

Saya perlu memikirkan lebih banyak tentang cara memperbaikinya, ide apa pun sangat disambut.

Selama di launchpad ...

chairmanK menulis 20 jam yang lalu:

Saya juga. Selain fungsi, bug ini juga muncul di ekspresi generator.

Komponen yang setara di trunk tampaknya adalah IPython.frontend.terminal.InteractiveShellEmbed. Tapi itu rusak dengan cara lain, dan ternyata belum banyak diuji. Adakah yang tahu apa masa depannya?

Mungkinkah ini masalah yang sama dengan # 136?

Ini sekarang telah diperbaiki:

amirbar[ipython]> python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from IPython import embed
>>> embed()
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12.dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: a = 1

In [2]: def f(x):
   ...:     return a*x
   ...: 

In [3]: f(3)
Out[3]: 3

In [4]: b = 1

In [5]: (lambda : b)()
Out[5]: 1

In [6]: 

Adakah yang bisa menjelaskan sedikit lebih banyak apa yang menyebabkan perbaikan ini? Saya masih mengalami masalah, tetapi hanya ketika saya satu lapisan dihapus melalui panggilan metode, dan hanya jika saya menjalankan dari skrip, tidak interaktif.

python 2.7.2 di OSX 10.6.8, ipython 0.11 dan 0.12 keduanya menunjukkan perilaku yang sama (menggunakan 0.12 untuk komentar ini)

Ini adalah masalah dengan proyek kami yang menampilkan (secara mencolok) shell IPython yang tertanam.

testembed.py

from IPython import embed

def hi():
    embed()

if __name__ == '__main__':
    #embed()
    hi()

Jalankan ini di baris perintah dengan python testembed.py dan lihat sesi ini:

Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import time

In [2]: def tim():
   ...:     print time.time()
   ...:     

In [3]: tim()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
[snip] in <module>()
----> 1 tim()

[snip] in tim()
      1 def tim():
----> 2     print time.time()
      3 

NameError: global name 'time' is not defined

In [4]: 

Namun, komentari panggilan ke hi() dan ganti dengan panggilan embed() :

Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import time

In [2]: def tim():
    print time.time()
   ...:     

In [3]: tim()
1328639444.29

In [4]: 

Setelah melihat-lihat, saya pikir itu ada hubungannya dengan parameter stack_depth digunakan di sini dan blok ini: https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/embed.py # L185

Pikiran?

Ini agak rumit, tapi saya yakin itu batasan di Python itu sendiri.

Dalam kasus gagal yang Anda tunjukkan, time sebenarnya tidak dimasukkan ke dalam namespace global: karena Anda memanggil embed di dalam fungsi hi , variabel baru yang Anda buat secara interaktif bersifat lokal untuk itu fungsi. Idealnya, tim() harus berfungsi sebagai penutup, menutup referensi ke modul waktu. Namun, closure hanya tampak berfungsi ketika fungsi yang memuatnya dikompilasi sekaligus. Sejauh yang saya tahu, tidak ada cara untuk mendefinisikan closure secara dinamis. Contoh sederhana ini gagal:

def outer():
    import time
    exec("def inner(): return time.time()")
    return inner

outer()()

Ini mungkin karena cakupan bersarang ditambahkan ke Python relatif terlambat (mereka akan diimpor di

Oke, saya rasa saya mengerti. Sepertinya kita tidak bisa melakukan apa-apa tentang hal ini, selain membuang apa yang akan saya tulis secara interaktif ke file dan kemudian membaca file itu kembali. Mungkin terlalu rumit untuk apa yang ingin kita lakukan secara interaktif.

Terima kasih, btw.

Maaf untuk terus berbicara di sini, tetapi itu hanya membuat sesi interaktif terasa sangat kikuk:

Python biasa:

>>> d={'one':1, 'two':2}
>>> getkeys=lambda: d.keys()
>>> getkeys()
['two', 'one']

IPython Reguler:

In [1]: d={'one':1, 'two':2}

In [2]: getkeys=lambda: d.keys()

In [3]: getkeys()
Out[3]: ['two', 'one']

IPython tertanam:

>>> from IPython import embed
>>> embed()
Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12.dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: d={'one':1, 'two':2}

In [2]: getkeys=lambda: d.keys()

In [3]: getkeys()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/Users/asadeveloper/Documents/Dev/code/pyon/bin/python in <module>()
----> 1 getkeys()

/Users/asadeveloper/Documents/Dev/code/pyon/bin/python in <lambda>()
----> 1 getkeys=lambda: d.keys()

NameError: global name 'd' is not defined

Saya kira tidak banyak yang bisa dilakukan, tapi saya tidak mengerti mengapa kita bisa membuat penutupan dinamis di python / ipython biasa tetapi bukan versi yang disematkan.

Dengan keanehan pelingkupan Python, dalam Python / IPython standar, kami sebenarnya tidak membuat penutupan. d adalah variabel global, dan aturan untuk mengaksesnya bekerja secara berbeda dari closure. Setiap fungsi menyimpan referensi ke namespace global tempat ia didefinisikan ( getkeys.func_globals ), sehingga dapat mengakses variabel apa pun yang ditentukan di sana.

Sebaliknya, saat Anda membuat closure, Python melampirkan referensi ke setiap variabel yang telah ditutupnya, seperti yang ditentukan oleh compiler - tetapi itu hanya berfungsi ketika fungsi dalam dan luar dikompilasi pada waktu yang sama. Ini terlihat seperti ini:

In [8]: def outer():
    a = 1
    def inner():
        return a
    return inner
   ...: 

In [9]: f = outer()

In [10]: f
Out[10]: <function __main__.inner>

In [11]: f.func_closure
Out[11]: (<cell at 0x9f5e344: int object at 0x9a830b0>,)

Ini mungkin dilakukan untuk menghemat memori - jika closure membawa referensi ke lingkup lokal tempat ia didefinisikan, tidak ada variabel dari ruang lingkup itu yang bisa dibebaskan saat closure ditayangkan.

Saya menggunakan Python 2.7.2 dan IPython 0.12.1 di OSX 10.7.3 dan masih mengalami masalah ini. Ketika saya menjalankan ./manage.py shell dari Django yang memanggil IPython.embed() , masalah terjadi. Namun, secara manual memanggil embed() di Python shell atau dari file script sederhana, tidak ada masalah.

Tidak banyak yang bisa kita lakukan secara langsung tentang itu, tetapi kita mungkin harus mendorong pihak ketiga untuk menjauh dari embed untuk penggunaan yang tidak sepele.

@takluyver Apakah maksud Anda lebih baik menggunakan ipython secara langsung dalam kasus ini?

Mungkin saja Django memulai IPython dengan cara yang tidak akan menyebabkan masalah ini, tetapi itu bukan cara yang kami buat saat ini mudah. Masalahnya terjadi saat IPython dimulai dengan ruang nama lokal dan global yang terpisah. Tidak ada alasan Django membutuhkan ruang nama lokal dan global yang terpisah, tapi itulah yang memanggil embed() di dalam fungsi.

Untuk referensi, berikut kode di Django:
https://code.djangoproject.com/browser/django/trunk/django/core/management/commands/shell.py

@takluyver Masuk akal, terima kasih! Saya membuka tiket Django untuk ini.

@takluyver , sekarang kita telah menggabungkan embed_kernel sehingga semua bagian utama berada di tempatnya, apakah Anda ingin sedikit membersihkan ini untuk membuat penggunaan yang lebih baik lebih mudah?

Saya akan melihat antarmuka apa yang paling masuk akal.

Saya masih mengalami masalah dengan masalah ini. Saya telah mencoba mempersempit apa yang menyebabkannya, dan yang terbaik yang dapat saya tentukan adalah itu hanya terjadi di Ubuntu 12.04. Saya sudah mencoba semua versi rilis terbaru di server lain, dan berfungsi dengan baik. Tetapi setiap kali saya mendefinisikan fungsi di ipython atau menggunakan% cpaste untuk menempelkan fungsi dari file lain, bagian dalam fungsi itu tidak memiliki akses ke ruang lingkup global. Itu pada dasarnya membuat tidak mungkin untuk melakukan sesuatu yang berguna dalam hal fungsi penulisan dengan cepat.

Saya masih mengalami masalah ini dengan IPython 0.13 ketika dipanggil dari alat lain (misalnya perintah debugsqlshell Django). Sangat membuat frustasi bahwa sesuatu yang mendasar seperti mendefinisikan suatu fungsi benar-benar rusak.

Saya pikir embed () adalah antarmuka yang salah untuk digunakan. embed () adalah
lebih ditujukan untuk memeriksa status program yang sedang berjalan, sehingga menggunakan
pisahkan ruang nama lokal dan global. Untuk mengatasi masalah ini, ipython
harus dimulai dengan satu antarmuka. Maaf, saya belum sempat
mencari tahu cara terbaik untuk melakukannya.

Tidak hanya ubuntu. debian wheezy juga menunjukkan hal itu.

FYI, tiket Django @liokm yang dibuat di atas adalah https://code.djangoproject.com/ticket/18204 , yang sekarang mengarah ke https://code.djangoproject.com/ticket/17078 , yang sepertinya sudah diperbaiki di bagasi . Itu harus mendarat di 1,5.

Saya mengalami masalah yang sama di Ubuntu dengan Ipython 0.13.2
screenshot from 2013-08-07 18 13 33

@bkvirendra yang diperbaiki di django 1.6

Tapi 1,6 bahkan belum stabil!

Tapi 1,6 bahkan belum stabil!

Perangkat lunak tidak selalu stabil, dan di antara rilis mungkin masih ada bug. Tetapi tidak ada yang harus diperbaiki di IPython. Bahkan jika kita melakukan sesuatu di sini, perbaikannya tidak akan ada di IPython stable sebelum dirilis ...

Masalah masih berlanjut di ipython == 4.2.0. Menariknya di bawah windows tidak ada masalah tetapi bersama dengan variabel global ubuntu tidak dikenali.

Kasus penggunaan:

from ipywidgets import interact, FloatSlider, IntSlider,RadioButtons, Dropdown

@interact(sv1 = radio_1, Scenario = drop_1, sv2 = slider_2)
def update_map(sv1,sv2, Scenario):

Masalah tetap ada. IPython 3.1.0, Debian Whezzy.

Jika saya mengingatnya dengan benar, ini telah diperbaiki untuk beberapa saat, dan tampaknya diperkenalkan kembali (direproduksi menggunakan IPython 5.1.0 di macOS di sini: https://git.io/vPDrJ).

(edit: Saya mungkin salah mengingat perbaikannya. Mungkin saja saya memasukkan semua yang saya butuhkan ke dalam file startup daripada menggunakan embed)

Untuk berjaga-jaga jika ini membantu siapa pun, saya telah menggunakan potongan kode ini sebagai solusi

    def fix_embed_globals(N=0):
        # Get the parent frame
        import inspect
        frame_level0 = inspect.currentframe()
        frame_cur = frame_level0
        N = 2  # I may have this value off by one. I rewrote this function to use only standard calls
        strict = False
        for _ix in range(N + 1):
            frame_next = frame_cur.f_back
            if frame_next is None:
                if strict:
                    raise AssertionError('Frame level %r is root' % _ix)
                else:
                    break
            frame_cur = frame_next
        parent_frame = frame_cur
        # Add locals to parent globals
        parent_frame.f_locals.update(parent_frame.f_globals)
        parent_frame.f_globals['_didfixipyglobals'] = True

Saya hanya memanggil fungsi itu setiap kali saya mendapatkan kesalahan nama. Ini menempatkan semua penduduk setempat dalam bingkai Anda saat ini ke dalam kamus global. Ini hacky tetapi berfungsi dalam banyak situasi.

Seperti disebutkan di https://github.com/inducer/pudb/issues/103 , solusi sementara saat menggunakan shell tertanam adalah: globals().update(locals()) (hanya setelah menentukan variabel global lokal).

Hmm, apakah milestone dari bug ini dapat diubah ke salah satu versi berikutnya?

Selesai.

akankah ini diperbaiki?

AFAIK, komentar saya dari beberapa tahun yang lalu berlaku:

  1. Saya yakin kita dibatasi oleh cara kerja Python itu sendiri. Penutupan dan evaluasi dinamis tidak bekerja sama dengan baik, dan IPython tidak dapat memperbaikinya. Orang-orang telah menemukan solusi untuk memindahkan variabel lokal ke namespace global, tetapi itu adalah peretasan yang dapat menyebabkan masalah lain, karena mereka memodifikasi namespace global. Saya tidak berencana untuk menerapkan solusi seperti itu ke IPython; Saya lebih suka meninggalkan masalah dengan sesuatu yang tidak kita lakukan daripada memperkenalkannya dengan mencoba menjadi terlalu pintar.

  2. Banyak tempat di mana orang mengira mereka ingin embed() , mereka seharusnya menggunakan start_ipython() dan menghindari masalah seperti ini.

Saya tidak mengerti, dan itu membuat saya marah.

maaf, saya kecewa karena hal-hal yang akan berfungsi jika Anda mengetiknya ke dalam file .py kosong tidak berfungsi di sini

tetapi membaca ulang riwayat, tampaknya ini adalah masalah khusus manage.py shell Django, 90% dari waktu saya di ipython saya melakukan itu tetapi mudah untuk melupakan itu masalahnya

secara memalukan saya bekerja banyak dalam versi usang Django (1.4)

menurut komentar sebelumnya, versi terbaru dari shell Django menggunakan ipython secara berbeda dan mungkin tidak memiliki masalah? misalnya https://code.djangoproject.com/ticket/17078

permintaan maaf atas kesalahpahaman

Yup, saya pikir itu sudah diperbaiki untuk Django 1.6. Jika Anda benar-benar tidak dapat meningkatkan, Anda mungkin ingin menerapkan perbaikan secara manual. Sepertinya ini dia: https://github.com/django/django/commit/3570ff734e93f493e023b912c9a97101f605f7f5

Saya menemukan solusi hari ini yang diposting di # 10695 . Perawatan yang lebih sederhana untuk kasus di mana IPython tidak disematkan di dalam suatu fungsi ditampilkan di utas ini . Saya bukan ahli jadi tolong bantu untuk memeriksa validitas.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat