ทำไมตัวแปรระดับโลกถึงชั่วร้าย? [ปิด]


122

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


การโหวตเพื่อเปิดอีกครั้ง - ฉันแก้ไขคำถามเพื่อเปลี่ยนโฟกัสไปที่คำอธิบายและอยู่ห่างจากแหล่งข้อมูลนอกสถานที่ (ผมคิดว่าทำไมมันถูกปิด แต่เพียงในกรณีที่เป็นสิ่งที่จะทำอย่างไรกับการตั้งคำถามเกี่ยวกับการปฏิบัติที่ไม่ดีให้เปรียบเทียบคำถามเหล่านี้อื่น ๆ เกี่ยวกับการปฏิบัติที่ไม่ดีซึ่งยังคงเปิดให้บริการ: eval, import *, concatenation สตริง , ตัวแปรid , เงาแอตทริบิวต์ )
wjandrea

คำตอบ:


157

สิ่งนี้ไม่เกี่ยวข้องกับ Python ตัวแปรส่วนกลางไม่ถูกต้องในภาษาโปรแกรมใด ๆ

อย่างไรก็ตามค่าคงที่ทั่วโลกไม่ได้เป็นแนวคิดเดียวกับตัวแปรทั่วโลก ; ค่าคงที่ทั่วโลกไม่เป็นอันตรายอย่างสมบูรณ์ ใน Python ความแตกต่างระหว่างทั้งสองนั้นเป็นไปตามแบบแผน: CONSTANTS_ARE_CAPITALIZEDและglobals_are_not.

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

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

สรุปแล้วคำถามของคุณสามารถตอบได้หลายวิธีดังนั้นทางออกที่ดีที่สุดของคุณคือ Google "ทำไมตัวแปรทั่วโลกถึงไม่ดี" ตัวอย่างบางส่วน:

หากคุณต้องการเจาะลึกและค้นหาว่าเหตุใดผลข้างเคียงจึงเป็นเรื่องเกี่ยวกับและสิ่งที่ทำให้กระจ่างอื่น ๆ อีกมากมายคุณควรเรียนรู้ Functional Programming:


35

ใช่ตามทฤษฎีแล้วโลก (และ "รัฐ" โดยทั่วไป) เป็นสิ่งชั่วร้าย ในทางปฏิบัติหากคุณดูไดเร็กทอรีแพ็กเกจของ python คุณจะพบว่าโมดูลส่วนใหญ่ในนั้นเริ่มต้นด้วยการประกาศทั่วโลกจำนวนมาก เห็นได้ชัดว่าผู้คนไม่มีปัญหากับพวกเขา

โดยเฉพาะอย่างยิ่งสำหรับ python การมองเห็นของ globals จะ จำกัด อยู่ที่โมดูลดังนั้นจึงไม่มี globals "จริง" ที่ส่งผลกระทบต่อโปรแกรมทั้งหมดซึ่งทำให้เป็นอันตรายน้อยลง อีกประเด็นหนึ่ง: ไม่มีconstดังนั้นเมื่อคุณต้องการค่าคงที่คุณต้องใช้ global

ในทางปฏิบัติของฉันหากฉันเกิดการแก้ไข global ในฟังก์ชันฉันมักจะประกาศด้วยglobalแม้ว่าในทางเทคนิคจะไม่จำเป็นก็ตามเช่นใน:

cache = {}

def foo(args):
    global cache

    cache[args] = ...

สิ่งนี้ทำให้การจัดการของลูกโลกง่ายขึ้นในการติดตาม


3
ในหลาย ๆ ด้านโมดูลใน python คล้ายกับคลาสซิงเกิลตันและโมดูลโกลบัลก็คล้ายกับคุณสมบัติคลาส
Corley Brigman

9
@CorleyBrigman: ชั้นเรียนเดี่ยวมักประสบปัญหาเดียวกันโดยทั่วไปเกิดจากโลก :)
Erik Kaplun

4
การมองเห็นโมดูล python ไม่ จำกัด เฉพาะโมดูล มีอยู่ในล่ามทั้งหมดและ (นี่คือสิ่งสำคัญ) การเปลี่ยนแปลงที่มีผลต่อล่ามทั้งหมด มันไม่เหมือนกับสตริงที่คุณสร้างอินสแตนซ์ ... เหมือนกับการแก้ไขอินสแตนซ์สตริงทั้งหมด กลิ่นลิงปะ.
ชม

2
โมดูลส่วนใหญ่ไม่ได้เริ่มต้นด้วยการกำหนด globals ยกเว้นค่าคงที่ Globals เป็นตัวแปรหมายถึงไม่ดี/ สภาวะโลกไม่ใช่ค่าคงที่
BlackJack

2
การใช้ลูกโลกเป็นความคิดที่น่าสยดสยองสาเหตุหนึ่งอาจเป็นเพราะไม่สามารถทดสอบฟังก์ชันที่อัปเดตพจนานุกรมตามอำเภอใจที่มีอยู่ "ที่ไหนสักแห่ง" ได้อย่างเหมาะสม codebase ที่มี globals ไม่สามารถพิสูจน์ได้ว่าใช้งานได้จริง
Tomasz Sosiński

10

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

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

(น่าจะ) ละเมิดนิยามฟังก์ชันบริสุทธิ์

ฉันเชื่อว่าโค้ดที่สะอาดและ (เกือบ) ปราศจากบั๊กควรมีฟังก์ชันที่บริสุทธิ์ที่สุดเท่าที่จะเป็นไปได้ (ดูฟังก์ชันที่บริสุทธิ์ ) ฟังก์ชันบริสุทธิ์คือฟังก์ชันที่มีเงื่อนไขดังต่อไปนี้:

  1. ฟังก์ชั่นมักจะประเมินค่าผลเดียวกันได้รับค่าอาร์กิวเมนต์เดียวกัน (s) ค่าผลลัพธ์ของฟังก์ชันไม่สามารถขึ้นอยู่กับข้อมูลหรือสถานะที่ซ่อนอยู่ซึ่งอาจเปลี่ยนแปลงได้ในขณะที่การดำเนินการของโปรแกรมดำเนินไปหรือระหว่างการดำเนินการต่าง ๆ ของโปรแกรมและไม่สามารถขึ้นอยู่กับอินพุตภายนอกจากอุปกรณ์ I / O ได้ (โดยปกติจะดูด้านล่าง)
  2. การประเมินผลลัพธ์ไม่ก่อให้เกิดผลข้างเคียงหรือผลลัพธ์ที่สังเกตได้ทางความหมายเช่นการกลายพันธุ์ของวัตถุที่เปลี่ยนแปลงไม่ได้หรือเอาต์พุตไปยังอุปกรณ์ I / O

การมีตัวแปรส่วนกลางกำลังละเมิดอย่างน้อยหนึ่งในข้างต้นหากไม่ใช่ทั้งสองอย่างเป็นรหัสภายนอกอาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด

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

(อาจ) ละเมิดหลักการทดสอบหน่วยแรก

เพิ่มเติมเกี่ยวกับว่าถ้าคุณพิจารณาหน่วยทดสอบและหลักการแรก ( Fทดสอบ AST, ผมทดสอบ ndependent, R epeatable, Sเอลฟ์การตรวจสอบและT imely) อาจจะละเมิดหลักการทดสอบอิสระ (ซึ่งหมายถึงว่าการทดสอบไม่ได้ขึ้นอยู่ ซึ่งกันและกัน).

การมีตัวแปรส่วนกลาง (ไม่เสมอไป) แต่ในกรณีส่วนใหญ่ (อย่างน้อยที่สุดก็คือการเตรียมและส่งผ่านผลลัพธ์ไปยังฟังก์ชันอื่น ๆ สิ่งนี้ละเมิดหลักการนี้เช่นกัน หากตัวแปรโกลบอลถูกใช้ในลักษณะนั้น (เช่นตัวแปรโกลบอลที่ใช้ในฟังก์ชัน X จะต้องถูกตั้งค่าในฟังก์ชัน Y ก่อน) หมายความว่าในการทดสอบหน่วยฟังก์ชัน X คุณต้องเรียกใช้ฟังก์ชันทดสอบ / รัน Y ก่อน

ลูกโลกเป็นค่าคงที่

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

ฉันไม่เชื่อว่าไม่ควรใช้ globals แต่หากมีการใช้งานผู้เขียนควรพิจารณาหลักการบางประการ (หลักการที่กล่าวถึงข้างต้นอาจรวมถึงหลักการทางวิศวกรรมซอฟต์แวร์และแนวทางปฏิบัติที่ดีอื่น ๆ ) เพื่อรหัสที่สะอาดและเกือบจะปราศจากข้อบกพร่อง


1
ฉันชอบ "globals เนื่องจากค่าคงที่เป็นปัญหา" ... เพราะถ้าคุณกำลังออกแบบ OO ... มันเป็นเช่นนั้นจริงๆ ทำไมทุกคนยกเว้นคลาส IdCreator จึงจำเป็นต้องรู้ ID_LEN
Erik Aronesty

3

สิ่งเหล่านี้มีความสำคัญหน้าจอเป็นตัวอย่างที่ดี อย่างไรก็ตามในสภาพแวดล้อมแบบมัลติเธรดหรือกับนักพัฒนาหลายคนที่เกี่ยวข้องในทางปฏิบัติมักจะมีคำถามเกิดขึ้น: ใครเป็นคนกำหนดหรือล้างมัน (ผิดพลาด)? ขึ้นอยู่กับสถาปัตยกรรมการวิเคราะห์อาจมีค่าใช้จ่ายสูงและจำเป็นต้องใช้บ่อยครั้ง ในขณะที่การอ่าน global var สามารถใช้ได้ แต่การเขียนถึงจะต้องได้รับการควบคุมเช่นโดย single thread หรือ threadsafe class ดังนั้นตัวแทนทั่วโลกจึงเกิดความกลัวว่าจะมีค่าใช้จ่ายในการพัฒนาที่สูงซึ่งเป็นไปได้จากผลที่ตัวเองถูกมองว่าชั่วร้าย ดังนั้นโดยทั่วไปจึงควรรักษาจำนวนตัวแทนทั่วโลกให้ต่ำ

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