这两天在看django的源代码,对包/模块的导入部分还不是很熟悉,这边结合Django源码代码,做一下理解和记录~
目录:
1. 模块属性:__name__
2. 动态模块导入:__import__() 和 importlib.import_module()
3. 包的目录结构 & __init__.py文件
参考:
----------------------------------------------------------------------------------------------------------------------------------------------------------
在模块b中直接运行的输出结果可以知道,通过模块的__name__属性可以知道,哪个是被导入模块,哪个是直接运行模块
#文件 ./a.py
print("hello world")
#文件 ./b.py
import a
print(a.__name__)
print(b.__name__)
#在模块b中直接运行的输出结果如下:
hello world
a #a为导入模块
__main__ #b为直接运行模块
二者都可实现动态导入模块(运行时导入),但__import__()只能导入第一级目录
__import__() :是系统提供的函数,而不是模块本身的属性方法
#文件 : b.py
__import__('sys') #等价于 import sys
print(dir(b)) #使用dir()查看模块b本身并不存在__import__()方法
动态导入模块:importlib,在Django的源代码中多处看到:
示例一:当我们在做数据库迁移:python manage.py migrate;或者是启动服务器时:python manage.py runserver; 中间会经过很多步,到达load_command_class()这个函数。在django/core/management/commonds文件夹下,有很多.py文件,其中就有migrete.py和runserver.py; 数据库迁移和数据库的启动就是执行这两个文件里的Command()。
#文件 ./django/core/management/__init__.py
def load_command_class(app_name, name): #app_name='django.core' name=migrate/runserver
module = import_module('%s.management.commands.%s' % (app_name, name))
return module.Command() #module='migrate.py'或者‘runserver’..其他.. 可实现动态变化
示例二: 代码还没看,先跳过
#文件 ./django/core/management/manage.py
import_module('.management', app_config.name)
#import_module的语法定义
#def import_module(name, package=None)
'''
a.py
__init__.py #没有__init__.py,导入包的时候将报错
b.py
__init__.py #没有__init__.py,导入包的时候将报错
c.py
'''
# 文件 ./a.py
import pack_b.b #输出两条语句: ‘pcak_b/b.py’ 'pack_b.__init__.py'
#文件 ./pack_b/b/py
print('pack_b/b.py')
#文件 ./pack_b/__init__.py
print('pack_b.__init__.py')
#文件 ./django/core/management/__init__.py
from django.apps import apps #开始以为导入的是apps模块,实际是apps对象,相当于from django.apps.registry import apps
def get_commands():
#.....部分代码省略........
for app_config in reversed(list(apps.get_app_configs())): #调用apps对象的get_app_configs()方法
path = os.path.join(app_config.path, 'management')
commands.update({name: app_config.name for name in find_commands(path)})
return commands
下一步,默认会先执行django.apps包下面的__init__.py,文件代码如下:
#文件 ./django/apps/__init__.py
from .config import AppConfig
from .registry import apps #加了这一句,上一段代码from django.apps import apps
__all__ = ['AppConfig', 'apps'] # 用于from django.apps import *
最后,执行registry.py文件
#文件 ./django/apps/registry.py
class Apps:
#....部分代码省略.........
def get_app_configs(self):
self.check_apps_ready()
return self.app_configs.values()
apps = Apps(installed_apps=None) #射击单例模式,再学习