Làm thế nào để tạo một biến mô-đun chéo?


122

Các __debug__biến là tiện dụng trong một phần vì nó ảnh hưởng đến mỗi mô-đun. Nếu tôi muốn tạo một biến khác hoạt động theo cách tương tự, tôi sẽ làm như thế nào?

Biến (chúng ta hãy là nguyên bản và gọi nó là 'foo') không nhất thiết phải là toàn cục thực sự, theo nghĩa là nếu tôi thay đổi foo trong một mô-đun, nó sẽ được cập nhật ở những mô-đun khác. Tôi sẽ ổn nếu tôi có thể đặt foo trước khi nhập các mô-đun khác và sau đó họ sẽ thấy giá trị tương tự cho nó.

Câu trả lời:


114

Tôi không xác nhận giải pháp này dưới bất kỳ hình thức, hình thức nào. Nhưng nếu bạn thêm một biến vào __builtin__mô-đun, nó sẽ có thể truy cập được như thể một toàn cục từ bất kỳ mô-đun nào khác bao gồm __builtin__- theo mặc định là tất cả chúng.

a.py chứa

print foo

b.py chứa

import __builtin__
__builtin__.foo = 1
import a

Kết quả là "1" được in.

Chỉnh sửa: Các __builtin__mô-đun có sẵn như là biểu tượng địa phương __builtins__- đó là lý do cho sự khác biệt giữa hai trong số các câu trả lời. Cũng lưu ý rằng nó __builtin__đã được đổi tên thành builtinstrong python3.


2
Bất kỳ lý do nào, mà bạn không thích tình huống này?
Người đam mê phần mềm vào

31
Đối với một điều, nó phá vỡ kỳ vọng của mọi người khi họ đang đọc mã. "Biểu tượng 'foo' này đang được sử dụng ở đây là gì? Tại sao tôi không thể thấy nó được định nghĩa ở đâu?"
Curt Hagenlocher

9
Nó cũng dễ bị tàn phá nếu một phiên bản Python trong tương lai bắt đầu sử dụng tên bạn đã chọn làm nội trang thực tế.
trực giác

4
Đây là một giải pháp tốt cho những thứ như chia sẻ kết nối db với các mô-đun đã nhập. Để kiểm tra sự tỉnh táo, tôi đảm bảo rằng mô-đun đã nhập xác nhận hasattr(__builtin__, "foo").
Mike Ellis

4
Đối với bất kỳ ai đọc câu trả lời này: DONT! LÀM! ĐIỀU NÀY ! Thực sự, không.
bruno desthuilliers

161

Nếu bạn cần một biến mô-đun chéo toàn cục có thể chỉ cần một biến cấp mô-đun toàn cục đơn giản là đủ.

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

Kiểm tra:

$ python b.py
# -> 1 2

Ví dụ trong thế giới thực: global_settings.py của Django (mặc dù trong cài đặt ứng dụng Django được sử dụng bằng cách nhập đối tượng django.conf.settings ).


3
Tốt hơn bởi vì nó tránh được những xung đột không gian tên có thể
BGW

Điều gì sẽ xảy ra nếu mô-đun bạn đang nhập, trong trường hợp này a.py, chứa main()? Có vấn đề gì không?
sedeh

4
@sedeh: không. Nếu a.py cũng được chạy dưới dạng tập lệnh thì hãy sử dụng if __name__=="__main__"bảo vệ trong đó để tránh chạy mã không mong muốn khi nhập.
jfs

6
Trong thế giới thực, bạn phải cẩn thận một chút với giải pháp này. Nếu một lập trình viên chọn biến 'toàn cục' của bạn bằng cách sử dụng 'từ var nhập khẩu', (hãy thử biến thể này trong c.py), họ sẽ nhận được bản sao của biến tại thời điểm nhập.
Paul Whipp

1
@PaulWhipp: sai (gợi ý: sử dụng id()để kiểm tra danh tính)
jfs

25

Tôi tin rằng có rất nhiều trường hợp mà nó có ý nghĩa và nó đơn giản hóa việc lập trình để có một số hình cầu được biết đến trên một số mô-đun (kết hợp chặt chẽ). Theo tinh thần này, tôi muốn giải thích thêm một chút về ý tưởng có một mô-đun của các khối cầu được nhập bởi các mô-đun cần tham chiếu đến chúng.

Khi chỉ có một mô-đun như vậy, tôi đặt tên nó là "g". Trong đó, tôi gán giá trị mặc định cho mọi biến mà tôi định coi là toàn cục. Trong mỗi mô-đun sử dụng bất kỳ mô-đun nào trong số chúng, tôi không sử dụng "from g import var", vì điều này chỉ dẫn đến một biến cục bộ chỉ được khởi tạo từ g tại thời điểm nhập. Tôi tạo hầu hết các tham chiếu ở dạng g.var và "g." phục vụ như một lời nhắc nhở liên tục rằng tôi đang xử lý một biến mà các mô-đun khác có thể truy cập được.

Nếu giá trị của một biến toàn cục như vậy được sử dụng thường xuyên trong một số hàm trong mô-đun, thì hàm đó có thể tạo một bản sao cục bộ: var = g.var. Tuy nhiên, điều quan trọng là phải nhận ra rằng các phép gán cho var là cục bộ và g.var toàn cục không thể được cập nhật nếu không tham chiếu rõ ràng g.var trong một phép gán.

Lưu ý rằng bạn cũng có thể có nhiều mô-đun toàn cầu như vậy được chia sẻ bởi các tập hợp con khác nhau của mô-đun của bạn để giữ cho mọi thứ được kiểm soát chặt chẽ hơn một chút. Lý do tôi sử dụng tên ngắn cho các mô-đun toàn cầu của mình là để tránh làm lộn xộn mã quá nhiều với sự xuất hiện của chúng. Chỉ với một chút kinh nghiệm, họ trở nên đủ dễ nhớ chỉ với 1 hoặc 2 ký tự.

Vẫn có thể gán gx khi x chưa được định nghĩa trong g và một mô-đun khác sau đó có thể truy cập gx Tuy nhiên, ngay cả khi trình thông dịch cho phép, cách tiếp cận này không quá minh bạch và tôi tránh nó. Vẫn có khả năng vô tình tạo một biến mới trong g do lỗi đánh máy trong tên biến cho một phép gán. Đôi khi việc kiểm tra dir (g) rất hữu ích để phát hiện ra bất kỳ tên gọi bất ngờ nào có thể phát sinh do tình cờ đó.


7
Quan sát thú vị này đã giải quyết được vấn đề của tôi: 'Tôi không sử dụng "from g import var", vì điều này chỉ dẫn đến một biến cục bộ chỉ được khởi tạo từ g tại thời điểm nhập.' Có vẻ hợp lý khi cho rằng "from..import" giống với "import" nhưng điều này không đúng.
Curtis Yallop vào

24

Định nghĩa một mô-đun (gọi nó là "globalbaz") và có các biến được định nghĩa bên trong nó. Tất cả các mô-đun sử dụng "pseudoglobal" này phải nhập mô-đun "globalbaz" và tham chiếu đến nó bằng cách sử dụng "globalbaz.var_name"

Điều này hoạt động bất kể nơi thay đổi, bạn có thể thay đổi biến trước hoặc sau khi nhập. Mô-đun đã nhập sẽ sử dụng giá trị mới nhất. (Tôi đã thử nghiệm điều này trong một ví dụ đồ chơi)

Để làm rõ, globalbaz.py trông giống như sau:

var_name = "my_useful_string"

9

Bạn có thể chuyển các hình cầu của một mô-đun này sang mô-đun khác:

Trong Mô-đun A:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

Trong Mô-đun B:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3

7

Biến toàn cục thường là một ý tưởng tồi, nhưng bạn có thể làm điều này bằng cách gán cho __builtins__:

__builtins__.foo = 'something'
print foo

Ngoài ra, bản thân các mô-đun là các biến mà bạn có thể truy cập từ bất kỳ mô-đun nào. Vì vậy, nếu bạn xác định một mô-đun được gọi là my_globals.py:

# my_globals.py
foo = 'something'

Sau đó, bạn cũng có thể sử dụng nó từ bất cứ đâu:

import my_globals
print my_globals.foo

Sử dụng các mô-đun thay vì sửa đổi __builtins__nói chung là một cách dễ dàng hơn để thực hiện toàn cầu loại này.


3
__builtins__là một đặc thù CPython, bạn thực sự không nên sử dụng nó - sử dụng tốt hơn __builtin__(hoặc builtinstrong Python3) như câu trả lời được chấp nhận chương trình
Tobias KIENZLER

5

Bạn đã có thể làm điều này với các biến cấp mô-đun. Các mô-đun đều giống nhau cho dù chúng được nhập từ mô-đun nào. Vì vậy, bạn có thể làm cho biến trở thành một biến cấp mô-đun trong bất kỳ mô-đun nào mà bạn có thể đặt nó vào và truy cập hoặc gán cho nó từ các mô-đun khác. Sẽ tốt hơn nếu gọi một hàm để đặt giá trị của biến hoặc biến nó thành thuộc tính của một đối tượng singleton nào đó. Bằng cách đó, nếu cuối cùng bạn cần chạy một số mã khi biến thay đổi, bạn có thể làm như vậy mà không làm hỏng giao diện bên ngoài của mô-đun.

Đó thường không phải là một cách tuyệt vời để thực hiện mọi việc - hiếm khi sử dụng quả cầu - nhưng tôi nghĩ đây là cách sạch sẽ nhất để làm điều đó.


3

Tôi muốn đăng một câu trả lời rằng có trường hợp biến sẽ không được tìm thấy.

Nhập tuần hoàn có thể phá vỡ hành vi của mô-đun.

Ví dụ:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

Đây là ví dụ rõ ràng, nhưng trong một cơ sở mã lớn, điều này có thể thực sự khó hiểu.


1

Điều này nghe giống như sửa đổi __builtin__không gian tên. Để làm điều đó:

import __builtin__
__builtin__.foo = 'some-value'

Không sử dụng __builtins__trực tiếp (chú ý thêm "s") - rõ ràng đây có thể là một từ điển hoặc một mô-đun. Cảm ơn ΤΖΩΤΖΙΟΥ đã chỉ ra điều này, bạn có thể tìm thêm thông tin tại đây .

Bây giờ foocó sẵn để sử dụng ở mọi nơi.

Tôi không khuyên bạn nên làm điều này nói chung, nhưng việc sử dụng nó là tùy thuộc vào lập trình viên.

Việc gán cho nó phải được thực hiện như trên, chỉ cần thiết lập foo = 'some-other-value'sẽ chỉ đặt nó trong không gian tên hiện tại.


1
Tôi nhớ (từ comp.lang.python) rằng nên tránh sử dụng nội trang trực tiếp ; thay vào đó, hãy nhập nội trang và sử dụng nó, như Curt Hagenlocher đã đề xuất.
tzot 27/09/08

1

Tôi sử dụng nó cho một vài hàm nguyên thủy được tích hợp sẵn mà tôi cảm thấy thực sự bị thiếu. Một ví dụ là hàm tìm có cùng ngữ nghĩa sử dụng như bộ lọc, ánh xạ, thu nhỏ.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

Khi điều này được chạy (ví dụ: bằng cách nhập gần điểm nhập của bạn), tất cả các mô-đun của bạn có thể sử dụng find () như thể, rõ ràng là nó đã được tích hợp sẵn.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

Lưu ý: Tất nhiên, bạn có thể làm điều này với bộ lọc và một dòng khác để kiểm tra độ dài bằng 0 hoặc với việc giảm bớt một loại dòng kỳ lạ, nhưng tôi luôn cảm thấy điều đó thật kỳ lạ.


1

Tôi có thể đạt được các biến có thể sửa đổi (hoặc có thể thay đổi ) giữa các mô-đun bằng cách sử dụng từ điển:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

Khi khởi chạy test_wait_app_up_fail, thời gian chờ thực tế là 3 giây.


1

Tôi tự hỏi liệu có thể tránh được một số nhược điểm của việc sử dụng biến toàn cục (xem ví dụ: http://wiki.c2.com/?GlobalVariablesAreBad ) bằng cách sử dụng không gian tên lớp thay vì không gian tên toàn cục / mô-đun để chuyển các giá trị của biến không . Đoạn mã sau chỉ ra rằng hai phương pháp về cơ bản giống hệt nhau. Có một lợi thế nhỏ trong việc sử dụng không gian tên lớp như được giải thích bên dưới.

Các đoạn mã sau đây cũng cho thấy rằng các thuộc tính hoặc biến có thể được tạo và xóa động trong cả không gian tên toàn cục / mô-đun và không gian tên lớp.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

Tôi gọi mô-đun này là 'tường' vì nó được sử dụng để trả lại các biến khỏi. Nó sẽ hoạt động như một không gian để tạm thời xác định các biến toàn cục và các thuộc tính toàn lớp của 'bộ định tuyến' lớp trống.

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

Mô-đun này nhập tường và xác định một chức năng duy nhất sourcefnđịnh nghĩa một thông báo và phát ra nó bằng hai cơ chế khác nhau, một thông qua hình cầu và một qua chức năng bộ định tuyến. Lưu ý rằng các biến wall.msgwall.router.messageđược định nghĩa ở đây lần đầu tiên trong không gian tên tương ứng của chúng.

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

Mô-đun này xác định một chức năng destfnsử dụng hai cơ chế khác nhau để nhận các thông báo do nguồn phát ra. Nó cho phép khả năng biến 'msg' có thể không tồn tại. destfncũng xóa các biến khi chúng đã được hiển thị.

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

Mô-đun này gọi các hàm được xác định trước đó theo trình tự. Sau lần gọi đầu tiên đến dest.destfncác biến wall.msgwall.router.msgkhông còn tồn tại.

Đầu ra từ chương trình là:

toàn cầu: Xin chào thế giới!
bộ định tuyến: Xin chào thế giới!
toàn cầu: không có
bộ định tuyến tin nhắn : không có tin nhắn

Các đoạn mã trên cho thấy mô-đun / toàn cục và cơ chế biến lớp / lớp về cơ bản giống hệt nhau.

Nếu nhiều biến được chia sẻ, ô nhiễm không gian tên có thể được quản lý bằng cách sử dụng một số mô-đun kiểu tường, ví dụ: wall1, wall2, v.v. hoặc bằng cách xác định một số lớp kiểu bộ định tuyến trong một tệp duy nhất. Cái thứ hai gọn gàng hơn một chút, vì vậy có lẽ đại diện cho một lợi thế biên khi sử dụng cơ chế biến lớ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.