2012-11-01 29 views
10

Để bắt đầu, tôi nghĩ rằng tôi có thể đã tìm ra cách để mã này hoạt động (dựa trên Changing module variables after import), nhưng câu hỏi của tôi thực sự là tại sao hành vi sau xảy ra không làm gì trong tương lai.Nhập mô-đun: __main__ vs nhập khẩu làm mô-đun

Tôi có ba tệp. Đầu tiên là mod1.py:

# mod1.py 

import mod2 

var1A = None 

def func1A(): 
    global var1 
    var1 = 'A' 
    mod2.func2() 

def func1B(): 
    global var1 
    print var1 

if __name__ == '__main__': 
    func1A() 

Tiếp theo, tôi có mod2.py:

# mod2.py 

import mod1 

def func2(): 
    mod1.func1B() 

Cuối cùng tôi có driver.py:

# driver.py 

import mod1 

if __name__ == '__main__': 
    mod1.func1A() 

Nếu tôi thực hiện lệnh python mod1.py thì đầu ra là None. Dựa trên liên kết tôi đã tham chiếu ở trên, có vẻ như có một số khác biệt giữa mod1.py đang được nhập là __main__mod1.py đang được nhập từ mod2.py. Do đó, tôi đã tạo driver.py. Nếu tôi thực hiện lệnh python driver.py thì tôi nhận được kết quả mong đợi: A. Tôi thấy sự khác biệt, nhưng tôi không thực sự thấy cơ chế hoặc lý do cho nó. Làm thế nào và tại sao điều này xảy ra? Có vẻ phản trực giác rằng cùng một mô-đun sẽ tồn tại hai lần. Nếu tôi thực thi python mod1.py, bạn có thể truy cập các biến trong phiên bản __main__ của mod1.py thay vì các biến trong phiên bản được nhập bởi mod2.py không?

+1

Bạn sẽ tự làm việc nếu bạn cấu trúc lại để loại bỏ việc nhập vòng tròn. – eryksun

Trả lời

19

Biến số __name__ luôn chứa tên của mô-đun, ngoại trừ khi tệp được tải vào trình thông dịch dưới dạng tập lệnh thay thế. Sau đó, biến đó được đặt thành chuỗi '__main__' thay thế.

Sau khi tất cả, tập lệnh sau đó được chạy dưới dạng tệp chính của toàn bộ chương trình, mọi thứ khác là các mô-đun được nhập trực tiếp hoặc gián tiếp bởi tệp chính đó. Bằng cách kiểm tra biến số __name__, bạn có thể phát hiện xem tệp đã được nhập dưới dạng mô-đun hay được chạy trực tiếp.

Nội bộ, các mô-đun được cung cấp từ điển không gian tên, được lưu trữ như một phần của siêu dữ liệu cho từng mô-đun, trong sys.modules. Tệp chính, tập lệnh được thực thi, được lưu trữ trong cấu trúc giống như '__main__'.

Nhưng khi bạn nhập tệp dưới dạng mô-đun, trăn đầu tiên sẽ xem sys.modules để xem liệu mô-đun đó đã được nhập trước đó chưa. Vì vậy, import mod1 có nghĩa là chúng tôi đầu tiên tìm kiếm trong sys.modules cho mô-đun mod1. Nó sẽ tạo ra một cấu trúc mô-đun mới với một không gian tên nếu mod1 chưa có.

Vì vậy, nếu bạn vừa chạy mod1.py như file chính, sau nhập khẩu nó như là một mô-đun python, nó sẽ nhận được hai mục namespace trong sys.modules. Một như '__main__', sau đó là 'mod1'. Hai không gian tên này hoàn toàn tách biệt. Toàn cầu var1 của bạn được lưu trữ trong sys.modules['__main__'], nhưng func1B đang tìm kiếm trong sys.modules['mod1'] cho var1, trong đó None.

Nhưng khi bạn sử dụng python driver.py, driver.py trở thành '__main__' tập tin chính của chương trình, và mod1 sẽ được nhập chỉ một lần vào cấu trúc sys.modules['mod1']. Lần này, func1A cửa hàng var1 trong cấu trúc sys.modules['mod1'] và đó là những gì func1B sẽ tìm thấy.

+0

Điều này có ổn không? 'if __name__ == '__main__': sys.modules ['mod1'] = sys.modules ['__ main__']; func1A() ' – Kos

+1

@Kos: Tôi sẽ không làm điều đó, tôi nghĩ bạn sẽ thấy rằng có rất nhiều giả định sẽ bị phá vỡ. Tránh sử dụng các mô-đun làm tập lệnh thay thế. –

+0

Tôi đã sử dụng 'if __name__ == '__main __':' để viết thói quen kiểm tra cho mỗi mô-đun của tôi trong mô-đun. Có cách nào tốt để tiếp tục thực hành này hay tôi nên viết các thủ tục kiểm tra như các hàm sau đó có một trình điều khiển trong một tệp riêng biệt không làm gì ngoài việc nhập mô-đun để thử nghiệm và gọi cho thường trình kiểm thử? – Brendan

1

Về một giải pháp thực tế cho việc sử dụng một mô-đun tùy chọn như kịch bản chính - hỗ trợ phù hợp cross-nhập khẩu:

Giải pháp 1:

Xem ví dụ trong mô-đun pdb Python, làm thế nào nó được chạy như một kịch bản bằng cách nhập bản thân khi thực hiện như __main__ (ở cuối):

#! /usr/bin/env python 
"""A Python debugger.""" 
# (See pdb.doc for documentation.) 
import sys 
import linecache 

... 

# When invoked as main program, invoke the debugger on a script 
if __name__ == '__main__': 
    import pdb 
    pdb.main() 

Chỉ cần tôi muốn giới thiệu để tổ chức lại __main__ khởi động vào đầu của kịch bản như thế này:

#! /usr/bin/env python 
"""A Python debugger.""" 
# When invoked as main program, invoke the debugger on a script 
import sys 
if __name__ == '__main__':   
    ##assert os.path.splitext(os.path.basename(__file__))[0] == 'pdb' 
    import pdb 
    pdb.main() 
    sys.exit(0) 

import linecache 
... 

Cách này cơ thể mô-đun không được thực hiện hai lần - "tốn kém", không mong muốn và đôi khi rất quan trọng.

Giải pháp 2:

Trong những trường hợp hiếm đó là mong muốn để lộ các module kịch bản thực tế __main__ thậm chí trực tiếp như các mô-đun bí danh thực tế (mod1):

# mod1.py  
import mod2 

... 

if __name__ == '__main__': 
    # use main script directly as cross-importable module 
    _mod = sys.modules['mod1'] = sys.modules[__name__] 
    ##_modname = os.path.splitext(os.path.basename(os.path.realpath(__file__)))[0] 
    ##_mod = sys.modules[_modname] = sys.modules[__name__] 
    func1A() 

nhược điểm Known:

  • reload(_mod) không thành công
  • lớp học được chọn sẽ cần thêm mappi ngs for unpickling (find_global ..)
Các vấn đề liên quan