İçe aktarılan modüllerde global değişkenlerin görünürlüğü


113

Bir Python betiğinde biraz duvar içe aktaran modüllerle karşılaştım. Hatayı açıklamak için elimden gelenin en iyisini yapacağım, neden onunla karşılaştığımı ve neden problemimi çözmek için bu özel yaklaşımı bağladığımı (bunu bir saniye içinde açıklayacağım):

Bu yardımcı modülün içe aktarılacağı ad alanında tanımlanan varlıklara atıfta bulunan bazı yardımcı işlevler / sınıflar tanımladığım bir modülüm olduğunu varsayalım ("a" böyle bir varlık olsun):

modül1:

def f():
    print a

Ve sonra, bu yardımcı programları içe aktarmak istediğim "a" nın tanımlandığı ana programım var:

import module1
a=3
module1.f()

Programın yürütülmesi aşağıdaki hatayı tetikleyecektir:

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

Geçmişte (iki gün önce) benzer sorular sorulmuş ve birkaç çözüm önerilmiştir, ancak bunların benim gereksinimlerime uyduğunu gerçekten düşünmüyorum. İşte benim özel bağlamım:

MySQL veritabanı sunucusuna bağlanan ve bir GUI ile verileri görüntüleyen / değiştiren bir Python programı yapmaya çalışıyorum. Temizlik uğruna, MySQL ile ilgili yardımcı / yardımcı işlevler grubunu ayrı bir dosyada tanımladım. Ancak hepsi Başlangıçta tanımlanmış olan ortak bir değişken var içeride kamu hizmetleri modülü ve hangi imleç MySQLdb modülünden nesnesi. Daha sonra, imleç nesnesinin (db sunucusuyla iletişim kurmak için kullanılan) ana modülde tanımlanması gerektiğini fark ettim , böylece hem ana modül hem de ona aktarılan her şey bu nesneye erişebilir.

Nihai sonuç şuna benzer bir şey olacaktır:

utilities_module.py:

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

Ve ana modülüm:

program.py:

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

Ve sonra, herhangi bir yardımcı program işlevini çağırmaya çalışır çalışmaz, yukarıda bahsedilen "genel ad tanımlanmadı" hatasını tetikliyor.

Özel bir öneri, yardımcı programlar dosyasında aşağıdaki gibi bir "from program import cur" ifadesinin bulunmasıydı:

utilities_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 *

Ancak bu döngüsel içe aktarım veya bunun gibi bir şey ve sonuç olarak, o da çöküyor. Yani sorum şu:

Ana modülde tanımlanan "cur" nesnesini, içine aktarılan yardımcı fonksiyonlar tarafından nasıl görünür yapabilirim?

Zaman ayırdığınız için teşekkürler ve çözüm başka bir yere gönderildiyse en derin özür dilerim. Cevabı kendim bulamıyorum ve kitabımda başka numaram yok.


1
Güncellemenize göre: Muhtemelen zaten tek bir paylaşılan imleç istemiyorsunuzdur. Tek bir paylaşılan bağlantı evet, ancak imleçler ucuzdur ve aynı anda birden fazla imleci canlı tutmak için genellikle iyi nedenler vardır (örneğin, bunun yerine fetch_alliki listeden geçmek zorunda kalmadan ve yinelemeye gerek kalmadan ikisini kilit adımda yineleyebilirsiniz. veya iki farklı iş parçacığı / yeşillik / geri arama zinciri / veritabanını çakışmadan kullanan her ne olursa olsun).
abarnert

Neyse, paylaşımına istediğini, burada cevap taşımak olduğunu düşünüyorum db(ve cur, eğer ısrar varsa) de bunun ayrı bir modüle programve utilities_moduleburadan içe. Bu şekilde döngüsel bağımlılıkları (programın içe aktardığı modüllerden içe aktarma) ve bunlarla birlikte gelen kafa karışıklığını elde edemezsiniz.
abarnert

Yanıtlar:


245

Python'daki globaller , tüm modüller için değil, bir modül için geneldir . (Pek çok insanın kafası karışır, çünkü örneğin C'de global, siz açıkça yapmadığınız sürece tüm uygulama dosyalarında aynıdır static.)

Gerçek kullanım durumunuza bağlı olarak, bunu çözmenin farklı yolları vardır.


Bu yola girmeden önce, kendinize bunun gerçekten küresel olması gerekip gerekmediğini sorun. Belki de gerçekten fözgür bir işlevden ziyade örnek yöntemi olan bir sınıf istiyorsunuz ? O zaman şöyle bir şey yapabilirsin:

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

Eğer gerçekten bir global istiyorsanız, ancak bu sadece tarafından kullanılmak için oradaysa module1, o modülde ayarlayın.

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

Öte yandan, açok sayıda modül tarafından paylaşılıyorsa, başka bir yere koyun ve herkesin içeri aktarmasını sağlayın:

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

… Ve module1.py'de:

import shared_stuff
def f():
    print shared_stuff.a

Etmeyin bir kullanmak fromdeğişken sabit olması amaçlanmıştır sürece ithalat. içe aktarma sırasında atıfta bulunulan her şeye başlatılan from shared_stuff import ayeni bir adeğişken yaratır shared_stuff.ave bu yeni adeğişken, atamalarından etkilenmez shared_stuff.a.


Veya, bir yerleşik gibi her yerde gerçekten küresel olmasına gerçekten ihtiyaç duyduğunuz nadir durumlarda, onu yerleşik modüle ekleyin. Kesin ayrıntılar Python 2.x ve 3.x arasında farklılık gösterir. 3.x'te şu şekilde çalışır:

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

12
Güzel ve kapsamlı.
nazik

Cevabınız için teşekkürler. Ana modülde tanımlanan değişkenlerin gerçekten global olmadığı gerçeğini aşamasam da, import shared_stuff yaklaşımını deneyeceğim - Programın neresinden olursa olsun herkes için gerçekten erişilebilir bir isim yapmanın bir yolu yok mu? ?
Nubarke

1
Herhangi bir programdan "herkes tarafından erişilebilir" bir şeye sahip olmak, python'un zenine , özellikle "Açıkça örtük olmaktan daha iyidir", tamamen aykırıdır . Python çok iyi düşünülmüş bir oo tasarımına sahiptir ve eğer onu iyi kullanırsanız, python kariyerinizin geri kalanına, global anahtar kelimeyi bir daha kullanmanıza gerek kalmadan devam edebilirsiniz.
Bi Rico

1
GÜNCELLEME: TEŞEKKÜRLER! Shared_stuff yaklaşımı gayet iyi çalıştı, bununla başka sorunların üstesinden gelebilirim.
Nubarke

1
@DanielArmengod: Gerçekten ihtiyacınız olursa diye yerleşik yanıtı ekledim. (Ve daha fazla ayrıntıya ihtiyacınız varsa, SO'da arama yapın; yerleşiklere nasıl düzgün bir şekilde malzeme ekleneceğine dair en az iki soru var.) Ancak, Bi Rico'nun dediği gibi, neredeyse kesinlikle gerçekten ihtiyacınız yok ya da istemiyorsunuz.
abarnert

9

Geçici bir çözüm olarak, dış katmanda bunun gibi ortam değişkenlerini ayarlamayı düşünebilirsiniz.

main.py:

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

mymodule.py:

import os

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

Ek bir önlem olarak MYVAL modül içinde tanımlanmadığında durumu ele alın.


5

Bir işlev, içinde tanımlandığı modülün globallerini kullanır . a = 3Örneğin, ayarlamak yerine, ayarlamalısınız module1.a = 3. Yani, curglobal bir giriş olarak mevcut olmasını istiyorsanız utilities_module, ayarlayın utilities_module.cur.

Daha iyi bir çözüm: küresel kullanmayın. İhtiyaç duyduğunuz değişkenleri ihtiyaç duyan işlevlere aktarın veya tüm verileri bir araya getirmek için bir sınıf oluşturun ve örneği başlatırken iletin.


'Modül1 içe aktar' yazmak yerine, eğer kullanıcı 'modül1'den içe aktarma f' yazmış olsaydı, o zaman f main.py'nin genel ad alanına gelirdi. Şimdi main.py'de f () kullanırsak, o zaman a = 3 ve f (fonksiyon tanımı) her ikisi de main'in global ad alanında olur. Bu bir çözüm mü? Eğer yanılıyorsam, lütfen beni bu konudaki herhangi bir makaleye yönlendirebilir misiniz lütfen
değişken

Yukarıdaki yaklaşımı kullandım ve globalleri kullanan sınıfları başlatırken globalleri argüman olarak geçtim. Bu tamamdı, ancak bir süre sonra kodun Sonarqube kod kontrolünü etkinleştirdik ve bu, işlevlerin çok fazla argümanı olduğundan şikayet etti. Bu yüzden başka bir çözüm aramalıydık. Şimdi, ihtiyaç duyan her modül tarafından okunan ortam değişkenlerini kullanıyoruz. Gerçekten OOP uyumlu değil ama hepsi bu. Bu, yalnızca kod yürütme sırasında küreseller değişmediğinde çalışır.
rimetnac

5

Bu gönderi, karşılaştığım Python davranışı için sadece bir gözlem. Belki yukarıda okuduğunuz tavsiyeler, aşağıda yaptığım şeyin aynısını yaptıysanız işinize yaramıyor.

Yani, global / paylaşılan değişkenleri içeren bir modülüm var (yukarıda önerildiği gibi):

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

Daha sonra paylaşılan şeyleri içe aktaran ana modüle sahiptim:

import sharedstuff as shared

ve bu dizileri fiilen dolduran diğer bazı modüller. Bunlar ana modül tarafından çağrılır. Bu diğer modüllerden çıkarken, dizilerin dolu olduğunu açıkça görebiliyorum. Ancak bunları ana modülde okurken boştu. Bu benim için oldukça garipti (evet, Python'da yeniyim). Ancak, ana modüldeki sharedstuff.py dosyasını içe aktarma yöntemimi değiştirdiğimde:

from globals import *

işe yaradı (diziler dolduruldu).

Sadece söylüyorum'


2

Bu özel soruna en kolay çözüm, modül içerisine, imleci modüle global bir değişkene depolayacak başka bir işlev eklemek olabilirdi. O zaman diğer tüm işlevler de kullanabilir.

modül1:

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

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

ana program:

import module1

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

2

Globals modüle özel olduğundan, aşağıdaki işlevi tüm içe aktarılan modüllere ekleyebilir ve ardından bunu kullanmak için kullanabilirsiniz:

  • Tekil değişkenleri (sözlük biçiminde) bunlar için global olarak ekleyin
  • Ana modül globallerinizi ona aktarın .

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

O zaman mevcut küreselleri aktarmanız gereken tek şey:

içe aktarma modülü

module.addglobals (globals ())


1

Yukarıdaki cevaplarda görmediğim için, basit geçici çözümümü ekleyeceğimi düşündüm, bu sadece global_dictçağıran modülün globallerini gerektiren işleve bir argüman eklemek ve ardından arama sırasında dikteyi işleve geçirmek için; Örneğin:

# 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

Bunu yapmanın OOP yolu, modülünüzü bir dizi bağlantısız yöntem yerine bir sınıf yapmak olacaktır. Ardından __init__, modül yöntemlerinde kullanılmak üzere çağırıcıdan değişkenleri ayarlamak için veya bir ayarlayıcı yöntemini kullanabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.