Khả năng hiển thị của các biến toàn cục trong các mô-đun đã nhập


112

Tôi đã gặp một chút rắc rối khi nhập các mô-đun trong một tập lệnh Python. Tôi sẽ cố gắng hết sức để mô tả lỗi, lý do tại sao tôi gặp phải nó và tại sao tôi buộc cách tiếp cận cụ thể này để giải quyết vấn đề của mình (tôi sẽ mô tả trong giây lát):

Giả sử tôi có một mô-đun trong đó tôi đã xác định một số chức năng / lớp tiện ích, tham chiếu đến các thực thể được xác định trong không gian tên mà mô-đun phụ trợ này sẽ được nhập vào (giả sử "a" là một thực thể như vậy):

mô-đun 1:

def f():
    print a

Và sau đó tôi có chương trình chính, trong đó "a" được định nghĩa, mà tôi muốn nhập các tiện ích đó vào:

import module1
a=3
module1.f()

Việc thực thi chương trình sẽ gây ra lỗi sau:

Traceback (most recent call last):
  File "Z:\Python\main.py", line 10, in <module>
    module1.f()
  File "Z:\Python\module1.py", line 3, in f
    print a
NameError: global name 'a' is not defined

Những câu hỏi tương tự đã được hỏi trong quá khứ (hai ngày trước, d'uh) và một số giải pháp đã được đề xuất, tuy nhiên tôi không thực sự nghĩ rằng những giải pháp này phù hợp với yêu cầu của tôi. Đây là bối cảnh cụ thể của tôi:

Tôi đang cố gắng tạo một chương trình Python kết nối với máy chủ cơ sở dữ liệu MySQL và hiển thị / sửa đổi dữ liệu bằng GUI. Vì lợi ích của sự sạch sẽ, tôi đã xác định một loạt các chức năng phụ trợ / tiện ích liên quan đến MySQL trong một tệp riêng biệt. Tuy nhiên, tất cả chúng đều có một biến chung, mà tôi đã xác định ban đầu bên trong mô-đun tiện ích và là đối tượng con trỏ từ mô-đun MySQLdb. Sau đó, tôi nhận ra rằng đối tượng con trỏ (được sử dụng để giao tiếp với máy chủ db) nên được xác định trong mô-đun chính, để cả mô-đun chính và bất kỳ thứ gì được nhập vào nó đều có thể truy cập đối tượng đó.

Kết quả cuối cùng sẽ như thế này:

tiện ích_module.py:

def utility_1(args):
    code which references a variable named "cur"
def utility_n(args):
    etcetera

Và mô-đun chính của tôi:

program.py:

import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

Và sau đó, ngay khi tôi cố gắng gọi bất kỳ chức năng tiện ích nào, nó sẽ gây ra lỗi "tên toàn cục không được xác định" nói trên.

Một gợi ý cụ thể là có câu lệnh "from program import cur" trong tệp tiện ích, chẳng hạn như sau:

tiện ích_module.py:

from program import cur
#rest of function definitions

program.py:

import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

Nhưng đó là nhập theo chu kỳ hoặc một cái gì đó tương tự và điểm mấu chốt, nó cũng bị lỗi. Vì vậy, câu hỏi của tôi là:

Làm thế quái nào mà tôi có thể làm cho đối tượng "cur", được xác định trong mô-đun chính, hiển thị với các chức năng phụ trợ được nhập vào nó?

Cảm ơn thời gian của bạn và lời xin lỗi sâu sắc nhất của tôi nếu giải pháp đã được đăng ở nơi khác. Tôi chỉ không thể tự mình tìm ra câu trả lời và tôi không còn thủ thuật nào trong cuốn sách của mình.


1
Dựa trên cập nhật của bạn: Dù sao thì bạn cũng không muốn có một con trỏ được chia sẻ. Một kết nối được chia sẻ duy nhất , vâng, nhưng con trỏ rẻ và thường có nhiều lý do chính đáng để có nhiều con trỏ hoạt động cùng một lúc (ví dụ: vì vậy bạn có thể lặp lại hai trong số chúng trong bước khóa thay vì phải fetch_alllặp đi lặp lại hai danh sách hoặc chỉ để bạn có thể có hai luồng khác nhau / greenlets / callback-chain / bất cứ thứ gì bằng cách sử dụng cơ sở dữ liệu mà không có xung đột).
abarnert

Dù sao, bất cứ điều gì bạn muốn chia sẻ, tôi nghĩ câu trả lời ở đây là chuyển db(và cur, nếu bạn nhấn mạnh) vào một mô-đun riêng biệt programutilities_modulenhập nó từ đó. Bằng cách đó, bạn không nhận được các phụ thuộc vòng tròn (nhập chương trình từ các mô-đun mà chương trình nhập) và sự nhầm lẫn đi kèm với chúng.
abarnert

Câu trả lời:


244

Globals trong Python là toàn cầu cho một mô-đun , không phải trên tất cả các mô-đun. (Nhiều người nhầm lẫn với điều này, bởi vì trong C, một toàn cầu giống nhau trên tất cả các tệp triển khai trừ khi bạn tạo nó một cách rõ ràng static.)

Có nhiều cách khác nhau để giải quyết vấn đề này, tùy thuộc vào trường hợp sử dụng thực tế của bạn.


Trước khi đi theo con đường này, hãy tự hỏi bản thân xem liệu điều này có thực sự cần phải mang tính toàn cầu hay không. Có lẽ bạn thực sự muốn một lớp, với fnhư một phương thức thể hiện, thay vì chỉ là một hàm miễn phí? Sau đó, bạn có thể làm một cái gì đó như sau:

import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()

Nếu bạn thực sự muốn một toàn cầu, nhưng nó chỉ ở đó để được sử dụng bởi module1, hãy đặt nó trong mô-đun đó.

import module1
module1.a=3
module1.f()

Mặt khác, nếu ađược chia sẻ bởi nhiều mô-đun, hãy đặt nó ở một nơi khác và yêu cầu mọi người nhập nó:

import shared_stuff
import module1
shared_stuff.a = 3
module1.f()

… Và trong module1.py:

import shared_stuff
def f():
    print shared_stuff.a

Không sử dụng fromnhập trừ khi biến được dự định là một hằng số. from shared_stuff import asẽ tạo một abiến mới được khởi tạo cho bất cứ thứ gì shared_stuff.ađược tham chiếu tại thời điểm nhập và abiến mới này sẽ không bị ảnh hưởng bởi các phép gán cho shared_stuff.a.


Hoặc, trong trường hợp hiếm hoi mà bạn thực sự cần nó thực sự mang tính toàn cầu ở mọi nơi, như một bản nội trang, hãy thêm nó vào mô-đun nội trang. Các chi tiết chính xác khác nhau giữa Python 2.x và 3.x. Trong 3.x, nó hoạt động như sau:

import builtins
import module1
builtins.a = 3
module1.f()

12
Đẹp và toàn diện.
kindall

Cảm ơn câu trả lời của bạn. Tôi sẽ thử phương pháp import shared_stuff, mặc dù tôi không thể vượt qua thực tế là các biến được xác định trong mô-đun chính không thực sự toàn cầu - Không có cách nào để làm cho một cái tên thực sự có thể truy cập được đối với mọi người, từ bất cứ đâu trong chương trình ?
Nubarke

1
Có một cái gì đó "có thể truy cập được cho tất cả mọi người, từ bất kỳ chương trình nào" đi ngược lại rất nhiều so với zen của python , cụ thể là "Rõ ràng tốt hơn là ẩn". Python có một thiết kế rất tốt, và nếu bạn sử dụng nó tốt, bạn có thể tiếp tục phần còn lại của sự nghiệp python của mình mà không cần sử dụng lại từ khóa toàn cầu.
Bi Rico

1
CẬP NHẬT: CẢM ƠN! Phương pháp shared_stuff hoạt động tốt, với điều này, tôi nghĩ rằng tôi có thể giải quyết bất kỳ vấn đề nào khác.
Nubarke

1
@DanielArmengod: Tôi đã thêm câu trả lời bên trong, trong trường hợp bạn thực sự cần. (Và nếu bạn cần thêm chi tiết, hãy tìm kiếm SO; có ít nhất hai câu hỏi về cách thêm nội dung đúng cách.) Nhưng, như Bi Rico nói, bạn gần như chắc chắn không thực sự cần hoặc không muốn.
abarnert

9

Để giải quyết vấn đề, bạn có thể cân nhắc việc đặt các biến môi trường ở lớp ngoài, như thế này.

main.py:

import os
os.environ['MYVAL'] = str(myintvariable)

mymodule.py:

import os

myval = None
if 'MYVAL' in os.environ:
    myval = os.environ['MYVAL']

Để đề phòng thêm, hãy xử lý trường hợp khi MYVAL không được xác định bên trong mô-đun.


5

Một hàm sử dụng toàn cầu của mô-đun mà nó được định nghĩa . a = 3Ví dụ: thay vì thiết lập , bạn nên thiết lập module1.a = 3. Vì vậy, nếu bạn muốn curcó sẵn dưới dạng toàn cầu utilities_module, hãy đặt utilities_module.cur.

Một giải pháp tốt hơn: không sử dụng hình cầu. Truyền các biến bạn cần vào các hàm cần nó hoặc tạo một lớp để gói tất cả dữ liệu lại với nhau và chuyển nó khi khởi tạo phiên bản.


Thay vì viết 'import module1', nếu người dùng đã viết 'from module1 import f', thì f sẽ nằm trong vùng tên chung của main.py. Bây giờ trong main.py nếu chúng ta sử dụng f (), thì vì a = 3 và f (định nghĩa hàm) đều nằm trong không gian tên toàn cục của main. Đây có phải là một giải pháp? Nếu tôi sai thì xin vui lòng bạn có thể trực tiếp tôi để bất kỳ bài viết về chủ đề này xin vui lòng
biến

Tôi đã sử dụng cách tiếp cận ở trên và chuyển các khối cầu làm đối số khi khởi tạo các lớp đang sử dụng các khối cầu. Điều này không sao cả, nhưng đôi khi sau đó, chúng tôi đã kích hoạt kiểm tra mã Sonarqube của mã và điều này phàn nàn rằng các hàm có quá nhiều đối số. Vì vậy, chúng tôi đã phải tìm kiếm một giải pháp khác. Bây giờ chúng tôi sử dụng các biến môi trường được đọc bởi mỗi mô-đun cần chúng. Nó không thực sự tuân thủ OOP nhưng đó là nó. Điều này chỉ hoạt động khi các hình cầu không thay đổi trong quá trình thực thi mã.
rimetnac

5

Bài đăng này chỉ là một quan sát cho hành vi Python mà tôi gặp phải. Có thể những lời khuyên bạn đọc ở trên không phù hợp với bạn nếu bạn đã làm điều tương tự như tôi đã làm dưới đây.

Cụ thể, tôi có một mô-đun chứa các biến toàn cục / được chia sẻ (như được đề xuất ở trên):

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

Sau đó, tôi có mô-đun chính nhập nội dung được chia sẻ với:

import sharedstuff as shared

và một số mô-đun khác thực sự phổ biến các mảng này. Chúng được gọi bởi mô-đun chính. Khi thoát khỏi các mô-đun khác này, tôi có thể thấy rõ rằng các mảng đã được điền. Nhưng khi đọc lại chúng trong mô-đun chính, chúng trống rỗng. Điều này khá lạ đối với tôi (tốt, tôi mới làm quen với Python). Tuy nhiên, khi tôi thay đổi cách nhập sharedstuff.py trong mô-đun chính thành:

from globals import *

nó đã hoạt động (các mảng đã được điền).

Just sayin '


2

Giải pháp đơn giản nhất cho vấn đề cụ thể này là thêm một hàm khác vào trong mô-đun đã lưu con trỏ trong một biến chung cho mô-đun. Sau đó, tất cả các chức năng khác cũng có thể sử dụng nó.

mô-đun 1:

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

def method(some, args):
    global cursor
    do_stuff(cursor, some, args)

chương trình chính:

import module1

cursor = get_a_cursor()
module1.setCursor(cursor)
module1.method()

2

Vì toàn cầu là mô-đun cụ thể, bạn có thể thêm chức năng sau vào tất cả các mô-đun đã nhập và sau đó sử dụng nó để:

  • Thêm các biến số ít (ở định dạng từ điển) dưới dạng toàn cầu cho những
  • Chuyển toàn cầu mô-đun chính của bạn sang nó.

addglobals = lambda x: Gloals (). update (x)

Sau đó, tất cả những gì bạn cần truyền cho các hình cầu hiện tại là:

nhập mô-đun

module.addglobals (global ())


1

Vì tôi chưa thấy nó trong các câu trả lời ở trên, tôi nghĩ tôi sẽ thêm cách giải quyết đơn giản của mình, đó là chỉ thêm một global_dictđối số vào hàm yêu cầu các hình cầu của mô-đun đang gọi, và sau đó chuyển dict vào hàm khi gọi; ví dụ:

# external_module
def imported_function(global_dict=None):
    print(global_dict["a"])


# calling_module
a = 12
from external_module import imported_function
imported_function(global_dict=globals())

>>> 12

0

Cách OOP thực hiện điều này là biến mô-đun của bạn thành một lớp thay vì một tập hợp các phương thức không liên kết. Sau đó, bạn có thể sử dụng __init__hoặc một phương thức setter để đặt các biến từ trình gọi để sử dụng trong các phương thức mô-đun.

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.