2013-05-02 64 views
3

我有一個應用程序在Postgres上運行& Mysql。每個程序檢查以確定數據庫,然後以db_util或db_util的形式導入postgres_db作爲db_util或mysql_dt。當主引用db_util中的代碼都運行良好時,但如果導入了類,則未定義對db_util的引用。Python導入爲全局名稱未定義

我創建了以下類和主要測試問題,並發現另一個有趣的副作用。 B類& C參考不同導入情況下的ClassA。 B & C是相同的,除了B在主要和C是進口。

ClassX.py

class ClassA(object): 
    def print_a(self): 
     print "this is class a" 

class ClassC(object): 
    def ref_a(self): 
     print 'from C ref a ==>', 
     xa=ClassA() 
     xa.print_a() 
    def ref_ca(self): 
     print 'from C ref ca ==>', 
     xa=ca() 
     xa.print_a() 

test_scope.py

from classes.ClassX import ClassA 
from classes.ClassX import ClassA as ca 
from classes.ClassX import ClassC as cb 


class ClassB(object): 
    def ref_a(self): 
     print 'from B ref a ==>', 
     xa=ClassA() 
     xa.print_a() 
    def ref_ca(self): 
     print 'from B ref ca ==>', 
     xa=ca() 
     xa.print_a() 

print 'globals:',dir() 
print 'modules','ca:',ca,'cb:',cb,'CA:',ClassA 
print '' 
print 'from main' 
xb=ClassB() 
xb.ref_a() 
xb.ref_ca() 

print '' 
print 'from imports' 
xbs=cb() 
xbs.ref_a() 
xbs.ref_ca() 

而且結果:

globals: ['ClassA', 'ClassB', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'ca', 'cb'] 
modules ca: <class 'classes.ClassX.ClassA'> cb: <class 'classes.ClassX.ClassC'> CA: <class 'classes.ClassX.ClassA'> 

from main 
from B ref a ==> this is class a 
from B ref ca ==> this is class a 

from imports 
from C ref a ==> this is class a 
from C ref ca ==> 
Traceback (most recent call last): 
    File "test_scope.py", line 32, in <module> 
    xbs.ref_ca() 
    File "R:\python\test_scripts\scope\classes\ClassX.py", line 13, in ref_ca 
    xa=ca() 
NameError: global name 'ca' is not defined 
Press any key to continue . . . 

從我的測試中,我看到的對象CA(進口AS)是不可用於ClassC,但是,ClassA模塊可用(導入時不帶)。

  1. 爲什麼導入和導入之間的區別是行爲?我不清楚爲什麼主要全球供應商不能上課。
  2. 什麼是動態確定適當的db_util模塊導入並讓其他導入的類可訪問的好方法?

更新: 閱讀命名空間的另一個職務後:「Visibility of global variables from imported modules」,據我所知,在我的例子上述原因ClassA的是提供給ClassC是,& C是在同一個導入文件,因此相同的命名空間。

所以剩下的問題是一個設計問題:

,如果我有這樣的代碼:

if db == 'MySQL': 
    from mysql_db import db_util 
elif db == 'Postgres' 
    from postgres_db import db_util 

什麼是一個好方法,使db_util適用於所有進口的模塊?

UPDATE:

從Blckknght的效應初探,我加入了代碼

cb.ca =ca 

到scope_test腳本。這需要的類呼叫xa = ca()被更改爲xa = self.ca()。我也認爲,雖然Python允許將對象添加到類外,但這不是一個好的設計方法,它將使調試成爲一場噩夢。

但是,因爲我認爲模塊和類應該是獨立的或專門聲明它們的依賴關係,所以我將使用上面的代碼示例來實現類。

突圍ClassA和ClassC分離模塊,並在ClassC的頂部,類定義之前,做進口

from ClassA import ClassA 
from ClassA import ClassA as ca 

class ClassB(object): 

,並在我的實際情況,在那裏我需要將db_util模塊導入數模塊

ci.py #NEW模塊在每個模塊中,選擇適當的類分貝

if db == 'MySQL': 
    from mysql_db import db_util 
elif db == 'Postgres' 
    from postgres_db import db_util 

需要的db_util類

import ci 
db_util=ci.db_util   #add db_util to module globals 

class Module(object): 

這樣做的一個問題是它需要每個模塊使用db_util來導入它,但它確實使依賴性已知。

我會關閉這個問題,並希望感謝Blckknght和Armin Rigo的回覆,這些回覆有助於爲我解釋這個問題。我會很感激任何與設計有關的反饋。

回答

9

在Python中,每個模塊都有它自己的全局名稱空間。當您執行導入時,您只會將導入的模塊添加到當前模塊的名稱空間,而不是添加到任何其他模塊的名稱空間。如果你想把它放在另一個命名空間中,你需要明確告訴Python。

main.py:

if db == "mysql": # or whatever your real logic is 
    import mysql_db as db_util 
elif db == "postgres": 
    import postgres_db as db_util 

import some_helper_module 

some_helper_module.db_util = db_util # explicitly add to another namespace 

#... 

其他模塊:

import some_helper_module 

db = some_helper_module.db_util.connect() # or whatever the real API is 

#... 

請注意,您通常不能使用你的主模塊(這是一個腳本執行)作爲共享命名空間。這是因爲Python使用模塊的__name__屬性來確定如何緩存模塊(以便始終從多個導入中獲取相同的對象),但腳本總是被賦予的"__main__"而不是其真實名稱。如果另一個模塊導入main,他們將獲得單獨的(重複)副本!

+0

我在導入後立即在上面的test_scope.py腳本中添加了'cb.ca = ca',認爲這會將ca添加到cb名稱空間。我仍然沒有定義全局,當我從ClassC打印全局變量時,它會打印全局變量:['self'] - 沒有對ca的引用,錯誤仍然存​​在。我錯過了什麼? – cswaim 2013-05-02 22:30:58

+0

我錯過了ca必須被引用爲self.ca,因爲ca現在在類中定義。 – cswaim 2013-05-03 17:47:54

2

您用錯誤的觀點來解決問題。每個模塊是一個啓動空的,充滿了名字命名空間(典型值)的每個語句運行:

import foo     => defines foo 
from foo import bar   => defines bar 
from foo import bar as baz => defines baz 
class kls: 
    pass      => defines kls 
def fun(): 
    pass      => defines fun 
var = 6 * 7     => defines var 

看着ClassX.py我們看到這個名字ca沒有這個模塊中定義的,但ClassAClassC是。所以從ClassX.py執行行xa=ca()失敗。

一般來說,這個想法是每個模塊都會導入它所需要的東西。你也可以從外部將一個名稱「修改」爲一個模塊,但這通常被認爲是非常糟糕的樣式(爲非常特殊的情況保留)。