Tại sao các biến toàn cục là xấu? [đóng cửa]


121

Tôi đang cố gắng tìm hiểu lý do tại sao việc sử dụng globalđược coi là hành vi xấu trong python (và trong lập trình nói chung). Ai đó có thể giải thích? Các liên kết với nhiều thông tin hơn cũng sẽ được đánh giá cao.


Bỏ phiếu để mở lại - Tôi đã chỉnh sửa câu hỏi để chuyển trọng tâm sang giải thích và tránh xa các tài nguyên bên ngoài trang web. (Tôi cho rằng lý do tại sao nó đã được đóng cửa, nhưng chỉ trong trường hợp đó là một cái gì đó để làm với được một câu hỏi về thực hành xấu, so sánh các câu hỏi khác về thực hành xấu mà vẫn còn mở: eval, import *, nối chuỗi , biếnid , bóng tối thuộc tính )
wjandrea

Câu trả lời:


156

Điều này không liên quan gì đến Python; biến toàn cục không tốt trong bất kỳ ngôn ngữ lập trình nào.

Tuy nhiên, về mặt khái niệm các hằng số toàn cục không giống như các biến toàn cục ; hằng số toàn cục hoàn toàn vô hại. Trong Python, sự khác biệt giữa cả hai hoàn toàn theo quy ước: CONSTANTS_ARE_CAPITALIZEDglobals_are_not.

Lý do các biến toàn cục không tốt là do chúng cho phép các hàm có các tác dụng phụ ẩn (không rõ ràng, đáng ngạc nhiên, khó phát hiện, khó chẩn đoán), dẫn đến sự gia tăng độ phức tạp, có khả năng dẫn đến mã Spaghetti .

Tuy nhiên, việc sử dụng thuần thục trạng thái toàn cục là có thể chấp nhận được (cũng như trạng thái cục bộ và khả năng thay đổi) ngay cả trong lập trình chức năng, để tối ưu hóa thuật toán, giảm độ phức tạp, lưu vào bộ nhớ đệm và ghi nhớ hoặc tính thực tế của các cấu trúc cổng có nguồn gốc chủ yếu là cơ sở mã bắt buộc.

Nói chung, câu hỏi của bạn có thể được trả lời theo nhiều cách, vì vậy cách tốt nhất của bạn là chỉ cần google "tại sao các biến toàn cục là xấu". Vài ví dụ:

Nếu bạn muốn đi sâu hơn và tìm hiểu lý do tại sao lại gây ra các tác dụng phụ và nhiều điều thú vị khác, bạn nên học Lập trình chức năng:


35

Vâng, về lý thuyết , các khối cầu (và "trạng thái" nói chung) là xấu. Trong thực tế, nếu bạn nhìn vào thư mục các gói của python, bạn sẽ thấy rằng hầu hết các mô-đun ở đó bắt đầu với một loạt các khai báo toàn cục. Rõ ràng, mọi người không có vấn đề gì với chúng.

Đặc biệt đối với python, khả năng hiển thị của hình cầu bị giới hạn trong một mô-đun, do đó không có hình cầu "thực sự" nào ảnh hưởng đến toàn bộ chương trình - điều đó làm cho chúng ít gây hại hơn. Một điểm khác: không có const, vì vậy khi bạn cần một hằng số, bạn phải sử dụng một toàn cục.

Trong thực tế của tôi, nếu tôi tình cờ sửa đổi một toàn cục trong một hàm, tôi luôn khai báo nó với global, ngay cả khi về mặt kỹ thuật không cần điều đó, như trong:

cache = {}

def foo(args):
    global cache

    cache[args] = ...

Điều này làm cho các thao tác của quả cầu dễ dàng theo dõi hơn.


3
theo nhiều cách, một mô-đun trong python tương tự như một lớp singleton và các khối cầu mô-đun tương tự như các thuộc tính của lớp.
Corley Brigman

9
@CorleyBrigman: lớp singleton thực sự thường bị những vấn đề tương tự thường do globals :)
Erik Kaplun

4
khả năng hiển thị của mô-đun python không bị giới hạn ở một mô-đun. Chúng có sẵn trong toàn bộ trình thông dịch và (đây là điều quan trọng) những thay đổi ở đó ảnh hưởng đến toàn bộ trình thông dịch. Nó không giống như một chuỗi mà bạn tạo các cá thể ... giống như việc sửa đổi tất cả các cá thể chuỗi. Khỉ vá mùi.
graffic

2
Hầu hết các mô-đun không bắt đầu với việc xác định các khối cầu ngoại trừ các hằng số. Toàn cầu là biến có nghĩa là xấu /Toàn cầu trạng thái toàn cục không phải là hằng số.
BlackJack,

2
Sử dụng toàn cầu là một ý tưởng kinh khủng, một lý do có thể là không có khả năng kiểm tra đúng các chức năng cập nhật một số từ điển tùy ý tồn tại "ở đâu đó". Một cơ sở mã có hình cầu không thể thực sự được chứng minh là hoạt động.
Tomasz Sosiński

10

Ý kiến ​​cá nhân về chủ đề này là việc có các biến toàn cục được sử dụng trong một hàm logic có nghĩa là một số mã khác có thể thay đổi logic và kết quả đầu ra mong đợi của hàm đó, điều này sẽ làm cho việc gỡ lỗi rất khó khăn (đặc biệt là trong các dự án lớn) và sẽ khiến việc kiểm tra khó hơn cũng.

Hơn nữa, nếu bạn cho rằng những người khác đang đọc mã của bạn (cộng đồng nguồn mở, đồng nghiệp, v.v.) thì họ sẽ gặp khó khăn khi cố gắng hiểu biến toàn cục đang được đặt ở đâu, đã được thay đổi ở đâu và điều gì sẽ xảy ra từ biến toàn cục này. đến một hàm riêng biệt mà chức năng của nó có thể được xác định bằng cách đọc chính định nghĩa hàm.

(Có thể là) Vi phạm định nghĩa Hàm thuần túy

Tôi tin rằng một mã sạch và (gần như) không có lỗi phải có các chức năng càng thuần càng tốt (xem các chức năng thuần túy ). Một hàm thuần túy là một hàm có các điều kiện sau:

  1. Hàm luôn đánh giá cùng một giá trị kết quả với cùng (các) giá trị đối số . Giá trị kết quả của hàm không thể phụ thuộc vào bất kỳ thông tin hoặc trạng thái ẩn nào có thể thay đổi trong khi tiến hành chương trình hoặc giữa các lần thực thi khác nhau của chương trình, cũng như không thể phụ thuộc vào bất kỳ đầu vào bên ngoài nào từ các thiết bị I / O (thường — xem bên dưới).
  2. Đánh giá kết quả không gây ra bất kỳ tác dụng phụ hoặc đầu ra có thể quan sát được về mặt ngữ nghĩa , chẳng hạn như đột biến của các đối tượng có thể thay đổi hoặc đầu ra cho các thiết bị I / O.

Việc có các biến toàn cục đang vi phạm ít nhất một trong những điều trên nếu không phải cả hai đều là mã bên ngoài có thể gây ra kết quả không mong muốn.

Một định nghĩa rõ ràng khác về hàm thuần túy: "Hàm thuần túy là hàm nhận tất cả các đầu vào của nó làm đối số rõ ràng và tạo ra tất cả các đầu ra của nó dưới dạng kết quả rõ ràng ." [1] . Việc có các biến toàn cục vi phạm ý tưởng về các hàm thuần túy vì một đầu vào và có thể một trong các đầu ra (biến toàn cục) không được đưa ra hoặc trả về một cách rõ ràng.

(Có thể) Vi phạm Nguyên tắc kiểm tra đơn vị đầu tiên

Hơn nữa, nếu bạn xem xét thử nghiệm đơn vị và nguyên tắc ĐẦU TIÊN ( thử nghiệm F ast, thử nghiệm độc lập I , R epeatable, S elf-Validating và T imely) có thể sẽ vi phạm nguyên tắc kiểm tra độc lập (có nghĩa là kiểm tra không phụ thuộc trên nhau).

Có một biến toàn cục (không phải luôn luôn) nhưng trong hầu hết các trường hợp (ít nhất là những gì tôi đã thấy cho đến nay) là để chuẩn bị và chuyển kết quả cho các hàm khác. Điều này cũng vi phạm nguyên tắc này. Nếu biến toàn cục đã được sử dụng theo cách đó (tức là biến toàn cục được sử dụng trong hàm X phải được đặt trong hàm Y trước) thì có nghĩa là để kiểm tra đơn vị hàm X, bạn phải chạy thử / chạy hàm Y trước.

Hình cầu dưới dạng hằng số

Mặt khác và như những người khác đã đề cập, nếu biến toàn cục được sử dụng như một biến "hằng số" có thể tốt hơn một chút vì ngôn ngữ không hỗ trợ hằng số. Tuy nhiên, tôi luôn thích làm việc với các lớp và có "hằng số" như một thành viên của lớp và hoàn toàn không sử dụng biến toàn cục. Nếu bạn có mã mà hai lớp khác nhau yêu cầu để chia sẻ một biến toàn cục thì bạn có thể cần phải cấu trúc lại giải pháp của mình và làm cho các lớp của bạn độc lập.

Tôi không tin rằng không nên sử dụng hình cầu. Nhưng nếu chúng được sử dụng, các tác giả nên xem xét một số nguyên tắc (có lẽ những nguyên tắc đã đề cập ở trên và các nguyên tắc kỹ thuật phần mềm và thực tiễn tốt khác) để có mã sạch hơn và gần như không có lỗi.


1
Tôi thích "hình cầu như hằng số là một vấn đề" ... bởi vì nếu bạn đang thiết kế OO ... thì nó thực sự là như vậy. Tại sao bất kỳ ai ngoài lớp IdCreator cần biết ID_LEN?
Erik Aronesty

3

Chúng rất cần thiết, màn hình là một ví dụ điển hình. Tuy nhiên, trong một môi trường đa luồng hoặc có nhiều nhà phát triển tham gia, trong thực tế câu hỏi thường đặt ra: ai đã (sai) đặt ra hoặc xóa nó? Tùy thuộc vào kiến ​​trúc, phân tích có thể tốn kém và được yêu cầu thường xuyên. Mặc dù việc đọc var toàn cục có thể được nhưng việc ghi vào nó phải được kiểm soát, ví dụ như bởi một luồng đơn hoặc lớp luồng an toàn. Do đó, các tín đồ toàn cầu nảy sinh nỗi sợ hãi về chi phí phát triển cao có thể gây ra bởi những hậu quả mà bản thân chúng bị coi là xấu xa. Do đó, nói chung, bạn nên giữ cho số lượng vars toàn cầu ở mức thấp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.