flyEn'blog

python项目分层模块导入处理优化

python项目分层模块导入处理优化 (通过字符串名导入模块)

参考:https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p10_import_modules_using_name_given_in_string.html

案例:chatbot项目

实践:构造一个新的逻辑处理器

构建机器人层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bot = ChatBot(
'Charlie',
storage_adapter='chatterbot.storage.SQLStorageAdapter',
logic_adapters=[
{
'import_path': 'chatterbot.logic.BestMatchWithDefault',
'statement_comparison_function': 'chatterbot.comparisons.cn_cosine_similarity',
'threshold': 0.8,
'default_response': '非常抱歉,该问题无法给出明确回复,建议咨询人工服务。',
},
],
read_only=True,
database='database.db',
)

chatterbot.py ChatBot类:

1
2
3
logic_adapters = kwargs.get('logic_adapters', [
'chatterbot.logic.BestMatch'
])

导入模块

1
2
3
# 将构造机器人参数中的字符串转为处理类对象(此为导入选用的逻辑处理器并赋值给chatbot类的logic属性)
for adapter in logic_adapters:
self.logic.add_adapter(adapter, **kwargs)

进入add_adapter方法:

1
2
3
4
5
6
def add_adapter(self, adapter, **kwargs):
# 验证是否是有效的处理类
utils.validate_adapter_class(adapter, LogicAdapter)
# 得到处理类的实例
adapter = utils.initialize_class(adapter, **kwargs)
self.adapters.append(adapter)

进入utils.validate_adapter_class方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from .adapters import Adapter

# 验证写入的逻辑处理器是否为有效格式
if isinstance(validate_class, dict):

if 'import_path' not in validate_class:
raise Adapter.InvalidAdapterTypeException(
'The dictionary {} must contain a value for "import_path"'.format(
str(validate_class)
)
)

# Set the class to the import path for the next check
validate_class = validate_class.get('import_path')

# 验证是否为adapter_class(此例即LogicAdapter)的子类,此步骤开始尝试导入新增的逻辑处理器import_module
if not issubclass(import_module(validate_class), adapter_class):
raise Adapter.InvalidAdapterTypeException(
'{} must be a subclass of {}'.format(
validate_class,
adapter_class.__name__
)
)

进入import_module方法:

1
2
3
4
5
6
7
8
9
10
11
def import_module(dotted_path):
"""
Imports the specified module based on the
dot notated import path for the module.
"""
import importlib
module_parts = dotted_path.split('.')
module_path = '.'.join(module_parts[:-1])
module = importlib.import_module(module_path)
print(module)
return getattr(module, module_parts[-1])

importlib.import_module手动导入名字为字符串给出的一个模块或者包的一部分 getattr(module, module_parts[-1])查看这个包是否包含module_parts[-1]这个属性值。

因此项目logic包下模块导入被限制(因) 进入logic包下的init.py(新增的逻辑处理器添入这个属性列表即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from .match_with_default import BestMatchWithDefault
from .logic_adapter import LogicAdapter
from .best_match import BestMatch
from .low_confidence import LowConfidenceAdapter
from .mathematical_evaluation import MathematicalEvaluation
from .multi_adapter import MultiLogicAdapter
from .no_knowledge_adapter import NoKnowledgeAdapter
from .specific_response import SpecificResponseAdapter
from .time_adapter import TimeLogicAdapter


__all__ = (
'LogicAdapter',
'BestMatch',
'LowConfidenceAdapter',
'MathematicalEvaluation',
'MultiLogicAdapter',
'NoKnowledgeAdapter',
'SpecificResponseAdapter',
'TimeLogicAdapter',
'BestMatchWithDefault',
)
# Import string module parameters
if 'statement_comparison_function' in kwargs:
import_path = kwargs.get('statement_comparison_function')
if isinstance(import_path, str):
kwargs['statement_comparison_function'] = import_module(import_path)

if 'response_selection_method' in kwargs:
import_path = kwargs.get('response_selection_method')
if isinstance(import_path, str):
kwargs['response_selection_method'] = import_module(import_path)

# By default, compare statements using Levenshtein distance
self.compare_statements = kwargs.get(
'statement_comparison_function',
levenshtein_distance
)

# By default, select the first available response
self.select_response = kwargs.get(
'response_selection_method',
get_first_response
)
Fork me on GitHub