Python'da kapsamı engelle


94

Diğer dillerde kod yazdığınızda, bazen aşağıdaki gibi bir blok kapsamı oluşturursunuz:

statement
...
statement
{
    statement
    ...
    statement
}
statement
...
statement

Amaçlardan biri (çoğunun) kod okunabilirliğini iyileştirmektir: belirli ifadelerin mantıksal bir birim oluşturduğunu veya belirli yerel değişkenlerin yalnızca o blokta kullanıldığını göstermek.

Python'da aynı şeyi yapmanın deyimsel bir yolu var mı?


2
One purpose (of many) is to improve code readability- Doğru yazılmış Python kodu (yani, python zenini takip ederek ) okunabilir olması için böyle bir garnitüre ihtiyaç duymaz. Aslında, Python hakkında sevdiğim (birçok) şeyden biri.
Burhan Halid

Oynamaya __exit__ve withifade etmeye çalıştım , değiştirdim globals()ama başarısız oldum.
Ruggero Turra

1
kaynak edinimine bağlı değişken yaşam
süresini

25
@BurhanKhalid: Bu doğru değil. Python'un zeni, burada burada geçici bir değişkenle yerel bir kapsamı kirletmenizi engellemez. Eğer açarsanız her kullanımını derhal denir iç içe bir işlevi tanımlayan örneğin içine tek geçici değişkenin, Python zen ya mutlu olmayacak. Bir değişkenin kapsamını açıkça sınırlamak, okunabilirliği artırmak için bir araçtır, çünkü doğrudan "bu tanımlayıcılar aşağıda mı kullanılıyor?" - en zarif Python kodunu okurken bile ortaya çıkabilecek bir soru.
bluenote10

18
@BurhanKhalid Bir özelliğin olmaması sorun değil. Ama buna "zen" demek iğrenç.
Phil

Yanıtlar:


83

Hayır, blok kapsamı oluşturmak için dil desteği yoktur.

Aşağıdaki yapılar kapsam oluşturur:

  • modül
  • sınıf
  • işlev (lambda dahil)
  • üretici ifadesi
  • kavramalar (dikte, küme, liste (Python 3.x'te))

38

Python'daki deyimsel yol, işlevlerinizi kısa tutmaktır. Buna ihtiyacınız olduğunu düşünüyorsanız, kodunuzu yeniden düzenleyin! :)

Python, her modül, sınıf, işlev, üreteç ifadesi, dikte anlama, küme anlama ve Python 3.x'te her liste kavrama için yeni bir kapsam oluşturur. Bunların dışında, işlevlerin içinde iç içe geçmiş kapsamlar yoktur.


12
"Programlamada en önemli şey, bir şeye bir ad verebilme yeteneğidir. İkinci en önemli şey, bir şeye bir ad verilmesi gerekmemesidir." Çoğunlukla, Python kapsamların (değişkenler vb. İçin) isimlendirilmesini gerektirir. Bu bakımdan Python değişkenleri ikinci en önemli testtir.
Krazy Glew

19
Programlamadaki en önemli şeyler, uygulamanızın bağımlılıklarını yönetme ve kod bloklarının kapsamlarını yönetme yeteneğidir. Anonim bloklar geri aramaların ömrünü sınırlamanıza izin verirken, aksi takdirde geri aramalarınız yalnızca bir kez kullanılır, ancak program süresince yaşar, bu genel kapsam karmaşasına neden olur ve kodun okunabilirliğine zarar verir.
Dmitry

Değişkenlerin de anlayışları dikte etmek / ayarlamak için yerel olduğunu fark ettim. Python 2.7 ve 3.3'ü denedim, ancak sürüme bağlı olup olmadığından emin değilim.
wjandrea

1
@wjandrea Haklısınız - listeye eklenmişsiniz. Bunların Python sürümleri arasında herhangi bir fark olmamalıdır.
Sven Marnach

4
İşlevler içinde çok iyi işlevler yaratabileceğiniz için son cümleyi yeniden söyleyeceğim. Dolayısıyla, işlevlerin içinde iç içe geçmiş kapsamlar vardır.
ThomasH

19

Python'daki bir C ++ blok kapsamına benzer bir şeyi, işlevinizin içinde bir işlev bildirip ardından hemen çağırarak yapabilirsiniz. Örneğin:

def my_func():
    shared_variable = calculate_thing()

    def do_first_thing():
        ... = shared_variable
    do_first_thing()

    def do_second_thing():
        foo(shared_variable)
        ...
    do_second_thing()

Bunu neden yapmak isteyebileceğinizden emin değilseniz, bu video sizi ikna edebilir.

Temel ilke, kesinlikle gerekenden daha geniş bir kapsamda herhangi bir 'çöp' (ekstra türler / işlevler) getirmeden her şeyi olabildiğince sıkı bir şekilde kapsamaktır - do_first_thing()Örneğin başka hiçbir şey yöntemi kullanmak istemez, bu nedenle kapsam dışında bırakılmamalıdır. çağırma işlevi.


Aynı zamanda Google geliştiricileri tarafından TensorFlow eğiticilerinde kullanılan yöntem de budur, örneğin burada
Nino Filiu

13

Blok kapsamı olmadığına katılıyorum. Ancak python 3'teki bir yer, blok kapsamı varmış gibi görünmesini sağlar.

bu bakışı veren ne oldu? Bu python 2'de düzgün çalışıyordu ancak python 3'te değişken sızıntıyı durdurmak için bu numarayı yaptılar ve bu değişiklik burada blok kapsamı varmış gibi görünmesini sağlıyor.

Açıklamama izin ver.


Kapsam fikrine göre, aynı kapsamda aynı isimlere sahip değişkenler sunduğumuzda, değeri değiştirilmelidir.

bu python 2'de olan şey

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'W'

Ancak python 3'te aynı isimli değişken tanıtılsa bile, listeyi anlama bir sebepten dolayı bir sandbox gibi davranır ve içinde yeni bir kapsam yaratır gibi görünür.

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'OLD'

ve bu cevap answerer @ Thomas'ın ifadesine aykırıdır Kapsam yaratmanın tek yolu işlevler, sınıflar veya modüllerdir çünkü burası yeni bir kapsam yaratmanın başka bir yeri gibi görünüyor.


0

Modüller (ve paketler), programınızı ayrı ad alanlarına bölmenin harika bir Pythonic yoludur ve bu, bu sorunun örtük bir amacı gibi görünüyor. Aslında, Python'un temellerini öğrenirken, bir blok kapsam özelliğinin olmaması beni hayal kırıklığına uğrattı. Ancak Python modüllerini anladıktan sonra, blok kapsamına ihtiyaç duymadan önceki hedeflerimi daha zarif bir şekilde gerçekleştirebildim.

Motivasyon olarak ve insanları doğru yöne yönlendirmek için Python'un kapsam belirleme yapılarının bazılarının açık örneklerini vermenin faydalı olacağını düşünüyorum. Öncelikle, blok kapsamını uygulamak için Python sınıflarını kullanma konusundaki başarısız girişimimi açıklayacağım. Daha sonra Python modüllerini kullanarak nasıl daha faydalı bir şey başardığımı açıklayacağım. Sonunda, verilerin yüklenmesi ve filtrelenmesi için pratik bir paket uygulamasını ana hatlarıyla anlatıyorum.

Sınıflarla blok kapsamı deneniyor

Birkaç dakika boyunca bir sınıf bildiriminin içine kod yapıştırarak blok kapsamı elde ettiğimi düşündüm:

x = 5
class BlockScopeAttempt:
    x = 10
    print(x) # Output: 10
print(x) # Output: 5

Ne yazık ki bu, bir işlev tanımlandığında bozulur:

x = 5 
class BlockScopeAttempt: 
    x = 10
    print(x) # Output: 10
    def printx2(): 
        print(x) 
    printx2() # Output: 5!!!

Bunun nedeni, bir sınıf içinde tanımlanan işlevlerin genel kapsam kullanmasıdır. Bunu düzeltmenin en kolay yolu (tek olmasa da) sınıfı açıkça belirtmektir:

x = 5 
class BlockScopeAttempt: 
    x = 10
    print(x) # Output: 10
    def printx2(): 
        print(BlockScopeAttempt.x)  # Added class name
    printx2() # Output: 10

Bu o kadar zarif değil çünkü bir sınıfta yer alıp almadıklarına bağlı olarak işlevleri farklı yazmalı.

Python modülleri ile daha iyi sonuçlar

Modüller statik sınıflara çok benzer, ancak modüller benim deneyimime göre çok daha temiz. Modüllerde de aynısını yapmak my_module.pyiçin şu anki çalışma dizininde şu içeriğe sahip bir dosya oluşturuyorum :

x = 10
print(x) # (A)

def printx():
    global x
    print(x) # (B)

Daha sonra ana dosyamda veya etkileşimli (örneğin Jupyter) oturumumda,

x = 5
import my_module # Output: 10 from (A)
my_module.printx() # Output: 10 from (B)
print(x) # Output: 5

Açıklama olarak, her Python dosyası kendi global ad alanına sahip bir modülü tanımlar. Bir modülü içe aktarmak, bu ad alanındaki değişkenlere .sözdizimi ile erişmenizi sağlar .

Modüllerle etkileşimli bir oturumda çalışıyorsanız, bu iki satırı başlangıçta çalıştırabilirsiniz.

%load_ext autoreload
%autoreload 2

ve modüller, karşılık gelen dosyaları değiştirildiğinde otomatik olarak yeniden yüklenecektir.

Veri yükleme ve filtreleme paketleri

Paket fikri, modül konseptinin küçük bir uzantısıdır. Paket, __init__.pyiçe aktarıldığında çalıştırılan (muhtemelen boş) bir dosya içeren bir dizindir . Bu dizindeki modüller / paketlere .sözdizimi ile erişilebilir .

Veri analizi için genellikle büyük bir veri dosyası okumam ve ardından etkileşimli olarak çeşitli filtreler uygulamam gerekir. Bir dosyayı okumak birkaç dakika sürüyor, bu yüzden sadece bir kez yapmak istiyorum. Okulda nesne yönelimli programlama hakkında öğrendiklerime dayanarak, sınıfta yöntem olarak filtreleme ve yükleme için kod yazılması gerektiğine inanırdım. Bu yaklaşımın önemli bir dezavantajı, filtrelerimi yeniden tanımlarsam, sınıfımın tanımı değişir, bu nedenle veriler dahil tüm sınıfı yeniden yüklemem gerekir.

Günümüzde Python ile, ve my_dataadlı alt modülleri içeren bir paket tanımlıyorum . İçinde göreceli bir içe aktarma yapabilirim:loadfilterfilter.py

from .load import raw_data

Değiştirirsem filter.py, autoreloaddeğişiklikleri algılar. Yeniden load.pyyüklenmediğinden verilerimi yeniden yüklememe gerek yok. Bu şekilde filtreleme kodumun prototipini bir Jupyter not defterinde oluşturabilir, onu bir işlev olarak paketleyebilir ve ardından not defterimden doğrudan filter.py. Bunu anlamak iş akışımda devrim yarattı ve beni bir şüpheciden "Python'un Zenine" inanan bir kişiye dönüştürdü.

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.