2010-08-26 23 views
23

Nếu bạn tình cờ cóPython: Tại sao 'từ <module> nhập *' bị cấm?

from <module> import * 

ở giữa chương trình của bạn (hoặc mô-đun), bạn sẽ nhận được cảnh báo:

/tmp/foo:100: SyntaxWarning: import * only allowed at module level 

Tôi hiểu tại sao import * không được khuyến khích nói chung (namespace tàng hình) , nhưng có nhiều tình huống mà nó sẽ chứng minh thuận tiện, đặc biệt là nơi mã không được chia sẻ với bất kỳ ai.

Vì vậy, bất kỳ ai cũng có thể giải thích chi tiết một cách chính xác chi tiết tại sao from <module> import * nên bị cấm trong mọi trường hợp có thể?

+0

IDE nào bạn đang sử dụng để cảnh báo? – avi

+0

Tôi đã viết một bài đăng blog giải thích tại sao điều này phải bị cấm ở phạm vi phi toàn cầu: https://www.lesinskis.com/TIL_python_imports.html – shuttle87

Trả lời

19

Tôi tin rằng bằng cách "ở giữa chương trình của bạn" bạn đang nói về một khẩu bên định nghĩa hàm:

def f(): 
    from module import * # not allowed 

này không được phép vì nó sẽ làm cho việc tối ưu hóa các cơ quan chức năng quá cứng. Việc triển khai Python muốn biết tất cả các tên của các biến chức năng cục bộ khi nó biên dịch một hàm, để nó có thể tối ưu hóa các tham chiếu biến thành các phép toán trên ngăn xếp của máy ảo (CPython), hoặc ít nhất là vào vùng biến cục bộ hoạt động thay vì tra cứu trong không gian tên bên ngoài. Nếu bạn có thể đổ toàn bộ nội dung của một mô-đun vào không gian tên cục bộ của hàm, thì trình biên dịch sẽ phải giả định rằng bất kỳ tên nào trong hàm này có thể tham chiếu đến một mô-đun toàn cục, vì danh sách tên được đưa vào bởi from module import * chỉ là được biết khi chạy.

Đưa from module import *ở giữa tờ khai cấp cao nhất là phong cách của người nghèo, nhưng nó cho phép:

def f(): 
    ... 

from module import * 

def g(): 
    ... 

EDIT tháng 4 năm 2013: Trong khi nhìn vào cái gì khác, tôi phát hiện ra rằng hạn chế này đã được giới thiệu trong Python 2.1, do hậu quả của "Nested Scopes" feature (PEP 227). Trích dẫn từ liên kết:

Một tác dụng phụ của thay đổi là các tuyên bố from module import *exec đã được thực hiện bất hợp pháp trong phạm vi chức năng trong các điều kiện nhất định. Hướng dẫn tham khảo Python đã nói rằng tất cả cùng một rằng from module import * chỉ là hợp pháp ở cấp cao nhất của một mô-đun, nhưng trình thông dịch CPython chưa bao giờ thực thi điều này trước đây. Là một phần của việc thực hiện các phạm vi lồng nhau, trình biên dịch biến nguồn Python thành bytecode phải tạo ra các mã khác nhau để truy cập các biến trong một phạm vi có chứa. from module import *exec làm cho trình biên dịch không thể tìm ra điều này, bởi vì chúng thêm tên vào không gian tên cục bộ không thể biết được tại thời gian biên dịch. Do đó, nếu một hàm chứa các định nghĩa hàm hoặc lambda các biểu thức có các biến miễn phí, thì trình biên dịch sẽ gắn cờ điều này bằng cách tăng một ngoại lệ SyntaxError.

Điều này làm rõ hành vi Python 3.x và 2.x được thảo luận trong các nhận xét. Nó luôn luôn trái ngược với đặc tả ngôn ngữ, nhưng CPython 2.1 đến 2.7 chỉ phát hành một lỗi cho from module import * trong một hàm nếu nó có thể ảnh hưởng đến khả năng của trình biên dịch để biết một biến gắn với cục bộ hay trong một phạm vi có chứa. Trong 3.x nó đã được đẩy lên một lỗi vô điều kiện.

SƠN CHỈNH SỬA: ... và rõ ràng là flashk đã chỉ ra điều này trong nhiều năm trước trong một câu trả lời khác, trích dẫn cùng một đoạn của "Có gì mới trong Python 2.1". Y'all đi upvote rằng bây giờ.

+3

Cảm ơn bạn đã phân tích, Zack. Dường như: (1) 'import *' được cho phép trong một thân hàm (với một cảnh báo) nếu nó không phải là, hoặc không có, bất kỳ khối lồng nhau nào; (2) Mặc dù Python doc nói rằng nó không được phép ngoại trừ ở cấp cao nhất của một mô-đun, nó không bao giờ được thi hành (tức là, vẫn có thể); (3) Nếu trình biên dịch byte có thể thực hiện mô-đun để nó biết tất cả các biến và hàm, nó sẽ không là vấn đề để tối ưu hóa; (4) Chỉ cho tôi một ví dụ về những gì đã trở thành sai với phạm vi lồng nhau, Python. – OTZ

+0

@Zack - Biến miễn phí là các biến được ràng buộc trong phạm vi bao quanh. Vì vậy, nếu bạn có một mô-đun gán toàn cầu 'x = 3' và một hàm' def foo(): in x', thì x là một biến miễn phí của foo. Nó không phải là một từ chồn - xem http://docs.python.org/reference/executionmodel.html để định nghĩa. "Nếu một biến được sử dụng trong một khối mã nhưng không được định nghĩa ở đó, nó là một biến miễn phí." –

+0

@ Jeremy: Ý tôi là với "từ chồn" là quy tắc ngôn ngữ là bạn chỉ có thể sử dụng "từ X nhập *" trong phạm vi mô-đun, nhưng việc triển khai (có vẻ như) chỉ thực thi trong các trường hợp hạn chế. – zwol

11

Các release notes for Python 2.1 dường như để giải thích lý do tại sao hạn chế này tồn tại:

hiệu lực thi hành

Một bên của sự thay đổi là từ mô-đun nhập khẩu * và exec báo cáo đã được thực hiện bất hợp pháp bên trong một phạm vi chức năng theo một số điều kiện. Tài liệu tham khảo hướng dẫn sử dụng của Python đã cho biết từ nhập mô-đun * chỉ hợp pháp ở mức cấp cao nhất của mô-đun, nhưng phiên dịch CPython chưa bao giờ thực thi trước đây . Là một phần của việc thực hiện phạm vi lồng nhau, trình biên dịch mà chuyển nguồn Python thành bytecodes có để tạo mã khác nhau để truy cập biến trong phạm vi có chứa. từ nhập mô-đun * và exec làm cho nó không thể cho trình biên dịch tìm được số điều này, bởi vì chúng thêm tên vào không gian tên cục bộ là không thể biết tại thời gian biên dịch. Do đó, nếu hàm chứa hàm định nghĩa hoặc biểu thức lambda với biến miễn phí, trình biên dịch sẽ gắn cờ điều này bằng cách tăng ngoại lệ SyntaxError .

+0

OK. Vì vậy, sẽ không 'import *' được thực hiện nếu trình biên dịch byte chạy trong 'từ nhập *' tại thời gian biên dịch để nó biết tất cả các biến và hàm có trong mô-đun? Tại sao nó sẽ không làm điều đó? – OTZ

+0

Đối với một điều, '' có thể không tồn tại khi mã chứa câu lệnh nhập được biên dịch. Hoặc tập hợp các biến được nhập có thể thay đổi giữa "thời gian biên dịch" và "thời gian chạy". –

1

Nó hoàn toàn không bị cấm. Nó hoạt động tốt, nhưng bạn nhận được một cảnh báo vì nó thường là một ý tưởng tồi (vì lý do người khác đã đi vào). Bạn có thể, nếu bạn thích, ngăn chặn các cảnh báo; mô-đun cảnh báo là những gì bạn muốn cho điều đó.

15

Ở bất kỳ cấp độ từ vựng nào, from amodule import * là một quyết định thiết kế có vẻ là một thảm họa thực tế trong cuộc sống thực. , Tôi không quá nóng trên nó - import module as m chỉ buộc thêm hai ký tự để sử dụng tên đủ điều kiện thay vì [tiền tố m.]], tên đủ điều kiện luôn sắc nét và linh hoạt hơn tên thanh, chưa kể đến tính hữu ích trong các tình huống tương tác khám phá của việc có m có sẵn cho help(m), reload(m) và các loại tương tự!). Điều này làm cho nó rất khó khăn, cho người nghèo đọc mã (thường là trong một nỗ lực cam chịu để giúp gỡ lỗi nó) để hiểu nơi xuất hiện bí ẩn tên - không thể, nếu xây dựng được sử dụng nhiều hơn hơn một lần ở cấp độ từ vựng; nhưng ngay cả khi được sử dụng chỉ một lần, nó buộc phải đọc lại toàn bộ mô-đun mỗi lần trước khi người ta có thể thuyết phục bản thân rằng, yep, tên mã bị phân mảnh đó phải đến từ mô-đun.

Ngoài ra, tác giả mô-đun thường không gặp phải sự cố cực đoan cần thiết để "hỗ trợ" cấu trúc khủng khiếp được đề cập. Nếu bạn có mã nào đó trong mã của mình, ví dụ: sử dụng sys.argv (và một số import sys ở đầu mô-đun của bạn), làm cách nào để bạn biết rằng sys là mô-đun cần ... hoặc một số hoàn toàn khác nhau (hoặc không phải là mô-đun) đến từ ... import *?!Nhân với tất cả các tên đủ điều kiện bạn đang sử dụng, và đau khổ là kết quả cuối cùng duy nhất - đó, và các lỗi bí ẩn đòi hỏi gỡ lỗi lâu dài, mất thời gian (thường với sự giúp đỡ miễn cưỡng của ai đó nào "get" Python ... ! -).

Trong một hàm, cách thêm và ghi đè tên địa phương tùy ý sẽ còn tệ hơn nữa. Là một trình tối ưu hóa cơ bản nhưng rất quan trọng, trình biên dịch Python nhìn xung quanh phần tử của hàm cho bất kỳ phép gán hoặc các câu lệnh ràng buộc nào khác trên mỗi tên mã, và coi "local" (tên địa phương) mà nó thấy được gán (các tên khác phải là các globals hoặc built-ins). Với một import * (giống như với exec somestring mà không có dấu hiệu rõ ràng để sử dụng như không gian tên), đột nhiên nó trở thành một bí ẩn tổng thể tên địa phương, tên là toàn cầu - do đó trình biên dịch nghèo sẽ phải nghỉ mát đến chiến lược chậm nhất có thể cho mỗi tra cứu tên, sử dụng dict cho các biến cục bộ (thay vì "vector" nhỏ gọn mà nó thường sử dụng) và thực hiện tối đa ba lần xem trước cho từng tên mã được tham chiếu, lặp đi lặp lại.

Chuyển tới bất kỳ lời nhắc tương tác Python nào. Nhập import this. Bạn thấy gì? Zen của Python. Cái gì cuối cùng và có lẽ là chút khôn ngoan nhất trong văn bản đó ...?

Không gian tên là một ý tưởng tuyệt vời đáng kinh ngạc - hãy làm nhiều hơn nữa!

By buộc việc sử dụng barenames nơi tên tiêu chuẩn là nên bao la thích hợp hơn, bạn đang chủ yếu làm điều ngược lại rất giới thiệu khôn ngoan này: thay vì ngưỡng mộ sự vĩ đại và honkingtude của không gian tên, và làm nhiều hơn trong số đó, bạn đang chia nhỏ hai không gian tên hoàn toàn tốt và sẵn sàng sử dụng (của mô-đun bạn đang nhập và phạm vi từ vựng bạn nhập vào) để tạo thành một đơn vị, không độc hại, buggy, chậm, cứng nhắc, không thể sử dụng mess.

Nếu tôi có thể quay trở lại và thay đổi một quyết định thiết kế đầu bằng Python (đó là một sự lựa chọn khó khăn, bởi vì việc sử dụng và đặc biệt là deflambda cho những gì Javascript rất nhiều readably hơn gọi function là một đóng thứ hai ;-), Tôi sẽ xóa sạch ý tưởng import * khỏi tâm trí của Guido. Không có số tiền bị cáo buộc tiện lợi cho việc khám phá tại dấu nhắc tương tác có thể cân bằng số lượng điều xấu mà nó bị ...!)

+2

+1 honkingtude: P –

+0

Nhưng, nhưng! Thực tế nói, nó là soooo ** 100 hữu ích. Giả sử bạn có một thư viện tiện ích đa năng, chính thống, ví dụ: 'aima/utils.py' của Peter Norvig. Tôi không muốn nối thêm 'm'. hàng chục lần trong tất cả các phiên tương tác và trong bất kỳ kịch bản nào tôi đang viết đồng thời để tham khảo các hàm util trong thư viện. Tôi đang nói: nếu bạn chắc chắn sẽ không có gì và sẽ xung đột với không gian tên cục bộ bằng 'import *' và nếu bạn nhận thức đầy đủ tất cả các mô-đun được nhập, thì tại sao lại loại bỏ sự tiện lợi? Tất nhiên, nó nên được khuyến khích trong một dự án của nhiều hơn một kỹ sư. – OTZ

+4

"tại sao loại bỏ sự tiện lợi?" - nó được gọi là "phiền toái hấp dẫn" trong phạm vi pháp lý: một "sự tiện lợi" được cho là lôi kéo mọi người hoàn tác của họ (và, trong trường hợp này, chạy chính xác trái với các nguyên tắc chính của Python, như tôi đã trình bày). Python thường khá giỏi ở đó, nhưng '... import *' là một ngoại lệ không may. Chỉ cần nhìn vào tất cả các sự xuất hiện của nó trên SO, ví dụ, và bạn sẽ bắt đầu nhìn thấy những gì một nền tảng sinh sản cho các lỗi, khó khăn và perplexities nó được. Bạn muốn conciseness ở tất cả các chi phí?Sau đó, Python không dành cho bạn - sức mạnh, sự rõ ràng, sang trọng, vâng, sự đồng nhất cực đoan, ** không **. –

0

những người khác đã đưa ra câu trả lời chuyên sâu, tôi sẽ đưa ra một câu trả lời tổng quan ngắn về sự hiểu biết của tôi .. khi sử dụng từ bạn đang tạo nó để bạn có thể gọi trực tiếp bất kỳ chức năng nào trong mô-đun mà bạn đã nhập mà không làm modulename.functioname (bạn chỉ có thể gọi "functionname") điều này tạo ra vấn đề nếu bạn có 2 chức năng cùng tên trong các mô-đun khác nhau và cũng có thể gây nhầm lẫn khi giao dịch với nhiều chức năng như bạn không biết đối tượng/mô-đun thuộc về (từ quan điểm của ai đó đang xem mã đã viết không quen thuộc với nó)

4

Nó không bị cấm, bởi vì ...

... nó tiện dụng cho các tập lệnh nhanh và khám phá vỏ.

...nhưng bạn không nên giữ nó trong bất kỳ mã nghiêm trọng

  1. nó có thể dẫn nhập khẩu tên mà bạn không biết về và xóa tên địa phương
  2. bạn không thể biết những gì đang được sử dụng trong mã của bạn, thật khó biết một kịch bản phụ thuộc nào
  3. đang hoàn tất sẽ không hoạt động nữa
  4. IDE kiểm tra thuận tiện như "var này chưa được công bố là" có thể không hoạt động nữa
  5. nó làm cho dễ dàng hơn để tạo nhập khẩu tròn
Các vấn đề liên quan