Làm cách nào để bạn truy cập biến từ phạm vi bên ngoài trong python?

Gần đây tôi đã xây dựng bản beta của Python 3000 – bản cải tiến hoàn toàn sắp tới của Python (sẽ được phát hành vào tháng 9 – 992 năm trước khi họ hứa. ) Bởi vì Py3K “không tương thích ngược” một cách đáng xấu hổ, nên cuối cùng họ cũng sửa tất cả các lỗi ngôn ngữ chính và biến mọi thứ thành “theo cách chúng nên hoạt động. ” (Lưu ý sẽ có quá trình chuyển đổi hơi tự động từ mã Python 2 sang 3)

Và tôi yêu nó. Tất cả mọi thứ được cố định theo cách tôi hy vọng. Do đó, đây là bài đầu tiên trong loạt bài đăng trên blog “Py3K rox my sox”. Bạn có thể xem tóm tắt các tính năng mới tại đây

OK, vì vậy một trong những vấn đề chính mà tôi đã phàn nàn (và đã nghe) trong Python là vấn đề được gọi là "phạm vi bên ngoài". Đây là một hạn chế rất rõ ràng về những gì bạn có thể làm trong Python. đọc tiếp

Toàn cầu thực sự hoạt động như thế nào

Đầu tiên là một chút thông tin cơ bản có thể bạn chưa biết. Điều này áp dụng cho tất cả các phiên bản Python, không chỉ 3. 0

Trong Python nếu bạn không khai báo một biến, Python sẽ xác định xem bạn đang đề cập đến một biến cục bộ hay toàn cầu dựa trên việc bạn viết cho biến đó. Ví dụ

x = 4
def f():
    return x

Ở đây, Python chỉ ra rằng x mà bạn đề cập thực sự là x toàn cầu và trả về 4. Nó tìm ra điều này bởi vì hàm không bao giờ ghi vào x, ở bất cứ đâu. Không chỉ bởi vì nó chưa được ghi vào x, mà bởi vì nó không có câu lệnh gán cho x. (Nó tìm ra điều này một cách tĩnh, không phải trong thời gian chạy). Ví dụ

x = 4
def f():
    if True:
        return x
    else:
        x = 2

Đây sẽ là một câu hỏi đố gọn gàng thực sự. f() đánh giá cái gì?

Câu trả lời. UnboundLocalError. biến cục bộ 'x' được tham chiếu trước khi gán

Thực tế là x được gán ở đâu đó trong hàm (thậm chí ở đâu đó sẽ không bao giờ được thực thi) khiến Python coi nó là cục bộ và do đó, nó không được xác định khi bạn trả lại nó

Giải pháp chính xác là khai báo nó một cách rõ ràng "toàn cầu", đây là cách duy nhất để tạo một hàm ghi vào một

x = 4
def f():
    global x
    if True:
        return x
    else:
        x = 2

Điều này hoạt động tốt trong thực tế, bởi vì bạn có thể xác định các hằng số như MAX_FOO và sử dụng chúng ở mọi nơi mà không cần khai báo chúng trên toàn cầu, nhưng bạn cần phải rõ ràng nếu bạn muốn cập nhật một toàn cầu (thường là một ý tưởng hay vì nó nguy hiểm – xem

Vấn đề “phạm vi bên ngoài”

Về vấn đề “phạm vi bên ngoài”. Về cơ bản, Python cho phép bạn viết các hàm lồng nhau và các hàm lồng nhau có quyền truy cập vào các biến cục bộ của mã chứa chúng. Ví dụ

def outer():
    x = 9
    def inner_read():
        return x
    return inner_read()

Nếu bạn gọi bên ngoài (), nó sẽ trả về 9. Biến x là cục bộ của hàm bên ngoài. Nhưng chức năng bên trong có thể đọc nó và trả lại

Vấn đề xảy ra khi bạn muốn ghi vào một biến không cục bộ, như thế này

def outer():
    x = 9
    def inner_read():
        return x
    def inner_write():
        x = 3
    inner_write()
    return inner_read()

Cũng giống như các biến toàn cục, Python có thể tìm thấy các biến phạm vi bên ngoài nếu bạn chỉ đọc chúng (như inner_read đã làm), nhưng nếu bạn viết chúng ở bất kỳ đâu trong hàm, nó sẽ giả định rằng bạn đang tạo một biến cục bộ mới (như inner_write đã làm). Do đó, inner_write tạo một x cục bộ mới và gán cho nó 3, và hàm outside trả về 9. Tôi muốn Internal_write cập nhật x hiện có và do đó có trả về bên ngoài 3

Giải pháp khá đơn giản. Có một từ khóa như toàn cầu, nhưng thay vì đi đến phạm vi trên cùng, nó chỉ yêu cầu Python tìm kiếm phạm vi trong cùng với một biến ràng buộc có tên đó

Trăn 3. 0 giới thiệu chính xác đó. từ khóa nonlocal. Thử một lần đi

def outer():
    x = 9
    def inner_read():
        return x
    def inner_write():
        nonlocal x
        x = 3
    inner_write()
    return inner_read()

tiếng kêu. Trăn 3. 0 biên dịch mã này và hàm bên ngoài trả về 3

Điều buồn cười là, vấn đề này dường như chỉ xảy ra với Python. Trong hầu hết các ngôn ngữ tĩnh, tất cả các biến được khai báo. Trong Haskell, tất cả các biến đều ở dạng chỉ đọc. Trong Ruby, bạn đề cập đến các biến toàn cục bằng cách đặt tiền tố cho chúng bằng một đô la. Trong JavaScript, nó là nghịch đảo của Python. bạn khai báo tất cả các biến cục bộ và chúng mặc định là biến toàn cục (đó là một ý tưởng tồi tệ - nếu bạn quên khai báo một biến, bạn sẽ ngầm bắt đầu chia sẻ ở nơi mà bạn không muốn chia sẻ). Tất nhiên có thể có các ngôn ngữ khác gặp vấn đề này nhưng Python là ngôn ngữ duy nhất tôi từng thấy

Người giới thiệu

  • PEP 3104 – Access to Name in Outer Scopes – Đề xuất/thảo luận chính thức về tính năng này

Chia sẻ cái này

  • Twitter
  • Facebook

Như thế này

Thích Đang tải.

Có liên quan

8 bình luận

8 phản hồi cho “Py3K. Giải quyết vấn đề "phạm vi bên ngoài""

  1. Làm cách nào để bạn truy cập biến từ phạm vi bên ngoài trong python?

    Tim Tháng Sáu 27, 2008 lúc 4. 02 giờ chiều. Đáp lại

    àh. tôi thấy bây giờ

  2. Làm cách nào để bạn truy cập biến từ phạm vi bên ngoài trong python?

    Justin George Tháng Sáu 28, 2008 tại 7. 14 giờ sáng. Đáp lại

    Thực tế, javascript là ngôn ngữ phải khai báo vì lý do đó

    Đừng bao giờ rời khỏi nhà mà không có JSLint của bạn, nó sẽ chỉ ra điều đó khi bạn sử dụng một biến không được khai báo. Có thể cho rằng, nó nên được tích hợp vào trình thông dịch như một lời cảnh báo

  3. Làm cách nào để bạn truy cập biến từ phạm vi bên ngoài trong python?

    pcdinh 29 Tháng sáu 2008 lúc 2. 46 giờ chiều. Đáp lại

    Tôi thấy đó là dấu hiệu của một ngôn ngữ thiết kế tồi. Guido muốn Python của mình trở thành một ngôn ngữ hackish

  4. Làm cách nào để bạn truy cập biến từ phạm vi bên ngoài trong python?

    Matt Giuca Tháng Sáu 29, 2008 tại 2. 54 giờ chiều. Đáp lại

    @pcdinh. Không biết tại sao bạn thấy điều này là "thiết kế xấu". Nếu bạn không yêu cầu khai báo biến và cho phép cập nhật biến, thì đây sẽ là một vấn đề và tôi thấy đây là một giải pháp đơn giản hay

    (Bây giờ tôi nghĩ về nó, $vars của Ruby sẽ không giúp truy cập các vars của phạm vi bên ngoài, vì vậy tôi không chắc Ruby giải quyết vấn đề này như thế nào)

    Thực tế là trong JavaScript bạn cần JSLint, như Justin đã chỉ ra, cho thấy mức độ khủng khiếp của nó đối với một ngôn ngữ động yêu cầu khai báo biến

    (Vấn đề với JavaScript so với ngôn ngữ được nhập tĩnh là nếu không có biến, nó sẽ tạo ra một biến toàn cầu, trong khi ở ngôn ngữ tĩnh, đó sẽ là lỗi trình biên dịch)

  5. Làm cách nào để bạn truy cập biến từ phạm vi bên ngoài trong python?

    SneakyWho_am_i Tháng Bảy 7, 2008 tại 8. 18 giờ sáng. Đáp lại

    Trước khi tôi bắt đầu, tôi có một khiếu nại nghiêm trọng. Có một bộ màu nền ảnh hưởng đến vùng văn bản này. Nhưng không có màu nền trước nào được đặt. Vì vậy, tôi đang viết chữ trắng trên nền trắng. Rất, rất không hay

    Dù sao về chủ đề thì quan điểm của mình khác pcdinh. Có lẽ vì php là ngôn ngữ kịch bản đầu tiên tôi từng học. Có lẽ bởi vì tôi chỉ là một lập trình viên mới vào nghề, nếu tôi thậm chí còn là một lập trình viên (tôi không hứa đâu;)). Hoặc có thể (tôi thấy điều này khó chấp nhận) đó là do khả năng kiểm soát phạm vi biến một cách rõ ràng khiến ngôn ngữ trở nên “hackish”

    Trong javascript, bạn có tùy chọn khai báo các biến của mình một cách rõ ràng. Tôi luôn chọn tùy chọn đó vì tôi coi đó là vấn đề ngăn chặn không chỉ lỗi mà cả sự không an toàn

    Trong php bạn không thực sự có lựa chọn. Mặc dù bạn tự động khai báo các biến của mình, nhưng KHÔNG có biến nào có sẵn trên toàn cầu (đừng đồng ý. Ý tôi là trong javascript, bạn có thể truy cập các biến toàn cầu mà không cần bất kỳ từ khóa hoặc thủ tục đặc biệt nào. Trong php bạn không thể. ), vì vậy, khi khai báo trường hợp nào của biến mà bạn sẽ đề cập đến, bạn phải nêu rõ phạm vi mà biến bạn sử dụng áp dụng cho

    Javascript có bị hack không? . PHP? . (đó là một sức mạnh, thực sự)…

    C có quan tâm đến phạm vi biến không? . Chúng buộc bạn phải nhập các biến của mình (gần như) một cách hiệu quả, vì vậy tôi sẽ rất ngạc nhiên nếu đó là một ngôn ngữ không có phạm vi

    Java có cho phép bạn viết mã mà không cần quan tâm đến phạm vi biến không??

    Dù sao, kết hợp tất cả những điều này lại với nhau, có vẻ như Python là ngôn ngữ cấp cao phổ biến duy nhất có thể hoạt động theo hướng thủ tục hoặc hướng đối tượng, điều này cho phép tôi không kiểm soát rõ ràng đối với phạm vi biến (kể từ 2. 5) …

    Tôi rất mới với tất cả, đặc biệt là trăn, vì vậy hãy sửa cho tôi nếu tôi sai về nó

    Nhưng theo cách tôi nhìn thấy, một ngôn ngữ (thực tế triển khai) càng ít hackish thì nó càng quan tâm đến phạm vi biến (và kiểm soát phạm vi càng trở nên phức tạp hơn)

    Tác giả của bài đăng gốc có tôn vinh khả năng gọi các biến cục bộ và/hoặc biến toàn cầu một cách rõ ràng không? . Không có tính năng này, Python kém mạnh mẽ hơn, ít hữu ích hơn và trong nhiều ứng dụng ít phù hợp hơn

    Kiểm soát được thêm vào có làm cho nó trở thành một ngôn ngữ hackish hơn không?
    Không. Nó làm cho nó không còn là một món đồ chơi theo sở thích của trẻ em mà trở thành một ngôn ngữ nghiêm túc hơn được tính đến.

    (Lưu ý khi tôi nói “ít đồ chơi hơn” tôi không có ý nói rằng nó thực sự là một món đồ chơi. Tôi sử dụng rất nhiều ứng dụng python thực sự xuất sắc và rất phức tạp trên máy tính ở nhà của mình, vì vậy tôi không có ý định đặt nó xuống. Trên thực tế, tôi đang viết một cái gì đó trong đó ngay bây giờ, vì vậy ha. Đó là một ngôn ngữ nghiêm túc, nhưng tính năng được thêm vào khiến nó thậm chí còn hơn thế nữa. )

  6. Matt Giuca Tháng Bảy 8, 2008 tại 1. 50 giờ chiều. Đáp lại

    @SneakyWho_am_i. Cảm ơn vì một phản hồi tuyệt vời

    Trên thực tế, bạn có thể nhận được toàn cầu trong PHP - nó dường như hoạt động giống như Python (mặc dù tôi không phải là chuyên gia về PHP). Xem tại đây. http. //au2. php. mạng/toàn cầu

    Tôi nghĩ rằng nếu bạn nhìn vào hai lựa chọn thay thế. Python/PHP là cục bộ theo mặc định và JavaScript là toàn cầu theo mặc định – lưu ý rằng CẢ HAI đều cung cấp cho bạn quyền kiểm soát phạm vi, nó chỉ thay đổi theo mặc định. Tôi có thể nói rằng JavaScript "hackish" hơn vì mặc định toàn cầu khiến dễ mắc lỗi hơn, nhưng có khả năng thực hiện một số điều mạnh mẽ hơn. (Mặc dù tôi chỉ muốn nói rằng nó được thiết kế tồi tệ hơn là sử dụng từ “hackish”)

    Các ngôn ngữ được nhập tĩnh không thực sự gặp vấn đề này vì chúng (thường) buộc bạn phải khai báo tất cả các biến. Điều đó có nghĩa là bạn không thể vô tình sử dụng một biến không được khai báo và bắt đầu thao túng các biến toàn cầu. Tôi nghĩ vấn đề là nếu một ngôn ngữ không buộc bạn phải khai báo các biến, thì chúng nên mặc định là gì - và local chắc chắn là tùy chọn “an toàn”

    Vấn đề duy nhất với việc đặt mặc định thành cục bộ là (và tôi đã phàn nàn về nó từ lâu) bạn hoàn toàn không thể truy cập phạm vi bên ngoài. Đó là những gì tính năng này khắc phục nó. Vì vậy, tôi chắc chắn nghĩ rằng nếu bạn có một ngôn ngữ với khai báo biến ẩn, thì đây là cách để làm điều đó

  7. SneakyWho_am_i Tháng Năm 27, 2009 tại 4. 31 giờ sáng. Đáp lại

    Có Matt, bạn có thể nhận các biến toàn cục trong PHP nhưng quyền truy cập là cục bộ. Tôi không biết tại sao tôi lại viết nó theo cách khiến nó không đúng nghĩa đen. Tôi tự hỏi mình đang cố nói gì…

    Tôi không thể đồng ý hơn về vấn đề javascript, tôi đã thấy những người lập trình javascript bị bối rối trong giây lát vì họ đã khai báo hoặc không khai báo một biến rõ ràng

    Dù sao đi nữa, giờ đây tất cả đều có ý nghĩa. Tôi đã rời xa Python (vẫn tự hỏi liệu đó có phải là một sai lầm) để chuyển sang C và C++. Tôi vẫn còn rất yêu thích chúng và vâng, tất nhiên, như mong đợi, nó hoàn toàn là địa phương.
    Thật ngạc nhiên là tôi chưa bao giờ thử truy cập một biến từ phạm vi cấp độ cha. Tôi đã đọc ở đâu đó rằng đó là một thói quen xấu khi sử dụng một biến mà bạn không tham số hóa (khi sử dụng PHP theo thói quen) và tôi đoán rằng tôi đã phải từ bỏ nó vào một lúc nào đó do nó.

    Bây giờ tôi phải đi xa và tìm hiểu về điều đó 😮

  8. Matt Giuca Tháng Năm 27, 2009 tại 6. 58 giờ tối. Đáp lại

    Tôi nghĩ đó chủ yếu là một thói quen xấu, nhưng không tệ như thói quen toàn cầu. Lý do bạn có thể muốn làm điều đó là nếu bạn muốn một hàm lồng nhau thay đổi một biến cục bộ trong hàm cha

    Trong Python và nhiều ngôn ngữ khác, bạn *không thể* tham số hóa các đối số đó vì không có tham chiếu chuyển tiếp (bạn có thể chuyển chúng vào nhưng không thể cập nhật chúng khi thoát ra). Vì vậy, bạn có thể trả về tất cả các giá trị bạn muốn sửa đổi dưới dạng một bộ hoặc chỉ sử dụng nonlocal

    Điều gì xảy ra khi bạn cố gắng truy cập một biến nằm ngoài phạm vi của nó?

    Sẽ gây ra lỗi nếu bạn cố truy cập vào một biến không thuộc phạm vi cục bộ hoặc toàn cầu.

    Phạm vi bên ngoài nhất trong Python là gì?

    Phạm vi chung (hoặc mô-đun) là phạm vi cao nhất trong chương trình, tập lệnh hoặc mô-đun Python. Phạm vi Python này chứa tất cả các tên mà bạn xác định ở cấp cao nhất của chương trình hoặc mô-đun. Tên trong phạm vi Python này có thể nhìn thấy từ mọi nơi trong mã của bạn.