Dış kapsamlarda gölgelendirme adları ne kadar kötü tanımlanır?


208

Sadece Pycharm'a geçtim ve kodumu geliştirmem için bana sağladığı tüm uyarı ve ipuçlarından çok memnunum. Anlamadığım bu hariç:

This inspection detects shadowing names defined in outer scopes.

Değişkene dış kapsamdan erişmenin kötü bir uygulama olduğunu biliyorum ama dış kapsamın gölgelenmesiyle ilgili sorun nedir?

İşte bir örnek, Pycharm bana uyarı mesajı veriyor:

data = [4, 5, 6]

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

print_data(data)

1
Ayrıca "Bu inceleme algılar ..." dizesini aradım ama pycharm çevrimiçi yardımında hiçbir şey bulamadım: jetbrains.com/pycharm/webhelp/getting-help.html
Framester 21:13

1
PyCharm'da bu mesajı kapatmak için: <Ctrl> + <Alt> + s (ayarlar), Editör , Denetimler , " Dış kapsamlardan gölgeleme adları ". İşaretini kaldırın.
ChaimG

Yanıtlar:


222

Yukarıdaki snippet'inizde büyük bir anlaşma yok, ancak birkaç argüman ve birkaç kod satırı daha olan bir fonksiyon düşünün. O zaman dataargümanınızı yeniden adlandırmaya karar veriyorsunuz yaddaancak fonksiyonun vücudunda kullanılan yerlerden birini kaçırıyorsunuz ... Şimdi dataküresel olana atıfta bulunuyorsunuz ve garip davranışlara başlıyorsunuz - NameErroreğer yapmazsanız çok daha açık olacağınız global bir isme sahip olmak data.

Ayrıca Python'da her şeyin bir nesne olduğunu (modüller, sınıflar ve işlevler dahil) unutmayın, bu nedenle işlevler, modüller veya sınıflar için ayrı bir ad alanı yoktur. Başka bir senaryo, foomodülü modülünüzün üst kısmına içe aktarmanız ve işlev gövdenizde bir yerde kullanmanızdır. Sonra işlevinize yeni bir argüman eklediniz ve buna - kötü şans - adını verdiniz foo.

Son olarak, yerleşik işlevler ve türler de aynı ad alanında yaşar ve aynı şekilde gölgelenebilir.

Kısa işlevleriniz, iyi adlandırma ve iyi bir birim testi kapsamı varsa bunların hiçbiri sorun değildir, ancak bazen mükemmel koddan daha azını korumak zorunda kalırsınız ve bu tür olası konular hakkında uyarılmak yardımcı olabilir.


21
Neyse ki PyCharm (OP tarafından kullanıldığı gibi), aynı kapsamda kullanıldığı her yerde değişkeni yeniden adlandıran çok güzel bir yeniden adlandırma işlemine sahiptir, bu da yeniden adlandırma hatalarını daha az olası hale getirir.
wojtow

PyCharm'ın yeniden adlandırma işlemine ek olarak, dış kapsamı ifade eden değişkenler için özel sözdizimi vurgularına sahip olmak isterim. Bu ikisi bu kez tüketen gölge çözünürlük oyununu alakasız hale getirmelidir.
Leo

Yan not: nonlocalAnahtar kelimeyi kullanarak dış puan referansını (kapanışlarda olduğu gibi) açık yapabilirsiniz. Açıkça değişkenleri dışarıdan gölgelediği için bunun gölgelendirmeden farklı olduğunu unutmayın.
Felix

149

Şu anda en çok oylanan ve kabul edilen cevap ve çoğu cevap burada özlüyor.

İşlevinizin ne kadar uzun olduğu veya değişkeninizi tanımlayıcı olarak nasıl adlandırdığınız önemli değildir (umarım potansiyel isim çarpışma olasılığını en aza indirmek için).

İşlevinizin yerel değişkeninin veya parametresinin genel kapsamda bir ad paylaşması gerçeğiyle tamamen ilgisizdir. Ve aslında, yerel değişken adını ne kadar dikkatli seçerseniz seçin, fonksiyonunuz "serin ismimin yaddagelecekte küresel bir değişken olarak da kullanıp kullanmayacağını" asla öngöremez . Çözüm? Bunun için endişelenme! Doğru zihniyet, işlevinizi yalnızca imzadaki parametrelerinden ve yalnızca parametrelerinden girdi tüketecek şekilde tasarlamaktır , böylece küresel kapsamda ne olduğunu (veya olacağını) önemsemenize gerek kalmaz ve daha sonra gölgeleme hiç sorun olmaz.

Başka bir deyişle, gölgeleme sorunu yalnızca işlevinizin aynı adı yerel değişkeni VE global değişkeni kullanması gerektiğinde önemlidir. Ancak ilk etapta böyle bir tasarımdan kaçınmalısınız. OP'nin kodunda böyle bir tasarım problemi YOKTUR. Sadece PyCharm yeterince akıllı değildir ve her ihtimale karşı bir uyarı verir. Bu nedenle, sadece PyCharm'ı mutlu etmek ve kodumuzu temizlemek için, küresel değişkeni tamamen kaldırmak için silyevsk'in cevabından alıntı yapan bu çözüme bakın .

def print_data(data):
    print data

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

main()

Bu, mevcut yerel işlevinizi ayarlayarak değil, küresel olanı düzelterek / kaldırarak bu sorunu "çözmek" için uygun yoldur.


11
Elbette, mükemmel bir dünyada, bir yazım hatası yaparsınız veya parametreyi değiştirdiğinizde arama değiştirme işlemlerinden birini unutursunuz, ancak hatalar olur ve PyCharm'ın söylediği şey budur - "Uyarı - teknik olarak hiçbir şey hatalı değildir, ancak bu kolayca bir sorun olabilir "
dwanderson

1
@dwanderson Belirttiğiniz durum yeni bir şey değil, o anda seçili olan cevapta açıkça tanımlanıyor. Ancak, anlatmaya çalıştığım nokta, küresel değişkeni gölgelemekten kaçınmak değil, küresel değişkenden kaçınmamız gerektiğidir. İkincisi noktayı kaçırır. Anla? Anladım?
RayLuo

4
İşlevlerin olabildiğince "saf" olması gerektiği konusunda tamamen katılıyorum, ancak iki önemli noktayı tamamen özlüyorsunuz: Python'un yerel olarak tanımlanmadıysa kapalı kapsamlarda bir ad aramasını kısıtlamanın bir yolu yok ve her şey (modüller) , fonksiyonlar, sınıflar vb.) bir nesnedir ve diğer herhangi bir "değişken" ile aynı ad alanında yaşar. Yukarıdaki snippet'inizde print_dataküresel bir değişkendir. Bir düşünün ...
bruno desthuilliers

2
Bu iş parçacığı üzerinde sona erdi çünkü işlevlerde tanımlanan işlevleri kullanıyorum, dış işlevi genel ad alanını dağınıklaştırmadan veya ayrı ayrı dosyalar kullanarak daha rahat okunabilir hale getirmek için. Buradaki örnek, yerel olmayan global olmayan değişkenlerin gölgelendiği genel durum için geçerli değildir.
micseydel

2
Katılıyorum. Buradaki sorun Python kapsam belirleme. Geçerli kapsamın dışındaki nesnelere açık olmayan erişim, sorun istiyor. Kim ister ki! Utanç çünkü aksi takdirde Python oldukça iyi düşünülmüş bir dildir (modül adlandırmada benzer bir belirsizliğe dayanmaz).
CodeCabbie

24

Bazı durumlarda iyi bir geçici çözüm vars + kodunu başka bir işleve taşımak olabilir:

def print_data(data):
    print data

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

main()

Evet. İyi bir yöntemin yerel değişkenleri ve küresel değişkenleri yeniden düzenleyerek işleyebileceğini düşünüyorum.
Bahşişiniz

5

Bu fonksiyonun ne kadar sürdüğüne bağlıdır. İşlev ne kadar uzun olursa, gelecekte birisinin onu değiştirmesi data, bunun küresel anlamına geldiğini düşünerek daha fazla şansı yazacaktır . Aslında yerel anlamına gelir, ancak işlev çok uzun olduğu için, bu ada sahip bir yerel var olduğu açık değildir.

Örnek fonksiyonunuz için, küresel gölgelemenin hiç de fena olmadığını düşünüyorum.


5

Bunu yap:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()

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)

47
Ben biri için karışık değilim. Oldukça açık bir şekilde parametre.

2
@delnan Bu önemsiz örnekte kafanız karışmamış olabilir, ancak yakınlarda tanımlanan diğer işlevler küreselse data, hepsi de birkaç yüz satır kod içinde derinse?
John Colanduoni

13
@HevyLight Yakındaki diğer işlevlere bakmam gerekmiyor. Sadece bu işlev bakıp görebilirsiniz datayerel bir isimdir bu yüzden bile / kontrol aynı adlı küresel olsun hatırlamak rahatsız etmeyin, işlevi Varlığından , İçinde ne var dursun.

4
Bu akıl yürütmenin geçerli olduğunu düşünmüyorum, çünkü sadece bir global kullanmak için fonksiyonun içinde "global veri" tanımlamanız gerekecekti. Aksi takdirde, küresel erişilemez.
CodyF

1
@CodyF False- tanımladığınız, ama sadece kullanımına deneyin yoksa datao biri bulana kadar o kadar, o kapsamları ile bakar yapar küresel bulmak data. data = [1, 2, 3]; def foo(): print(data); foo()
dwanderson

3

Pycharm'ın sağ üst köşesinde yeşil bir onay işareti görmek istiyorum. Önemli uyarılara odaklanabilmek için bu uyarıyı temizlemek için değişken adlarını alt çizgiyle ekliyorum.

data = [4, 5, 6]

def print_data(data_): 
    print(data_)

print_data(data)

2

% 100 pytest kod kalıbı gibi görünüyor

görmek:

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

Ben de aynı sorunu vardı, bu yüzden bu yazı bulundu;)

# ./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

Ve uyarır This inspection detects shadowing names defined in outer scopes.

Bunu düzeltmek için twitterfikstürünüzü./tests/conftest.py

# ./tests/conftest.py
import pytest

from syntropy import db


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

Ve twitterfikstürü./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

Bu mutlu QA, Pycharm ve herkesi mutlu edecek

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.