Çapraz modül değişkeni nasıl yapılır?


122

__debug__Her modül etkilediğinden değişken kısmen kullanışlıdır. Aynı şekilde çalışan başka bir değişken oluşturmak istersem, bunu nasıl yaparım?

Değişkenin (hadi orijinal olalım ve ona 'foo' diyelim) gerçekten global olması gerekmez, yani bir modülde foo'yu değiştirirsem, diğerlerinde güncellenir. Diğer modülleri içe aktarmadan önce foo ayarlayabilsem ve bunun için aynı değeri görürlerse sorun olmaz.

Yanıtlar:


114

Bu çözümü hiçbir şekilde, biçimde veya biçimde onaylamıyorum. Ancak __builtin__modüle bir değişken eklerseniz __builtin__, varsayılan olarak hepsini içeren diğer herhangi bir modülden bir globalmiş gibi erişilebilir olacaktır .

a.py şunu içerir:

print foo

b.py şunu içerir:

import __builtin__
__builtin__.foo = 1
import a

Sonuç, "1" yazdırılır.

Düzenleme:__builtin__ modül yerel sembolü olarak kullanılabilir __builtins__- bu cevapların ikisi arasındaki çelişkinin nedeni bu. Ayrıca python3'te __builtin__olarak yeniden adlandırıldığını unutmayın builtins.


2
Bu durumu beğenmemek için herhangi bir sebep var mı?
Software Enthusiastic

31
Öncelikle, insanların kodu okurken beklentilerini kırıyor. "Burada kullanılan bu 'foo' sembolü nedir? Nerede tanımlandığını neden göremiyorum?"
Curt Hagenlocher

9
Ayrıca, Python'un gelecekteki bir sürümü gerçek bir yerleşik olarak seçtiğiniz adı kullanmaya başlarsa, ortalığı kasıp kavurabilir.
intuited

4
Bu, içe aktarılan modüllerle bir db bağlantısını paylaşmak gibi şeyler için güzel bir çözümdür. Bir akıl sağlığı kontrolü olarak, ithal edilen modülün doğrulandığından emin oluyorum hasattr(__builtin__, "foo").
Mike Ellis

4
Bu cevabı okuyan herkes için: DONT! YAPMAK ! BU ! Gerçekten yapma.
bruno desthuilliers

161

Global bir çapraz modül değişkenine ihtiyacınız varsa, belki sadece basit global modül düzeyinde değişken yeterli olacaktır.

a.py:

var = 1

b.py:

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

c.py:

import a
a.var = 2

Ölçek:

$ python b.py
# -> 1 2

Gerçek dünya örneği: Django'nun global_settings.py (Django uygulamalarında ayarlar nesneyi içe aktararak kullanılır django.conf.settings).


3
Olası ad alanı çatışmalarını önlediği için daha iyi
bgw

Ya içe aktardığınız modül bu durumda a.pyiçeriyorsa main()? Önemli mi?
sedeh

4
@sedeh: hayır. A.py ayrıca bir komut dosyası olarak da çalıştırılıyorsa if __name__=="__main__", içe aktarma sırasında beklenmeyen kodların çalıştırılmasını önlemek için içinde koruma kullanın .
jfs

6
Gerçek dünyada, bu çözüme biraz dikkat etmelisiniz. Bir programcı "global" değişkeninizi "içe aktarılmış bir değişken" kullanarak alırsa, (bu değişkeni c.py'de deneyin) içe aktarma sırasında değişkenin bir kopyasını elde eder.
Paul Whipp

1
@PaulWhipp: yanlış (ipucu: id()kimliği kontrol etmek için kullanın )
jfs

25

Mantıklı olduğu birçok koşul olduğuna ve birkaç (sıkı bir şekilde bağlı) modülde bilinen bazı globallere sahip olmak için programlamayı basitleştirdiğine inanıyorum. Bu ruhla, onlara referans vermesi gereken modüller tarafından ithal edilen bir küresel modülüne sahip olma fikrini biraz detaylandırmak istiyorum.

Böyle tek bir modül olduğunda, ona "g" adını veriyorum. İçinde, global olarak ele almayı düşündüğüm her değişken için varsayılan değerler atıyorum. Bunlardan herhangi birini kullanan her modülde, "from g import var" kullanmıyorum, çünkü bu yalnızca içe aktarma sırasında sadece g'den başlatılan yerel bir değişkenle sonuçlanıyor. Çoğu referansı g.var ve "g" biçiminde yapıyorum. diğer modüller tarafından potansiyel olarak erişilebilir olan bir değişkenle uğraştığımı sürekli olarak hatırlatıyor.

Böyle bir global değişkenin değeri bir modüldeki bazı işlevlerde sıkça kullanılacaksa, bu işlev yerel bir kopya yapabilir: var = g.var. Ancak, var atamalarının yerel olduğunu ve genel g.var'ın bir atamada g.var'a açıkça başvurmadan güncellenemeyeceğini anlamak önemlidir.

İşleri biraz daha sıkı bir şekilde kontrol altında tutmak için modüllerinizin farklı alt kümeleri tarafından paylaşılan birden fazla bu tür global modülüne sahip olabileceğinizi unutmayın. Globals modüllerim için kısa isimler kullanmamın nedeni, bunların oluşumuyla kodu çok fazla karıştırmaktan kaçınmaktır. Sadece biraz deneyimle, sadece 1 veya 2 karakterle yeterince anımsatıcı hale gelirler.

Örneğin, x g'de tanımlanmamışken gx'e bir atama yapmak hala mümkündür ve bu durumda farklı bir modül gx'e erişebilir Ancak, yorumlayıcı izin verse bile, bu yaklaşım o kadar şeffaf değildir ve bundan kaçınıyorum o. Bir atama için değişken adındaki bir yazım hatası sonucu g'de yanlışlıkla yeni bir değişken oluşturma olasılığı hala vardır. Bazen dir (g) 'nin incelenmesi, bu tür bir kazayla ortaya çıkmış olabilecek sürpriz isimleri keşfetmek için yararlıdır.


7
Bu ilginç gözlem sorunumu çözdü: "" from g import var "ı kullanmıyorum, çünkü bu yalnızca içe aktarma sırasında g'den başlatılan yerel bir değişkenle sonuçlanıyor." "From..import" un "import" ile aynı olduğunu varsaymak mantıklı görünüyor, ancak bu doğru değil.
Curtis Yallop

24

Bir modül tanımlayın (buna "globalbaz" deyin) ve içinde tanımlanmış değişkenlere sahip olun. Bu "sözde küresel" kullanan tüm modüller "globalbaz" modülünü içe aktarmalı ve "globalbaz.var_name" kullanarak ona başvurmalıdır.

Bu, değişikliğin olduğu yere bakılmaksızın çalışır, değişkeni içe aktarmadan önce veya sonra değiştirebilirsiniz. İçe aktarılan modül en son değeri kullanacaktır. (Bunu bir oyuncak örneğinde test ettim)

Açıklama için, globalbaz.py şuna benzer:

var_name = "my_useful_string"

9

Bir modülün globallerini diğerine geçirebilirsiniz:

Modül A'da:

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

Modül B'de:

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

7

Global değişkenler genellikle kötü bir fikirdir, ancak bunu aşağıdakilere atayarak yapabilirsiniz __builtins__:

__builtins__.foo = 'something'
print foo

Ayrıca modüllerin kendisi, herhangi bir modülden erişebileceğiniz değişkenlerdir. Yani şu adında bir modül tanımlarsanız my_globals.py:

# my_globals.py
foo = 'something'

O zaman bunu her yerde kullanabilirsin:

import my_globals
print my_globals.foo

__builtins__Değişiklik yapmak yerine modülleri kullanmak , genellikle bu türden globaller yapmanın daha temiz bir yoludur.


3
__builtins__bir CPython özelliği, onu gerçekten kullanmamalısınız - kabul edilen yanıtın gösterdiği gibi __builtin__(veya builtinsPython3'te) daha iyi kullanın
Tobias Kienzler

5

Bunu zaten modül seviyesindeki değişkenlerle yapabilirsiniz. Modüller, hangi modülden aktarılırsa aktarılsın aynıdır. Böylece, değişkeni, onu yerleştirmenin mantıklı olduğu modülde modül düzeyinde bir değişken haline getirebilir ve ona erişebilir veya diğer modüllerden ona atayabilirsiniz. Değişkenin değerini ayarlamak için bir işlev çağırmak veya onu bir tekil nesnenin özelliği yapmak daha iyi olacaktır. Bu şekilde, değişken değiştiğinde bir kod çalıştırmanız gerekirse, bunu modülünüzün harici arayüzünü bozmadan yapabilirsiniz.

Genelde bir şeyler yapmanın harika bir yolu değil - küreselleri kullanmak nadiren öyle - ama bence bunu yapmanın en temiz yolu bu.


3

Değişkenin bulunamayacağı bir durum olduğuna dair bir cevap göndermek istedim.

Döngüsel içe aktarma, modül davranışını bozabilir.

Örneğin:

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

Bu örnekte açık olmalı, ancak büyük bir kod tabanında bu gerçekten kafa karıştırıcı olabilir.


1

Bu, __builtin__isim boşluğunu değiştirmeye benziyor . Yapmak için:

import __builtin__
__builtin__.foo = 'some-value'

__builtins__Doğrudan kullanmayın (fazladan "s" ye dikkat edin) - görünüşe göre bu bir sözlük veya bir modül olabilir. Bunu işaret ettiği için ΤΖΩΤΖΙΟΥ teşekkürler, burada daha fazlasını bulabilirsiniz .

Artık fooher yerde kullanılabilir.

Genel olarak bunu yapmanızı önermiyorum, ancak bunun kullanımı programcıya bağlıdır.

Ona atama işlemi yukarıdaki gibi yapılmalıdır, sadece ayarlama foo = 'some-other-value'onu sadece mevcut isim alanında ayarlayacaktır.


1
(Comp.lang.python'dan) doğrudan yerleşiklerin kullanılmasından kaçınılması gerektiğini hatırlıyorum ; bunun yerine yerleşik içe aktarın ve Curt Hagenlocher'in önerdiği gibi bunu kullanın.
tzot

1

Bunu, gerçekten eksik olduğunu hissettiğim birkaç yerleşik ilkel işlev için kullanıyorum. Buna bir örnek, filtre, eşleme, azaltma ile aynı kullanım semantiğine sahip bir bul işlevidir.

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

import __builtin__
__builtin__.find = builtin_find

Bu çalıştırıldıktan sonra (örneğin, giriş noktanızın yakınında içe aktararak) tüm modülleriniz find () işlevini açık bir şekilde yerleşik gibi kullanabilir.

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

Not: Bunu elbette, sıfır uzunluğu test etmek için filtre ve başka bir çizgi ile veya bir tür garip çizgide küçültme ile yapabilirsiniz, ancak ben her zaman bunun tuhaf olduğunu hissettim.


1

Bir sözlük kullanarak modüller arası değiştirilebilir (veya değiştirilebilir ) değişkenler elde edebilirim :

# 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

Başlarken test_wait_app_up_fail, gerçek zaman aşımı süresi 3 saniyedir.


1

Değişkenlerin değerlerini geçirmek için bir global / modül ad alanı yerine bir sınıf ad alanı kullanarak global değişkenleri kullanmanın bazı dezavantajlarından kaçınmanın mümkün olup olmadığını merak ettim (bkz. Http://wiki.c2.com/?GlobalVariablesAreBad ) . Aşağıdaki kod, iki yöntemin temelde aynı olduğunu gösterir. Sınıf ad alanlarını aşağıda açıklandığı gibi kullanmanın küçük bir avantajı vardır.

Aşağıdaki kod parçaları ayrıca özniteliklerin veya değişkenlerin dinamik olarak oluşturulabileceğini ve hem global / modül ad alanlarında hem de sınıf ad alanlarında silinebileceğini gösterir.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

Değişkenleri geri döndürmek için kullanıldığı için bu modüle 'duvar' diyorum. Global değişkenleri ve boş 'yönlendirici' sınıfının sınıf çapında özniteliklerini geçici olarak tanımlamak için bir alan görevi görecektir.

source.py

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

Bu modül duvarı içe aktarır ve sourcefnbir mesajı tanımlayan ve bunu biri küreseller ve diğeri yönlendirici işlevi aracılığıyla olmak üzere iki farklı mekanizma ile yayınlayan tek bir işlevi tanımlar. Değişkenlerin wall.msgve wall.router.messageburada ilk kez kendi ad alanlarında tanımlandığını unutmayın.

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'

Bu modül, destfnkaynak tarafından gönderilen mesajları almak için iki farklı mekanizmayı kullanan bir işlevi tanımlar . 'Msg' değişkeninin mevcut olmaması olasılığına izin verir. destfnayrıca görüntülendikten sonra değişkenleri siler.

main.py

import source, dest

source.sourcefn()

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

Bu modül, önceden tanımlanmış işlevleri sırayla çağırır. dest.destfnDeğişkenlere ilk çağrıdan sonra wall.msgve wall.router.msgartık yok.

Programın çıktısı:

global: Merhaba dünya!
yönlendirici: Merhaba dünya!
genel: mesaj
yönlendirici yok: mesaj yok

Yukarıdaki kod parçaları, modül / global ve sınıf / sınıf değişken mekanizmalarının temelde aynı olduğunu gösterir.

Çok sayıda değişken paylaşılacaksa, ad alanı kirliliği, duvar1, duvar2 vb. Gibi birkaç duvar tipi modül kullanılarak veya tek bir dosyada çeşitli yönlendirici tipi sınıflar tanımlanarak yönetilebilir. İkincisi biraz daha derli topludur, bu nedenle belki de sınıf değişken mekanizmasının kullanımı için marjinal bir avantajı temsil eder.

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.