Hướng dẫn avoiding global variables python - tránh các biến toàn cục python

Thuật ngữ "Pythonic" không áp dụng cho chủ đề này-sử dụng các thế giới như thế này là thực tiễn kém trong bất kỳ ngôn ngữ lập trình và mô hình nào và không phải là một thứ gì đó cụ thể cho Python.

Từ khóa global là công cụ mà Python cung cấp cho bạn từ chối đóng gói và phá vỡ phạm vi tự nhiên của một biến. Đóng gói có nghĩa là mỗi thành phần của bạn là một đơn vị hợp lý, khép kín, nên hoạt động như một hộp đen và thực hiện một điều (lưu ý: điều này là khái niệm và có thể bao gồm nhiều bước, có thể không tầm thường) mà không có đột biến trạng thái toàn cầu hoặc tạo ra các tác dụng phụ. Lý do là mô -đun: nếu có sự cố xảy ra trong một chương trình (và nó sẽ), việc đóng gói mạnh mẽ giúp bạn rất dễ dàng xác định thành phần thất bại ở đâu.

Pacpsulsation làm cho mã dễ dàng hơn để tái cấu trúc, duy trì và mở rộng. Nếu bạn cần một thành phần để hoạt động khác nhau, sẽ dễ dàng loại bỏ nó hoặc điều chỉnh nó mà không có những sửa đổi này gây ra hiệu ứng domino của các thay đổi trên các thành phần khác trong hệ thống.

Các công cụ cơ bản để thực thi đóng gói bao gồm các lớp, chức năng, tham số và từ khóa return. Các ngôn ngữ thường cung cấp các mô-đun, không gian tên và đóng cửa cho hiệu ứng tương tự, nhưng mục tiêu cuối cùng là luôn giới hạn phạm vi và cho phép lập trình viên tạo ra các trừu tượng được kết hợp lỏng lẻo.

Các chức năng lấy đầu vào thông qua các tham số và tạo đầu ra thông qua các giá trị trả về. Bạn có thể gán giá trị trả về cho các biến trong phạm vi gọi. Bạn có thể nghĩ về các tham số là "núm" điều chỉnh hành vi của chức năng. Bên trong hàm, các biến chỉ là lưu trữ tạm thời được sử dụng bởi hàm cần thiết để tạo ra một giá trị trả về của nó sau đó biến mất.

Lý tưởng nhất, các chức năng được viết là tinh khiết và idempotent; Đó là, họ không sửa đổi trạng thái toàn cầu và tạo ra kết quả tương tự khi được gọi là nhiều lần. Python ít nghiêm ngặt hơn về điều này so với các ngôn ngữ khác và việc sử dụng các chức năng tại chỗ nhất định như sortrandom.shuffle là điều tự nhiên. Đây là những trường hợp ngoại lệ chứng minh quy tắc (và nếu bạn biết một chút về việc sắp xếp và xáo trộn, chúng có ý nghĩa trong các bối cảnh này do các thuật toán được sử dụng và nhu cầu về hiệu quả).

Một thuật toán tại chỗ không tinh khiết và không có ý kiến, nhưng nếu trạng thái mà nó sửa đổi bị giới hạn trong (các) tham số của nó và tài liệu và giá trị trả về của nó (thường là ____7) hỗ trợ điều này, hành vi này có thể dự đoán được và dễ hiểu.

Vậy tất cả những điều này trông như thế nào trong mã? Thật không may, ví dụ của bạn dường như bị giả mạo và không rõ ràng về mục đích/mục tiêu của nó, vì vậy không có cách nào trực tiếp để biến đổi nó làm cho những lợi thế của đóng gói rõ ràng.

Dưới đây là danh sách một số vấn đề trong các chức năng này ngoài việc sửa đổi trạng thái toàn cầu:

  • Sử dụng các chữ "yes""no" theo nghĩa đen thay vì ____ 10/________ 11 giá trị boolean.
  • Các giá trị mã hóa cứng trong các chức năng, làm cho chúng hoàn toàn một mục đích đơn lẻ (chúng cũng có thể được đưa vào).
  • def fmt_higher(name, n, cutoff=12):
        verb = "is" if n > cutoff else "isn't"
        return f"{name} {verb} higher than {cutoff}"
    
    if __name__ == "__main__":
        print(fmt_higher("alpha", 42))
        print(fmt_higher("beta", 6))
        print(fmt_higher("epsilon", 0))
        print(fmt_higher(name="delta", n=2, cutoff=-5))
    
    2ing trong các chức năng (xem các tác dụng phụ Ghi chú ở trên-yêu cầu trả về các giá trị và để phạm vi gọi điện in nếu chúng muốn làm như vậy).
  • Các tên biến chung như
    def fmt_higher(name, n, cutoff=12):
        verb = "is" if n > cutoff else "isn't"
        return f"{name} {verb} higher than {cutoff}"
    
    if __name__ == "__main__":
        print(fmt_higher("alpha", 42))
        print(fmt_higher("beta", 6))
        print(fmt_higher("epsilon", 0))
        print(fmt_higher(name="delta", n=2, cutoff=-5))
    
    3 (Tôi cho rằng điều này tương đương với ____ 14/________ 15 cho ví dụ, nhưng nó vẫn không biện minh cho lý do tồn tại của chúng, gây khó khăn cho việc sửa đổi làm ví dụ sư phạm).

Nhưng dù sao đây cũng là bức ảnh của tôi:

if __name__ == "__main__":
    alpha = 42
    beta = 6
    print("alpha %s higher than 12" % ("is" if alpha > 12 else "isn't"))
    print("beta %s higher than 12" % ("is" if beta > 12 else "isn't"))

Chúng ta có thể thấy không cần tất cả các chức năng-chỉ cần viết

def fmt_higher(name, n, cutoff=12):
    verb = "is" if n > cutoff else "isn't"
    return f"{name} {verb} higher than {cutoff}"

if __name__ == "__main__":
    print(fmt_higher("alpha", 42))
    print(fmt_higher("beta", 6))
    print(fmt_higher("epsilon", 0))
    print(fmt_higher(name="delta", n=2, cutoff=-5))
6 bất cứ nơi nào bạn cần để so sánh và gọi
def fmt_higher(name, n, cutoff=12):
    verb = "is" if n > cutoff else "isn't"
    return f"{name} {verb} higher than {cutoff}"

if __name__ == "__main__":
    print(fmt_higher("alpha", 42))
    print(fmt_higher("beta", 6))
    print(fmt_higher("epsilon", 0))
    print(fmt_higher(name="delta", n=2, cutoff=-5))
2 khi bạn cần in. Một nhược điểm của các chức năng là chúng có thể phục vụ để ẩn logic quan trọng, vì vậy nếu tên và "hợp đồng" của chúng (được xác định bởi tên, tài liệu và giá trị trả về) chức năng (chính bạn, nói chung).

Vì lợi ích của hình minh họa, hãy nói rằng bạn đang gọi định dạng này thường xuyên. Sau đó, có lý do để trừu tượng; Mã gọi sẽ trở nên cồng kềnh và lặp đi lặp lại. Bạn có thể di chuyển mã định dạng sang hàm trợ giúp và truyền bất kỳ dữ liệu động nào để bơm vào mẫu:

def fmt_higher(name, n, cutoff=12):
    verb = "is" if n > cutoff else "isn't"
    return f"{name} {verb} higher than {cutoff}"

if __name__ == "__main__":
    print(fmt_higher("alpha", 42))
    print(fmt_higher("beta", 6))
    print(fmt_higher("epsilon", 0))
    print(fmt_higher(name="delta", n=2, cutoff=-5))

Chúng ta có thể tiến thêm một bước và giả vờ rằng

def fmt_higher(name, n, cutoff=12):
    verb = "is" if n > cutoff else "isn't"
    return f"{name} {verb} higher than {cutoff}"

if __name__ == "__main__":
    print(fmt_higher("alpha", 42))
    print(fmt_higher("beta", 6))
    print(fmt_higher("epsilon", 0))
    print(fmt_higher(name="delta", n=2, cutoff=-5))
8 là một thử nghiệm phức tạp hơn nhiều với nhiều bước nhỏ sẽ vi phạm trách nhiệm đơn nếu để lại trong
def fmt_higher(name, n, cutoff=12):
    verb = "is" if n > cutoff else "isn't"
    return f"{name} {verb} higher than {cutoff}"

if __name__ == "__main__":
    print(fmt_higher("alpha", 42))
    print(fmt_higher("beta", 6))
    print(fmt_higher("epsilon", 0))
    print(fmt_higher(name="delta", n=2, cutoff=-5))
9. Có thể thử nghiệm phức tạp được sử dụng ở nơi khác trong mã và có thể được khái quát hóa để hỗ trợ cả hai trường hợp sử dụng.

Trong tình huống này, bạn vẫn có thể sử dụng các tham số và trả về các giá trị thay vì global và thực hiện cùng một loại trừu tượng với vị ngữ như bạn đã làm với định dạng:

def complex_predicate(n, cutoff):
    # pretend this function is much more 
    # complex and/or used in many places...
    return n > cutoff

def fmt_higher(name, n, cutoff=12):
    verb = "is" if complex_predicate(n, cutoff) else "isn't"
    return f"{name} {verb} higher than {cutoff}"

if __name__ == "__main__":
    print(fmt_higher("alpha", 42))
    print(fmt_higher("beta", 6))
    print(fmt_higher("epsilon", 0))
    print(fmt_higher(name="delta", n=2, cutoff=-5))

Chỉ tóm tắt khi có đủ lý do để trừu tượng (mã gọi sẽ bị tắc nghẽn hoặc khi bạn lặp lại các khối mã tương tự nhiều lần là các quy tắc cổ điển). Và khi bạn làm trừu tượng, hãy làm đúng.