python的作用域问题
高洛峰
高洛峰 2017-04-18 09:29:09
0
4
753
x = 3
y = [3]

def test1():
    x += 1
    print x

def test2():
    y[0] = 4
    y.append(5)
    print y

test2()
test1()

这段代码执行结果: test2()成功打印[4, 5], test1()却报错: UnboundLocalError: local variable 'x' referenced before assignment

想不明白,为什么会这样,全局变量在函数里可以直接打印,但是如果要改变它的值,会报错,但是test2()为什么不报错?如果把y换成dict类型,就能在函数里不需要用global声明,可以直接改变y的值,但如果是str或number,就会报错,为什么?

高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

membalas semua(4)
洪涛

Malah, soalan ini boleh diketepikan sama ada objek yang dirujuk oleh pembolehubah itu boleh berubah atau tidak berubah Apa yang perlu kita perhatikan ialah kedudukan pembolehubah, titik masa dan skop. definisi.

Pertimbangkan kod berikut:

def test(a):
    print(a)
    print(b)
    
test(1)

Kod ini akan menimbulkan ralat:

1
Traceback (most recent call last):
  File "tp.py", line 5, in <module>
    test(1)
  File "tp.py", line 3, in test
    print(b)
NameError: name 'b' is not defined

a ialah pembolehubah param fungsi test, jadi ia tergolong dalam pembolehubah tempatan, dan skopnya ialah fungsi test Kami luluskan 1 dalam untuk dirujuk, jadi tiada masalah besar dengan print(a).
Tetapi b tidak ditakrifkan dari awal hingga akhir Walaupun anda mencari mengikut prinsip LEGB, anda tidak dapat menemuinya, jadi anda menaikkan NameError.

Untuk menyelesaikan masalah ini, mungkin kita boleh menentukan pembolehubah global:

b = 100

def test(a):
    print(a)
    print(b)
    
test(1)

Bagus, kali ini nampaknya tiada masalah, kerana Python menemui b dalam skop global Sebab Python menggunakan global b adalah kerana kami tidak mentakrifkan b dalam bahasa tempatan.

Kemudian kita mula menetapkan pembolehubah dalam fungsi:

b = 100

def test(a):
    b = 20
    print(a)
    print(b)
    
test(1)
print(b)

Keputusan:

1
20
100

Dua print dalam fungsi mencetak 1 dan 20 tidak menghairankan, tetapi mengapa nilai b dicetak selepas meninggalkan fungsi menjadi 100?
Kerana kami menulis ini dalam fungsi A tugasan juga merupakan definisi b = 20, jadi test yang dilihat dalam b semuanya tempatan, bukan global, jadi sebarang perubahan yang kami buat kepada b Tiada perubahan akan menjejaskan global b.

Ini memberitahu kami:

Apabila pembolehubah tempatan tidak ditakrifkan, Python akan secara automatik menggunakan pembolehubah global, jika tidak, ia tidak akan

Maka bagaimana kita boleh mengendalikan pembolehubah global dalam fungsi ini memerlukan bantuan kata kunci global:

b = 100

def test(a):
    global b
    b = 20
    print(a)
    print(b)
    
test(1)
print(b)
1
20
20

Dengan kata kunci global yang menerangkan b, Python akan menganggap test dalam b sebagai pembolehubah untuk mengakses global b dan b = 20 hanya akan dianggap sebagai Tindakan tugasan tidak mentakrifkan pembolehubah tempatan baharu.

Mari kita lihat contoh yang lebih mengelirukan:

b = 100

def test(a):
    print(a)
    print(b)
    b = 20
    
test(1)
print(b)

Keputusan:

1
Traceback (most recent call last):
  File "tp.py", line 8, in <module>
    test(1)
  File "tp.py", line 5, in test
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment

muncul di sini UnboundLocalError, mengapa ini berlaku Sebabnya sangat mudah, b mempunyai tindakan tugasan dan definisi dalam fungsi ini: b = 20, jadi test di dalam b adalah setempat (global tidak digunakan di sini untuk menunjukkan bahawa b adalah global), jadi apabila print(b) digunakan, Python akan cuba merebut tempatan b bukannya global b, tetapi tragedinya ialah di sini Pada langkah pertama, tempatan b belum ditugaskan, jadi dikatakan local variable 'b' referenced before assignment dirujuk sebelum ditugaskan, yang secara semula jadi tidak mungkin.

Lihat kembali contoh yang anda berikan:

x = 3

def test1():
    x += 1
    print x

Di sini, takrifan test1 berlaku dalam x (tiada global, dan x muncul di sebelah kiri tanda sama dengan itu, apabila merujuk kepada x, anda pasti mahu gunakan tempatan, tetapi Kerana:

x += 1 等義 x = x + 1

Jadi apabila anda ingin mendapatkan nilai yang dirujuk oleh x di sebelah kanan tanda sama, anda mendapati ia belum diberikan nilai lagi, maka ini adalah sama seperti contoh di atas:

UnboundLocalError: local variable 'x' referenced before assignment

Bagi test2 anda tidak akan ada masalah:

y = [3]

def test2():
    y[0] = 4
    y.append(5)
    print y

adalah kerana y tidak muncul di sebelah kiri tanda sama dalam test2, jadi Python secara automatik menganggap bahawa global y digunakan, jadi sudah tentu tiada masalah!

Kesimpulan

  1. Untuk melihat sama ada pembolehubah tempatan ditakrifkan dalam fungsi, lihat sama ada nama pembolehubah muncul di sebelah kiri tanda sama dan nama pembolehubah tidak ditentukan oleh

    global

  2. Jika pembolehubah tempatan ditakrifkan, Python tidak akan mencari pembolehubah global, jika tidak Python akan menggunakan pembolehubah global secara automatik

  3. Jika variabel tempatan ditakrifkan tetapi anda membaca rujukan sebelum pembolehubah ditetapkan, UnboundLocalError akan muncul

  4. Untuk menyelesaikan masalah ini, sila ingat untuk menambah

    untuk menunjukkan keseluruhan domain, jika tidak, kaedah penulisan ini tidak sepatutnya muncul (operasi di tempat atau tugasan kemudian)global

  5. Masalah yang serupa juga akan berlaku dalam fungsi setempat Penyelesaiannya adalah dengan menambah

    (Python3), tetapi itu cerita lain. nonlocal


Soalan yang saya jawab: Python-QA

洪涛

Skop pembolehubah Python ditetapkan dengan cara ini dalam ujian1, anda boleh print x tetapi tidak boleh mengubah suai x
Sesetengah orang mengatakan bahawa "pembolehubah global dalam skop tempatan harus dibaca sahaja", tetapi ini tidak benar untuk. pembolehubah rujukan keadaan ini. . .

Tempat dalam ular sawa ini sungguh mengelirukan

Peter_Zhu

Ringkasnya, pengikatan pembolehubah global tidak boleh ditukar dalam skop setempat, dan alamat pembolehubah tidak boleh ditukar dalam CPython.

Lihat: Kesilapan Biasa 4 dalam: Sepuluh Kesilapan Paling Biasa Yang Dilakukan Pengaturcara Python: Salah Faham Penghuraian Nama Pembolehubah dalam Python

伊谢尔伦

Fungsi test1 anda mempunyai operasi penugasan x += 1, yang dianggap oleh penterjemah Python sebagai pembolehubah dalam skop tempatan fungsi Walau bagaimanapun, pembolehubah x tidak ditakrifkan sebelum tugasan, dan x ialah number, tergolong dalam jenis tidak berubah Untuk menetapkan nilai baharu kepada jenis tidak berubah, anda perlu mencipta semula objek jenis tidak berubah dan menunjuk semula pembolehubah asal ke objek yang baru dibuat, tetapi ini. objek yang baru dicipta tidak termasuk dalam LEGB Tidak ditemui, jadi ralat akan dilaporkan

Dalam fungsi

test2, y ialah list, yang merupakan jenis pembolehubah Senarai masih menunjuk ke alamat memori yang sama selepas lampiran kerana senarai adalah jenis pembolehubah dan boleh diubah suai , ia tidak Laporkan ralat

Semua operasi tugasan dalam

Python pada asasnya dibahagikan kepada tiga langkah berikut (mengambil a = 3 sebagai contoh):

  1. Buat objek untuk mewakili nilai3

  2. Buat pembolehubah a, jika ia belum dibuat lagi

  3. Sambungkan pembolehubah a ke objek baharu 3

Mari kita perhatikan perubahan alamat memori pembolehubah test2 semasa pelaksanaan fungsi y anda

y = [3]
print(id(y))
def test2():
    y[0] = 4
    print(id(y))
    y.append(5)
    print(id(y))
    print(y)
    
test2()
x = 3
print(id(x))
x = 4
print(id(x))

Output

79051032
79051032
79051032
[4, 5]
1375754544
1375754560

Anda dapat melihat bahawa y sebenarnya diubah suai di tempatnya, jadi tidak perlu membuat pembolehubah berulang kali dan untuk x jenis tidak berubah, untuk memberikan nilai kepadanya, anda mesti mencipta objek baharu dahulu, walaupun nama Sama, anda dapat melihat bahawa jika tugasan berbeza dibuat setiap kali, alamat memori x sebenarnya berbeza, yang juga boleh menjelaskan mengapa str atau number akan melaporkan ralat

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan