Seberapa burukkah nama bayangan didefinisikan dalam lingkup luar?

208

Saya baru saja beralih ke Pycharm dan saya sangat senang dengan semua peringatan dan petunjuk yang diberikannya untuk memperbaiki kode saya. Kecuali untuk yang ini saya tidak mengerti:

This inspection detects shadowing names defined in outer scopes.

Saya tahu itu praktik buruk untuk mengakses variabel dari lingkup luar tetapi apa masalah dengan membayangi lingkup luar?

Ini adalah satu contoh, di mana Pycharm memberi saya pesan peringatan:

data = [4, 5, 6]

def print_data(data): # <-- Warning: "Shadows 'data' from outer scope
    print data

print_data(data)
Framester
sumber
1
Saya juga mencari string "Inspeksi ini mendeteksi ..." tetapi tidak menemukan apa pun dalam bantuan online pycharm
Framester
1
Untuk mematikan pesan ini di PyCharm: <Ctrl> + <Alt> + s (pengaturan), Editor , Inspeksi , " Membayangi nama dari lingkup luar ". Hapus centang.
ChaimG

Jawaban:

222

Bukan masalah besar dalam cuplikan di atas, tetapi bayangkan sebuah fungsi dengan beberapa argumen dan beberapa baris kode. Kemudian Anda memutuskan untuk mengubah nama dataargumen Anda sebagai yaddatetapi melewatkan salah satu tempat yang digunakan dalam fungsi fungsi ... Sekarang datamengacu pada global, dan Anda mulai memiliki perilaku aneh - di mana Anda akan memiliki jauh lebih jelas NameErrorjika Anda tidak memiliki nama global data.

Juga ingat bahwa dalam Python semuanya adalah objek (termasuk modul, kelas, dan fungsi) sehingga tidak ada ruang nama yang berbeda untuk fungsi, modul, atau kelas. Skenario lain adalah Anda mengimpor fungsi foodi bagian atas modul Anda, dan menggunakannya di suatu tempat di badan fungsi Anda. Kemudian Anda menambahkan argumen baru ke fungsi Anda dan menamainya - nasib buruk - foo.

Akhirnya, fungsi dan tipe bawaan juga hidup di namespace yang sama dan dapat dibayangi dengan cara yang sama.

Tak satu pun dari ini banyak masalah jika Anda memiliki fungsi pendek, penamaan yang baik dan cakupan unittest yang layak, tetapi baik, kadang-kadang Anda harus mempertahankan kode kurang sempurna dan diperingatkan tentang masalah yang mungkin dapat membantu.

perusak Bruno
sumber
21
Untungnya PyCharm (seperti yang digunakan oleh OP) memiliki operasi penggantian nama yang sangat bagus yang mengubah nama variabel di mana pun ia digunakan dalam cakupan yang sama, yang membuat kemungkinan kesalahan penggantian nama lebih kecil.
wojtow
Selain operasi penggantian nama PyCharm, saya ingin memiliki highlight sintaks khusus untuk variabel yang merujuk ke lingkup luar. Keduanya harus membuat game resolusi bayangan yang memakan waktu ini tidak relevan.
Leo
Catatan samping: Anda dapat menggunakan nonlocalkata kunci untuk membuat rujukan skor luar (seperti pada penutupan) secara eksplisit. Perhatikan bahwa ini berbeda dari membayangi, karena secara eksplisit tidak membayangi variabel dari luar.
Felix D.
149

Jawaban yang saat ini paling banyak dipilih dan diterima dan sebagian besar jawaban di sini tidak tepat.

Tidak masalah berapa lama fungsi Anda, atau bagaimana Anda memberi nama variabel secara deskriptif (semoga meminimalkan kemungkinan tabrakan nama potensial).

Fakta bahwa variabel lokal fungsi Anda atau parameternya terjadi untuk membagikan nama dalam lingkup global sama sekali tidak relevan. Dan faktanya, tidak peduli seberapa hati-hati Anda memilih nama variabel lokal, fungsi Anda tidak akan pernah dapat memperkirakan "apakah nama keren saya yaddajuga akan digunakan sebagai variabel global di masa mendatang?". Solusinya? Jangan khawatir tentang itu! Pola pikir yang benar adalah merancang fungsi Anda untuk menggunakan input dari dan hanya dari parameternya di tanda tangan , dengan begitu Anda tidak perlu peduli dengan apa (atau akan) dalam lingkup global, dan kemudian membayangi menjadi bukan masalah sama sekali.

Dengan kata lain, masalah bayangan hanya penting ketika fungsi Anda perlu menggunakan nama variabel lokal DAN variabel global yang sama. Tetapi Anda harus menghindari desain semacam itu sejak awal. Kode OP TIDAK benar-benar memiliki masalah desain seperti itu. Hanya saja PyCharm tidak cukup pintar dan mengeluarkan peringatan untuk berjaga-jaga. Jadi, hanya untuk membuat PyCharm senang, dan juga membuat kode kita bersih, lihat solusi ini mengutip dari jawaban silyevsk untuk menghapus variabel global sepenuhnya.

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()

Ini adalah cara yang tepat untuk "menyelesaikan" masalah ini, dengan memperbaiki / menghapus hal global Anda, tidak menyesuaikan fungsi lokal Anda saat ini.

RayLuo
sumber
11
Ya, tentu saja, di dunia yang sempurna, Anda mungkin membuat kesalahan ketik, atau melupakan salah satu ganti pencarian Anda ketika Anda mengubah parameter, tetapi kesalahan terjadi dan itulah yang dikatakan PyCharm - "Peringatan - tidak ada yang secara teknis salah, tapi ini dapat dengan mudah menjadi masalah "
dwanderson
1
@dwanderson Situasi yang Anda sebutkan bukanlah hal yang baru, jelas dijelaskan dalam jawaban yang saat ini dipilih. Namun, poin yang saya coba sampaikan adalah bahwa kita harus menghindari variabel global, bukan menghindari variabel global yang membayangi. Yang terakhir melewatkan intinya. Mengerti? Mengerti?
RayLuo
4
Saya sepenuhnya setuju pada fakta bahwa fungsi harus "murni" mungkin tetapi Anda benar-benar kehilangan dua poin penting: tidak ada cara untuk membatasi Python dari mencari nama di lingkup terlampir jika tidak ditentukan secara lokal, dan semuanya (modul , fungsi, kelas dll) adalah objek dan tinggal di namespace yang sama dengan "variabel" lainnya. Dalam cuplikan di atas, print_dataIS adalah variabel global. Pikirkan tentang hal itu ...
bruno desthuilliers
2
Saya berakhir di utas ini karena saya menggunakan fungsi yang didefinisikan dalam fungsi, untuk membuat fungsi luar lebih mudah dibaca tanpa mengacaukan namespace global atau menggunakan file terpisah secara berat. Contoh ini di sini tidak berlaku untuk kasus umum, variabel non-lokal non-global yang dibayangi.
micseydel
2
Setuju. Masalahnya di sini adalah pelingkupan Python. Akses tidak eksplisit ke objek di luar ruang lingkup saat ini meminta masalah. Siapa yang mau itu! Sayang sekali karena kalau tidak Python adalah bahasa yang dipikirkan dengan cukup baik (tidak tahan ambiguitas serupa dalam penamaan modul).
CodeCabbie
24

Solusi yang baik dalam beberapa kasus mungkin untuk memindahkan kode vars + ke fungsi lain:

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()
silyevsk
sumber
Iya. Saya pikir ide yang bagus adalah mampu menangani variabel lokal dan variabel global dengan refactoring. Tip Anda benar-benar membantu menghilangkan potensi risiko seperti itu untuk ide primitif
stanleyxu2005
5

Tergantung berapa lama fungsinya. Semakin lama fungsinya, semakin besar peluang seseorang memodifikasinya di masa depan akan menulis databerpikir bahwa itu berarti global. Sebenarnya itu berarti lokal tetapi karena fungsinya begitu lama tidak jelas bagi mereka bahwa ada lokal dengan nama itu.

Sebagai contoh fungsi Anda, saya pikir membayangi global tidak buruk sama sekali.

Steve Jessop
sumber
5

Melakukan hal ini:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()

sumber
3
data = [4, 5, 6] #your global variable

def print_data(data): # <-- Pass in a parameter called "data"
    print data  # <-- Note: You can access global variable inside your function, BUT for now, which is which? the parameter or the global variable? Confused, huh?

print_data(data)
JoeC
sumber
47
Saya tidak bingung. Ini jelas parameternya.
2
@delnan Anda mungkin tidak bingung dalam contoh sepele ini, tetapi bagaimana jika fungsi-fungsi lain yang didefinisikan di dekatnya menggunakan global data, semua dalam beberapa ratus baris kode?
John Colanduoni
13
@HevyLight Saya tidak perlu melihat fungsi lain di dekatnya. Saya melihat fungsi ini saja dan dapat melihat bahwa dataadalah nama lokal dalam hal ini fungsi, jadi saya tidak repot-repot memeriksa / mengingat apakah global dengan nama yang sama eksis , apalagi apa yang dikandungnya.
4
Saya tidak berpikir alasan ini valid, hanya karena untuk menggunakan global, Anda perlu mendefinisikan "data global" di dalam fungsi. Jika tidak, global tidak dapat diakses.
CodyF
1
@CodyF False- jika Anda tidak mendefinisikan, tapi hanya mencoba untuk menggunakan data, terlihat melalui cakupan sampai menemukan satu, sehingga tidak menemukan global data. data = [1, 2, 3]; def foo(): print(data); foo()
dwanderson
3

Saya suka melihat tanda centang hijau di sudut kanan atas di pycharm. Saya menambahkan nama variabel dengan garis bawah hanya untuk menghapus peringatan ini sehingga saya dapat fokus pada peringatan penting.

data = [4, 5, 6]

def print_data(data_): 
    print(data_)

print_data(data)
Baz
sumber
2

Sepertinya pola kode 100% pytest

Lihat:

https://docs.pytest.org/en/latest/fixture.html#conftest-py-sharing-fixture-functions

Saya memiliki masalah yang sama dengan, ini sebabnya saya menemukan posting ini;)

# ./tests/test_twitter1.py
import os
import pytest

from mylib import db
# ...

@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

Dan itu akan memperingatkan This inspection detects shadowing names defined in outer scopes.

Untuk memperbaikinya, cukup pindahkan twitterperlengkapan ke./tests/conftest.py

# ./tests/conftest.py
import pytest

from syntropy import db


@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

Dan hapus twitterfixture seperti di./tests/test_twitter2.py

# ./tests/test_twitter2.py
import os
import pytest

from mylib import db
# ...

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

Ini akan membuat QA, Pycharm, dan semua orang senang

Andrei.Danciuc
sumber