ขอบเขตการบล็อกใน Python


94

เมื่อคุณเขียนโค้ดเป็นภาษาอื่นบางครั้งคุณจะสร้างขอบเขตการบล็อกเช่นนี้:

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

จุดประสงค์หนึ่ง (จากหลาย ๆ อย่าง) คือเพื่อปรับปรุงความสามารถในการอ่านโค้ด: เพื่อแสดงว่าข้อความบางคำสั่งเป็นหน่วยตรรกะหรือตัวแปรโลคัลบางตัวถูกใช้เฉพาะในบล็อกนั้น

มีวิธีสำนวนในการทำสิ่งเดียวกันใน Python หรือไม่?


2
One purpose (of many) is to improve code readability- รหัส Python ที่เขียนอย่างถูกต้อง (เช่นตามzen ของ python ) ไม่จำเป็นต้องมีการปรุงแต่งเพื่อให้อ่านได้ อันที่จริงมันเป็นหนึ่งในหลาย ๆ สิ่งที่ฉันชอบเกี่ยวกับ Python
Burhan Khalid

ฉันได้พยายามที่จะเล่นกับ__exit__และwithคำสั่งเปลี่ยนglobals()แต่ฉันล้มเหลว
Ruggero Turra

1
มันจะมีประโยชน์มากในการกำหนดอายุการใช้งานของตัวแปรที่เชื่อมต่อกับการได้มาซึ่งทรัพยากร
Ruggero Turra

25
@BurhanKhalid: นั่นไม่เป็นความจริง zen of Python ไม่ได้ป้องกันไม่ให้คุณสร้างมลพิษในขอบเขตท้องถิ่นด้วยตัวแปรชั่วคราวที่นี่และที่นั่น หากคุณเปลี่ยนทุกการใช้งานของตัวแปรชั่วคราวตัวเดียวให้เป็นเช่นการกำหนดฟังก์ชันซ้อนกันซึ่งถูกเรียกใช้ทันที zen ของ Python ก็จะไม่พอใจเช่นกัน การ จำกัด ขอบเขตของตัวแปรอย่างชัดเจนเป็นเครื่องมือในการปรับปรุงความสามารถในการอ่านเพราะตอบได้โดยตรง "ตัวระบุเหล่านี้ถูกใช้ด้านล่างหรือไม่" - คำถามที่อาจเกิดขึ้นจากการอ่านแม้แต่รหัส Python ที่หรูหราที่สุด
bluenote10

18
@BurhanKhalid ไม่เป็นไรที่ไม่มีฟีเจอร์ แต่การเรียกสิ่งนั้นว่า "เซน" ก็น่ารังเกียจ
ฟิลิป

คำตอบ:


83

ไม่ไม่มีการรองรับภาษาสำหรับการสร้างขอบเขตการบล็อก

โครงสร้างต่อไปนี้สร้างขอบเขต:

  • โมดูล
  • ชั้นเรียน
  • ฟังก์ชัน (รวมแลมบ์ดา)
  • นิพจน์เครื่องกำเนิดไฟฟ้า
  • ความเข้าใจ (dict, set, list (ใน Python 3.x))

38

วิธีสำนวนใน Python คือการทำให้ฟังก์ชันของคุณสั้น หากคุณคิดว่าคุณต้องการสิ่งนี้ให้ refactor รหัสของคุณ! :)

Python สร้างขอบเขตใหม่สำหรับแต่ละโมดูลคลาสฟังก์ชันการแสดงออกของตัวสร้างความเข้าใจตามคำสั่งตั้งค่าความเข้าใจและใน Python 3.x สำหรับความเข้าใจแต่ละรายการ นอกเหนือจากนี้ไม่มีขอบเขตที่ซ้อนกันภายในฟังก์ชัน


12
"สิ่งที่สำคัญที่สุดในการเขียนโปรแกรมคือความสามารถในการตั้งชื่อสิ่งที่สำคัญรองลงมาคือไม่จำเป็นต้องตั้งชื่ออะไรสักอย่าง" โดยส่วนใหญ่ Python ต้องการให้กำหนดขอบเขต (สำหรับตัวแปร ฯลฯ ) ในแง่นี้ตัวแปร Python เป็นการทดสอบที่สำคัญอันดับสอง
Krazy Glew

19
สิ่งที่สำคัญที่สุดในการเขียนโปรแกรมคือความสามารถในการจัดการการอ้างอิงของแอปพลิเคชันของคุณและจัดการขอบเขตของบล็อกโค้ด การบล็อกแบบไม่ระบุตัวตนช่วยให้คุณสามารถ จำกัด อายุการใช้งานการโทรกลับได้ในขณะที่มิฉะนั้นการโทรกลับของคุณจะถูกใช้เพียงครั้งเดียว แต่จะใช้งานได้ตลอดระยะเวลาของโปรแกรมซึ่งทำให้เกิดความยุ่งเหยิงของขอบเขตทั่วโลกและเป็นอันตรายต่อการอ่านโค้ด
Dmitry

ฉันเพิ่งสังเกตว่าตัวแปรต่างๆก็เป็นตัวแปรในท้องถิ่นที่จะเขียนตามคำบอก / ตั้งค่าความเข้าใจ ฉันลอง Python 2.7 และ 3.3 แล้ว แต่ไม่แน่ใจว่ามันขึ้นอยู่กับเวอร์ชันหรือไม่
wjandrea

1
@wjandrea คุณพูดถูก - เพิ่มในรายการ ไม่ควรมีความแตกต่างระหว่างเวอร์ชัน Python สำหรับสิ่งเหล่านี้
Sven Marnach

4
ฉันจะพูดประโยคสุดท้ายใหม่เนื่องจากคุณสามารถสร้างฟังก์ชันภายในฟังก์ชันได้เป็นอย่างดี ดังนั้นจึงมีขอบเขตที่ซ้อนกันอยู่ภายในฟังก์ชัน
ThomasH

19

คุณสามารถทำสิ่งที่คล้ายกับขอบเขตบล็อก C ++ ใน Python ได้โดยการประกาศฟังก์ชันภายในฟังก์ชันของคุณแล้วเรียกใช้ทันที ตัวอย่างเช่น:

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()

หากคุณไม่แน่ใจว่าทำไมคุณถึงต้องการทำเช่นนี้วิดีโอนี้อาจโน้มน้าวคุณได้

หลักการพื้นฐานคือกำหนดขอบเขตทุกอย่างให้แน่นที่สุดโดยไม่แนะนำ 'ขยะ' (ประเภท / ฟังก์ชันพิเศษ) ใด ๆ ให้อยู่ในขอบเขตที่กว้างกว่าที่จำเป็นอย่างยิ่ง - ไม่มีสิ่งอื่นใดที่ต้องการใช้do_first_thing()วิธีการนี้เช่นดังนั้นจึงไม่ควรกำหนดขอบเขตภายนอก ฟังก์ชั่นการโทร


นอกจากนี้ยังเป็นวิธีที่นักพัฒนาซอฟต์แวร์ของ Google ใช้ในบทช่วยสอน TensorFlow ดังที่เห็นตัวอย่างที่นี่
Nino Filiu

13

ฉันยอมรับว่าไม่มีขอบเขตการปิดกั้น แต่ที่หนึ่งใน python 3 ทำให้มันดูเหมือนว่ามีขอบเขตการบล็อก

เกิดอะไรขึ้นที่ทำให้รูปลักษณ์นี้? สิ่งนี้ทำงานได้อย่างถูกต้องใน python 2 แต่เพื่อให้ตัวแปรหยุดการรั่วไหลใน python 3 พวกเขาได้ทำเคล็ดลับนี้แล้วและการเปลี่ยนแปลงนี้ทำให้ดูเหมือนว่ามีขอบเขตบล็อกอยู่ที่นี่

ให้ฉันอธิบาย


ตามแนวคิดของขอบเขตเมื่อเราแนะนำตัวแปรที่มีชื่อเดียวกันภายในขอบเขตเดียวกันควรแก้ไขค่า

นี่คือสิ่งที่เกิดขึ้นใน python 2

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

แต่ใน python 3 แม้ว่าจะมีการนำตัวแปรที่มีชื่อเดียวกันมาใช้ แต่ก็ไม่ได้ลบล้างความเข้าใจของรายการจะทำหน้าที่เหมือนแซนด์บ็อกซ์ด้วยเหตุผลบางประการและดูเหมือนว่าจะสร้างขอบเขตใหม่ในนั้น

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

และคำตอบนี้ขัดแย้งกับคำสั่งของผู้ตอบ @ Thomas วิธีเดียวในการสร้างขอบเขตคือฟังก์ชันคลาสหรือโมดูลเพราะดูเหมือนว่าจะสร้างขอบเขตใหม่อีกแห่งหนึ่ง


0

โมดูล (และแพ็คเกจ) เป็นวิธีที่ยอดเยี่ยมของ Pythonic ในการแบ่งโปรแกรมของคุณออกเป็นเนมสเปซแยกกันซึ่งดูเหมือนว่าจะเป็นเป้าหมายโดยปริยายของคำถามนี้ อันที่จริงขณะที่ฉันเรียนรู้พื้นฐานของ Python ฉันรู้สึกผิดหวังที่ไม่มีคุณสมบัติขอบเขตบล็อก อย่างไรก็ตามเมื่อฉันเข้าใจโมดูล Python แล้วฉันก็สามารถบรรลุเป้าหมายก่อนหน้านี้ได้อย่างสง่างามมากขึ้นโดยไม่ต้องใช้ขอบเขตการบล็อก

เพื่อเป็นแรงจูงใจและเพื่อชี้ให้ผู้คนไปสู่ทิศทางที่ถูกต้องฉันคิดว่าการให้ตัวอย่างที่ชัดเจนของโครงสร้างขอบเขตของ Python นั้นมีประโยชน์ ก่อนอื่นฉันอธิบายความพยายามที่ล้มเหลวในการใช้คลาส Python เพื่อใช้ขอบเขตบล็อก ต่อไปฉันจะอธิบายว่าฉันบรรลุสิ่งที่มีประโยชน์มากขึ้นโดยใช้โมดูล Python ได้อย่างไร ในตอนท้ายฉันร่างการประยุกต์ใช้แพคเกจในการโหลดและกรองข้อมูล

กำลังพยายามบล็อกขอบเขตกับคลาส

ครู่หนึ่งฉันคิดว่าฉันได้บรรลุขอบเขตการบล็อกโดยการติดรหัสไว้ในการประกาศคลาส:

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

น่าเสียดายที่สิ่งนี้พังลงเมื่อมีการกำหนดฟังก์ชัน:

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

นั่นเป็นเพราะฟังก์ชันที่กำหนดภายในคลาสใช้ขอบเขตส่วนกลาง วิธีที่ง่ายที่สุด (แต่ไม่ใช่วิธีเดียว) ในการแก้ไขปัญหานี้คือการระบุคลาสอย่างชัดเจน:

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

สิ่งนี้ไม่สวยหรูนักเพราะต้องเขียนฟังก์ชันต่างกันขึ้นอยู่กับว่ามีอยู่ในชั้นเรียนหรือไม่

ผลลัพธ์ที่ดีขึ้นด้วยโมดูล Python

โมดูลนั้นคล้ายกับคลาสแบบคงที่มาก แต่โมดูลนั้นสะอาดกว่ามากจากประสบการณ์ของฉัน ในการทำเช่นเดียวกันกับโมดูลฉันสร้างไฟล์ที่เรียกว่าmy_module.pyในไดเร็กทอรีการทำงานปัจจุบันโดยมีเนื้อหาต่อไปนี้:

x = 10
print(x) # (A)

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

จากนั้นในไฟล์หลักของฉันหรือเซสชันโต้ตอบ (เช่น Jupyter) ฉันทำ

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

ตามคำอธิบายไฟล์ Python แต่ละไฟล์กำหนดโมดูลที่มีเนมสเปซส่วนกลางของตัวเอง การนำเข้าโมดูลช่วยให้คุณสามารถเข้าถึงตัวแปรในเนมสเปซนี้ด้วย.ไวยากรณ์

หากคุณกำลังทำงานกับโมดูลในเซสชันแบบโต้ตอบคุณสามารถดำเนินการสองบรรทัดนี้ได้ในตอนต้น

%load_ext autoreload
%autoreload 2

และโมดูลจะโหลดใหม่โดยอัตโนมัติเมื่อไฟล์ที่เกี่ยวข้องถูกแก้ไข

แพ็กเกจสำหรับโหลดและกรองข้อมูล

แนวคิดเรื่องแพ็คเกจเป็นส่วนขยายเล็กน้อยของแนวคิดโมดูล แพ็กเกจคือไดเร็กทอรีที่มีไฟล์ (อาจว่างเปล่า) __init__.pyซึ่งดำเนินการเมื่อนำเข้า โมดูล / แพ็คเกจภายในไดเร็กทอรีนี้สามารถเข้าถึงได้ด้วย.ไวยากรณ์

สำหรับการวิเคราะห์ข้อมูลฉันมักจะต้องอ่านไฟล์ข้อมูลขนาดใหญ่จากนั้นจึงใช้ตัวกรองต่างๆแบบโต้ตอบ การอ่านไฟล์ใช้เวลาหลายนาทีดังนั้นฉันจึงต้องการทำเพียงครั้งเดียว จากสิ่งที่ฉันเรียนรู้ในโรงเรียนเกี่ยวกับการเขียนโปรแกรมเชิงวัตถุฉันเคยเชื่อว่าควรเขียนโค้ดสำหรับกรองและโหลดเป็นวิธีการในชั้นเรียน ข้อเสียที่สำคัญของแนวทางนี้คือถ้าฉันกำหนดตัวกรองของฉันใหม่คำจำกัดความของคลาสของฉันจะเปลี่ยนไปดังนั้นฉันจึงต้องโหลดคลาสใหม่ทั้งหมดรวมถึงข้อมูลด้วย

ทุกวันนี้ด้วย Python ฉันกำหนดแพ็คเกจที่เรียกว่าmy_dataซึ่งมีโมดูลย่อยชื่อloadและfilter. ภายในของfilter.pyฉันสามารถนำเข้าสัมพัทธ์:

from .load import raw_data

ถ้าฉันแก้ไขfilter.pyก็autoreloadจะตรวจพบการเปลี่ยนแปลง มันไม่โหลดซ้ำload.pyดังนั้นฉันไม่จำเป็นต้องโหลดข้อมูลของฉันใหม่ วิธีนี้ฉันสามารถสร้างต้นแบบรหัสกรองของฉันในโน๊ตบุ๊ค Jupyter filter.pyห่อเป็นฟังก์ชั่นและจากนั้นตัดวางจากสมุดบันทึกของฉันโดยตรงใน การคิดว่านี่เป็นการปฏิวัติขั้นตอนการทำงานของฉันและทำให้ฉันเปลี่ยนจากคนขี้ระแวงเป็นผู้เชื่อใน“ Zen of Python”

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.