Python重构:整理导入
这可能是个新手问题(我从PHP5面向对象转到Python),但我对import
语句在整理和重构我们的一些Python模块时有些不确定的地方。
举个例子,我有一个数据库连接工厂模块,它导入了mysqldb,代码大概是这样的:
import MySQLdb as mysql
from MySQLdb import cursors
class ConnectionFactory():
@staticmethod
def connect(db_host,db_user,db_pass,db,cursor='DictCursor'):
connection = mysql.connect(host = db_host,
user = db_user,
passwd = db_pass,
db = db,
cursorclass=getattr(mysql.cursors, cursor))
cur = connection.cursor();
connection.ping(True)
return (cur,connection)
首先,为什么第二个导入,专门获取cursors
是必要的呢? 我似乎不能直接访问mysql.cursors。
另外,假设另一个模块导入了这个模块来获取数据库连接,然后使用它执行查询。
import ConnectionFactory
def runquery(q):
try:
cur,connection = ConnectionFactory.connect(db_host,db_user,db_pass,db)
cur.execute(q)
res = cur.fetchall()
return res
except Exception as e:
log(str(e))
理想情况下,捕获的那个异常应该是MySQLdb.Error异常。这是否意味着我也需要在这里导入MySQLdb? 我觉得应该有更优雅的做法。我肯定还没有完全理解Python的思维方式。
最后,假设这两个模块都在另一个父模块中被导入,以便在WSGI中使用(我所有的请求都是通过Werkzeug进来的,然后运行其他模块中的很多方法)
如果WSGI导入了这两个模块,我假设它们各自导入的内容并不在它的命名空间中。那么在多个模块中有相同的导入是个坏习惯吗,如果最终它们会在一个更大的模块中一起使用?
编辑:为了澄清第三个问题,
我有一个特殊的json编码器,它几乎在所有地方都被用作json.dumps()
的cls
参数。
from datetime import datetime
import gnengine
import json
class SpecialEncoder(json.JSONEncoder):
def default(self,obj):
if isinstance(obj,set):
return list(obj)
elif isinstance(obj,datetime):
return obj.isoformat()
elif isinstance(obj,gnengine.searchresult):
return obj.jsonify()
return json.JSONEncoder.default(self,obj)
gnengine
是我的WSGI,而searchresult
是该模块中的一个类型。我在这里导入它是因为它有自己的json方法,我的编码器需要这个方法。但这个编码器显然也被导入到gnengine
中。它们实际上是互相导入的。
我的PHP背景告诉我,如果searchresult
类型成为一个独立的模块,代表一个对象模型,然后两个模块分别导入它,这样就能解决这个问题。但我不确定这是不是我一个很大的误解。
抱歉把三个问题放在一起,基本上我想问的是关于导入的一般性澄清。我明白命名空间的概念,也知道from name import *
是个坏主意,但我就是不明白当几个包一起使用时,如何处理相似的依赖关系才是最佳做法。在Python中,是否期望在多个地方都有相同的import
?
2 个回答
MySQLdb的
__init__.py
文件(或者其他初始化的方法)并没有导入游标(cursors),所以MySQLdb并不知道游标模块的存在,因为它只是自己目录下的一个子目录。这就意味着你需要手动导入这个模块。你需要直接从MySQLdb导入异常(exception)。比如说可以用
from MySQLdb import Exception
。异常其实就是一些类,它们是Extension类的子类。Python无法处理它不知道的类。这里使用“from”这个词,是为了只导入你需要的部分。不,Python的做法是把相同的对象导入到多个不同的文件中,这些文件最终会一起工作。解释器会确保运行时不会出错,也不会覆盖任何东西等等。(试着连续导入同样的东西两次——不会报错,而且你只会得到一个实例)。
我觉得
MySQLdb.cursors
是另一个模块,嵌套的模块需要单独导入。如果你想专门捕捉在
MySQLdb
中声明的异常,那你确实需要导入一些东西。如果你想捕捉 任何 异常,那就不需要导入。不过这样做可能不太好,正如 @tkone 在下面的评论中解释的那样。无论如何,我觉得从MySQLdb
导入一些东西并没有问题——如果你的数据库访问代码没有分散在多个模块中,而是集中在一个数据库访问层里,那么你从MySQLdb
导入的模块就不会太多。我不太明白这个问题。你能再解释一下吗?