一、概述
模块(module)和 包(package)是Python用于组织大型程序的利器。
模块 是一个由 变量、函数、类 等基本元素组成的功能单元,设计良好的模块通常是高内聚、低耦合、可复用、易维护的。包 是管理模块的容器,它具有 可嵌套性:一个包可以包含模块和其他包。从文件系统的视角来看,包就是目录,模块就是文件。
从本质上讲,一个模块就是一个独立的名字空间(namespace),单纯的多个模块只能构成扁平结构的名字空间集;而包的可嵌套性,使得多个模块可以呈现出多层次结构的名字空间树。
二、导入语句
如果要在一个模块A中使用另一个模块B(即访问模块B的属性),则必须首先 导入 模块B。此时模块A称为导入模块(即importer),而模块B称为被导入模块(即importee)。
导入语句(import statement)有两种风格:import <>
和from <> import
。对模块的导入同时支持这两种风格。
1、基本语法
1)导入模块module(重命名为name)
import module [as name]
>>> import sys
>>> sys.version
'2.7.3 (default, Apr 10 2013, 05:46:21) \n[GCC 4.6.3]'
>>> import sys as s
>>> s.version
'2.7.3 (default, Apr 10 2013, 05:46:21) \n[GCC 4.6.3]'
2)导入模块module1(重命名为name1),模块module2(重命名为name2),等等
import module1 [as name1], module2 [as name2], ...
>>> import sys, os
>>> sys.platform, os.name
('linux2', 'posix')
>>> import sys as s, os as o
>>> (s.platform, o.name)
('linux2', 'posix')
3)从模块module中导入属性attribute(重命名为name)
from module import attribute [as name]
>>> from sys import executable
>>> executable
'/usr/bin/python'
>>> from sys import executable as exe
>>> exe
'/usr/bin/python'
4)从模块module中导入属性attribute1(重命名为name1),属性attribute2(重命名为name2),等等
from module import attribute1 [as name1], attribute2 [as name2], ...
>>> from sys import platform, executable
>>> platform, executable
('linux2', '/usr/bin/python')
>>> from sys import platform as plf, executable as exe
>>> plf, exe
('linux2', '/usr/bin/python')
5)从模块module中导入属性attribute1(重命名为name1),属性attribute2(重命名为name2),等等
from module import (attribute1 [as name1], attribute2 [as name2], ...)
>>> from sys import (platform, executable)
>>> platform, executable
('linux2', '/usr/bin/python')
>>> from sys import (platform as plf, executable as exe)
>>> plf, exe
('linux2', '/usr/bin/python')
6)从模块module中导入所有属性
from module import *
>>> from sys import *
>>> platform, executable
('linux2', '/usr/bin/python')
2、推荐风格
以下是在Python程序中推荐使用的导入语句:
-
import module [as name]
(导入单个模块) -
from module import attribute [as name]
(导入单个属性) -
from module import attribute1 [as name1], attribute2 [as name2], ...
(导入较少属性时,单行书写) -
from module import (attribute1 [as name1], attribute2 [as name2], ...)
(导入较多属性时,分行书写)
应当尽量避免使用的导入语句是:
-
import module1 [as name1], module2 [as name2], ...
它会降低代码的可读性,应该用多个
import module [as name]
语句代替。 -
from module import *
它会让importer的名字空间变得不可控(很可能一团糟)。
三、模块
1、模块名
一个 模块 就是一个Python源码文件。如果文件名为mod.py,那么模块名就是mod。
模块的导入和使用都是借助模块名来完成的,模块名的命名规则与变量名相同。
2、模块属性
模块属性 是指在模块文件的全局作用域内,或者在模块外部(被其他模块导入后)可以访问的所有对象名字的集合。这些对象名字构成了模块的名字空间,这个名字空间其实就是全局名字空间(参考 名字空间与作用域)。
模块的属性由两部分组成:固有属性 和 新增属性。可以通过 M.__dict__ 或 dir(M) 来查看模块M的属性。
1)固有属性
固有属性 是Python为模块默认配置的属性。
例如,新建一个空文件mod.py:
$ touch mod.py
$ python
...
>>> import mod # 导入模块mod
>>> mod.__dict__ # 模块mod的属性全貌
{'__builtins__': {...}, '__name__': 'mod', '__file__': 'mod.pyc', '__doc__': None, '__package__': None}
>>> dir(mod) # 只查看属性名
['__builtins__', '__doc__', '__file__', '__name__', '__package__']
上述示例中,空模块mod的所有属性都是固有属性,包括:
-
__builtins__
内建名字空间(参考 名字空间) -
__file__
文件名(对于被导入的模块,文件名为绝对路径格式;对于直接执行的模块,文件名为相对路径格式) -
__name__
模块名(对于被导入的模块,模块名为去掉“路径前缀”和“.pyc后缀”后的文件名,即os.path.splitext(os.path.basename(__file__))[0]
;对于直接执行的模块,模块名为__main__
) -
__doc__
文档字符串(即模块中在所有语句之前第一个未赋值的字符串) -
__package__
包名(主要用于相对导入,请参考 PEP 366)
2)新增属性
新增属性 是指在模块文件的顶层(top-level),由赋值语句(如import、=、def和class)创建的属性。
例如,修改文件mod.py为:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''this is a test module'''
import sys
debug = True
_static = ''
class test_class(object):
def say(self): pass
def test_func():
var = 0
再次查看模块mod的属性:
>>> import mod
>>> dir(mod)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_static', 'debug', 'sys', 'test_class', 'test_func']
对比上一小节可知,除开固有属性外的其他属性都是新增属性,包括:
- sys(由“import”创建)
- debug和_static(均由“=”创建)
- test_class(由“class”创建)
- test_func(由“def”创建)
这些属性的共同点是:它们都在模块文件的顶层创建。相比之下,类方法say(在类test_class内部创建)和局部变量var(在函数test_func内部创建)都不在顶层,因此不在新增属性之列。(作为一个例子,'''this is a test module'''就是模块mod的文档字符串,即mod.__doc__
的值)
3、可导出的公有属性
在 『导入语句』 中描述的基本语法可以归纳为三类:
-
import module
在导入模块(importer)中,可以通过module.*的形式访问模块module中的所有属性 -
from module import attribute
只能通过名字attribute访问模块module中的指定属性module.attribute -
from module import *
可以直接通过属性名访问模块module中的所有 公有属性
换句话说,模块的 公有属性 就是那些可以通过from module import *
被导出给其他模块直接使用的属性。
模块的公有属性有以下特点:
- 可以在模块中定义一个特殊列表
__all__
,其中包含所有可导出的公有属性的字符串名称,从而实现对公有属性的定制 - 如果没有定义
__all__
,那么默认所有不以下划线“_”开头的属性都是可导出的公有属性
以 『新增属性』 中的mod.py为例,没有定义__all__
的公有属性:
>>> dir() # 导入模块mod前的名字空间
['__builtins__', '__doc__', '__name__', '__package__']
>>> from mod import * # 导入模块mod中的所有公有属性
>>> dir() # 导入模块mod后的名字空间
['__builtins__', '__doc__', '__name__', '__package__', 'debug', 'sys', 'test_class', 'test_func']
对比导入模块mod前后的情况可知,模块mod中的sys、debug、test_class和test_func都属于公有属性,因为它们的名字不以“_”开头;而其他以“_”开头的属性(包括所有“固有属性”,以及“新增属性”中的_static)都不在公有属性之列。
如果在模块文件mod.py中顶层的任何位置,增加定义一个特殊列表__all__ = ['sys', '_static', 'test_func']
(此时__all__
也是模块属性),那么此时的公有属性:
>>> dir() # 导入模块mod前的名字空间
['__builtins__', '__doc__', '__name__', '__package__']
>>> from mod import * # 导入模块mod中的所有公有属性
>>> dir() # 导入模块mod后的名字空间
['__builtins__', '__doc__', '__name__', '__package__', '_static', 'sys', 'test_func']
可以看出,只有在__all__
中指定的属性才是公有属性。
4、直接执行
模块可以在命令行下被直接执行,以模块mod(对应文件mod.py)为例:
1)以脚本方式执行
python mod.py <arguments>
2)以模块方式执行
python -m mod <arguments>
四、包
一个 包 就是一个含有__init__.py文件的目录。
包与模块之间的包含关系是:一个包可以包含子包或子模块,但一个模块却不能包含子包和子模块。
1、包名
与模块名类似,包名的命名规则也与变量名相同。
此外,需要特别注意的是:如果在同一个目录下,存在两个同名的包和模块,那么导入时只会识别包,而忽略模块。(参考 specification for packages 中的 『What If I Have a Module and a Package With The Same Name?』)
例如,在目录dir下新建一个文件spam.py(即模块spam),此时import spam
会导入模块spam:
$ cd dir/
$ touch spam.py
$ python
...
>>> import spam
>>> spam
<module 'spam' from 'spam.py'>
如果在目录dir下再新建一个含有__init__.py文件的目录spam(即包spam),此时import spam
则会导入包spam(而不再是模块spam):
$ mkdir spam && touch spam/__init__.py
$ python
...
>>> import spam
>>> spam
<module 'spam' from 'spam/__init__.py'>
2、包属性
包属性与模块属性非常相似,也分为 固有属性 和 新增属性。
1)固有属性
与模块相比,包的 固有属性 仅多了一个__path__
属性,其他属性完全一致(含义也类似)。
__path__
属性即包的路径(列表),用于在导入该包的子包或子模块时作为搜索路径;修改一个包的__path__
属性可以扩展该包所能包含的子包或子模块。(参考 Packages in Multiple Directories)
例如,在dir目录下新建一个包pkg(包含一个模块mod),显然在包pkg中只能导入一个子模块mod:
$ mkdir pkg && touch pkg/__init__.py
$ touch pkg/mod.py
$ python
...
>>> import pkg.mod # 可以导入子模块mod
>>> import pkg.mod_1 # 不能导入子模块mod_1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named mod_1
如果在dir目录下再新建一个包pkg_1(包含一个模块mod_1):
$ mkdir pkg_1 && touch pkg_1/__init__.py
$ touch pkg_1/mod_1.py
并且在pkg/__init__.py中修改包pkg的__path__
属性:
print('before:', __path__)
__path__.append(__path__[0].replace('pkg', 'pkg_1')) # 将“包pkg_1所在路径”添加到包pkg的__path__属性中
print('after:', __path__)
此时,在包pkg中就可以导入子模块mod_1(仿佛子模块mod_1真的在包pkg中):
$ python
...
>>> import pkg.mod # 可以导入子模块mod
('before:', ['pkg'])
('after:', ['pkg', 'pkg_1'])
>>> import pkg.mod_1 # 也可以导入子模块mod_1
2)新增属性
包的 新增属性 包括两部分:静态的新增属性和动态的新增属性。
静态的新增属性是指:在__init__.py的顶层(top-level),由赋值语句(如import、=、def和class)创建的属性。这部分与模块的新增属性一致。
动态的新增属性是指:在执行导入语句后动态添加的新增属性。具体而言,如果有一个导入语句导入了某个包pkg中的子模块submod(或子包subpkg),那么被导入的子模块submod(或子包subpkg)将作为一个属性,被动态添加到包pkg的新增属性当中。
以包含模块mod的包pkg为例:
>>> import pkg
>>> dir(pkg)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__']
>>> import pkg.mod # 该语句导入了包pkg中的模块mod
>>> dir(pkg) # mod成为了包pkg的“动态的新增属性”
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'mod']
3、可导出的公有属性
在公有属性方面,包与模块的行为完全一致,当然别忘了包还有 动态的新增属性。
4、其他
1)导入语句
加入包的概念以后,导入语句的风格(与仅有模块时相比)不变,但是语法上有一些细微差异:用“.”来表示包与模块之间的包含关系;可操作的对象扩充为 包、模块 和 属性。
下面是涉及包时,一些典型的导入语句:
import package
import package.module
import package.subpackage
import package.subpackage.module
from packagae import module
from packagae import subpackagae
-
from packagae import subpackagae.module
或from packagae.subpackagae import module
from packagae.module import attribute
from packagae.subpackagae.module import attribute
2)__init__.py
关于包的__init__.py,有以下几点总结:
- 一般为空即可
- 有时也可以放置一些初始化代码,用于在包加载时执行
- 少数情况下,还可以用于定制包的一些属性(如
__all__
、__path__
等)
五、导入原理
模块与包的导入原理几乎完全一致,因此下面以模块为主进行讨论,仅在有显著差异的地方对包作单独说明。
1、导入依赖
对于模块M而言,根据导入语句的不同(指明了模块M是否在一个包中),可能存在导入依赖的问题:
-
import M
模块M不在一个包中,因此无导入依赖:直接以“M”为 完整名(fully qualified name)导入模块M
-
import A.B.M
或者from A.B import M
模块M在一个子包B中,而子包B又在一个包A中,因此存在导入依赖:会首先以“A”为 完整名 导入包A,接着以“A.B”为 完整名 导入子包B,最后以“A.B.M”为 完整名 导入模块M。
2、导入过程
一个模块的导入过程主要分三步:搜索、加载 和 名字绑定。(具体参考 The import statement)
1)搜索
搜索 是整个导入过程的核心,也是最为复杂的一步。对于被导入模块M,按照先后顺序,搜索的处理步骤为:
- 在缓存 sys.modules 中查找模块M,若找到则直接返回模块M
- 否则,顺序搜索 sys.meta_path,逐个借助其中的 finder 来查找模块M,若找到则加载后返回模块M
- 否则,如果模块M在一个包P中(如
import P.M
),则以P.__path__
为搜索路径进行查找;如果模块M不在一个包中(如import M
),则以 sys.path 为搜索路径进行查找
2)加载
正如 『搜索』 步骤中所述,对于找到的模块M:如果M在缓存 sys.modules 中,则直接返回;否则,会加载M。
加载 是对模块的初始化处理,包括以下步骤:
- 设置属性:包括
__name__
、__file__
、__package__
和__loader__
(对于包,则还有__path__
) - 编译源码:将模块文件(对于包,则是其对应的__init__.py文件)编译为字节码(*.pyc),如果字节码文件已存在且仍然是最新的,则不会重编
- 执行字节码:执行编译生成的字节码(即模块文件或__init__.py文件中的语句)
有一点值得注意的是,加载不只是发生在导入时,还可以发生在 reload() 时。
3)名字绑定
加载完importee模块后,作为最后一步,import语句会为 导入的对象 绑定名字,并把这些名字加入到importer模块的名字空间中。其中,导入的对象 根据导入语句的不同有所差异:
- 如果导入语句为
import obj
,则对象obj可以是包或者模块 - 如果导入语句为
from package import obj
,则对象obj可以是package的子包、package的属性或者package的子模块 - 如果导入语句为
from module import obj
,则对象obj只能是module的属性
3、更多细节
根据 The import statement 中的描述,以下是导入原理对应的Python伪码:
import sys
import os.path
def do_import(name):
'''导入'''
parent_pkg_name = name.rpartition('.')[0]
if parent_pkg_name:
parent_pkg = do_import(parent_pkg_name)
else:
parent_pkg = None
return do_find(name, parent_pkg)
def do_find(name, parent_pkg):
'''搜索'''
if not name:
return None
# step 1
if name in sys.modules:
return sys.modules[name]
else:
# step 2
for finder in sys.meta_path:
module = do_load(finder, name, parent_pkg)
if module:
return module
# step 3
src_paths = parent_pkg.__path__ if parent_pkg else sys.path
for path in src_paths:
if path in sys.path_importer_cache:
finder = sys.path_importer_cache[path]
if finder:
module = do_load(finder, name, parent_pkg)
if module:
return module
else:
# handled by an implicit, file-based finder
else:
finder = None
for callable in sys.path_hooks:
try:
finder = callable(path)
break
except ImportError:
continue
if finder:
sys.path_importer_cache[path] = finder
elif os.path.exists(path):
sys.path_importer_cache[path] = None
else:
sys.path_importer_cache[path] = # a finder which always returns None
if finder:
module = do_load(finder, name, parent_pkg)
if module:
return module
raise ImportError
def do_load(finder, name, parent_pkg):
'''加载'''
path = parent_pkg.__path__ if parent_pkg else None
loader = finder.find_module(name, path)
if loader:
return loader.load_module(name)
else:
return None
4、sys.path
正如 『导入过程』 中所述,sys.path是 不在包中的模块(如import M)的“搜索路径”。在这种情况下,控制sys.path就能控制模块的导入过程。
sys.path 是一个路径名的列表,按照先后顺序,其中的路径主要分为以下四块:
- 程序主目录(默认定义):如果是以脚本方式启动的程序,则为 启动脚本所在目录;如果在交互式命令行中,则为 当前目录。
- PYTHONPATH目录(可选扩展):以 os.pathsep 分隔的多个目录名,即环境变量
os.environ['PYTHONPATH']
(类似shell环境变量PATH) - 标准库目录(默认定义):Python标准库所在目录(与安装目录有关)
- .pth文件目录(可选扩展):以“.pth”为后缀的文件,其中列有一些目录名(每行一个目录名),用法参考 site
为了控制sys.path,可以有三种选择:
- 直接修改sys.path列表
- 使用PYTHONPATH扩展
- 使用.pth文件扩展
六、重新加载
关于导入,还有一点非常关键:加载只在第一次导入时发生。这是Python特意设计的,因为加载是个代价高昂的操作。
通常情况下,如果模块没有被修改,这正是我们想要的行为;但如果我们修改了某个模块,重复导入不会重新加载该模块,从而无法起到更新模块的作用。有时候我们希望在 运行时(即不终止程序运行的同时),达到即时更新模块的目的,内建函数 reload() 提供了这种 重新加载 机制。
关键字reload
与import
不同:
-
import
是语句,而reload
是内建函数 -
import
使用 模块名,而reload
使用 模块对象(即已被import语句成功导入的模块)
重新加载(reload(module))有以下几个特点:
- 会重新编译和执行模块文件中的顶层语句
- 会更新模块的名字空间(字典 M.__dict__):覆盖相同的名字(旧的有,新的也有),保留缺失的名字(旧的有,新的没有),添加新增的名字(旧的没有,新的有)
- 对于由
import M
语句导入的模块M:调用reload(M)
后,M.x
为 新模块 的属性x(因为更新M后,会影响M.x
的求值结果) - 对于由
from M import x
语句导入的属性x:调用reload(M)
后,x
仍然是 旧模块 的属性x(因为更新M后,不会影响x
的求值结果) - 如果在调用
reload(M)
后,重新执行import M
(或者from M import x
)语句,那么M.x
(或者x
)为 新模块 的属性x
七、相对导入
严格来说,模块(或包)的导入方式分为两种:绝对导入 和 相对导入。以上讨论的导入方式都称为 绝对导入,这也是Python2.7的默认导入方式。相对导入是从Python2.5开始引入的,主要用于解决“用户自定义模块可能会屏蔽标准库模块”的问题(参考 Rationale for Absolute Imports)。
相对导入 使用前导的“.”来指示importee(即被导入模块或包)与importer(当前导入模块)之间的相对位置关系。相对导入 只能使用from <> import
风格的导入语句,import <>
风格的导入语句只能用于 绝对导入。(相对导入的更多细节,请参考 PEP 328)
1、导入语句
例如有一个包的布局如下:
pkg/
__init__.py
subpkg1/
__init__.py
modX.py
modY.py
subpkg2/
__init__.py
modZ.py
modA.py
假设当前在文件modX.py或subpkg1/__init__.py中(即当前包为subpkg1),那么下面的导入语句都是相对导入:
from . import modY # 从当前包(subpkg1)中导入模块modY
from .modY import y # 从当前包的模块modY中导入属性y
from ..subpkg2 import modZ # 从当前包的父包(pkg)的包subpkg2中导入模块modZ
from ..subpkg2.modZ import z # 从当前包的父包的包subpkg2的模块modZ中导入属性z
from .. import modA # 从当前包的父包中导入模块modA
from ..modA import a # 从当前包的父包的模块modA中导入属性a
2、导入原理
与绝对导入不同,相对导入的导入原理比较简单:根据 模块的__name__
属性 和 由“.”指示的相对位置关系 来搜索并加载模块(参考 Relative Imports and __name__)。
3、直接执行
由于相对导入会用到模块的__name__
属性,而在直接执行的主模块中,__name__
值为__main__
(没有包与模块的信息),所以在主模块中:尽量全部使用绝对导入。
如果非要使用相对导入,也可以在顶层包(top-level package)的外部目录下,以模块方式执行主模块:python -m pkg.mod
(假设顶层包为pkg,mod为主模块,其中使用了相对导入)。(具体参考 PEP 366)
Python基础:模块的更多相关文章
-
python基础——模块
python基础——模块 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文 ...
-
自学Python之路-Python基础+模块+面向对象+函数
自学Python之路-Python基础+模块+面向对象+函数 自学Python之路[第一回]:初识Python 1.1 自学Python1.1-简介 1.2 自学Python1.2-环境的 ...
-
python基础-模块
一.模块介绍 ...
-
Python基础+模块、异常
date:2018414+2018415 day1+2 一.python基础 #coding=utf-8 #注释 #算数运算 +(加) -(减) *(乘) /(除) //(取整) %(取余) ...
-
Python 基础 模块
python 中模块和保定 概念 如果将代码分才投入多个py 文件,好处: 同一个变量名也互不影响. python 模块导入 要使用一个模块,我们必须先导入该模块.python 使用import ...
-
python基础----模块、包
一 模块 ...
-
Python基础-模块与包
一.如何使用模块 上篇文章已经简单介绍了模块及模块的优点,这里着重整理一下模块的使用细节. 1. import 示例文件:spam.py,文件名spam.py,模块名spam #spam.py pri ...
-
Python菜鸟之路:Python基础-模块
什么是模块? 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护.为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,分组的规则就是把实现了某个 ...
-
python基础--模块&;包
一.模块 1.模块是程序 任何Python程序都可以作为模块导入. 程序的保存也很重要,如果想把程序保存在C:\python (Windows)目录下,需要告诉解释器在哪里寻找模块了. >> ...
-
python基础-------模块与包(四)
configparser模块与 subprcess 利用configparser模块配置一个类似于 windows.ini格式的文件可以包含一个或多个节(section),每个节可以有多个参数(键=值 ...
随机推荐
-
COGS247. 售票系统[线段树 RMQ]
247. 售票系统 ★★☆ 输入文件:railway.in 输出文件:railway.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 某次列车途经C个城市,城市 ...
-
Mac与iPhone屏幕录制
1. Mac电脑屏幕录制 1.1 文件->新建屏幕录制 1.2 点击红色按钮 1.3 截取需要录制的屏幕部分,点击开始录制 1.4 点击工具栏的停止按钮,停止录制 1.5 然后会 ...
-
Golang控制goroutine的启动与关闭
最近在用golang做项目的时候,使用到了goroutine.在golang中启动协程非常方便,只需要加一个go关键字: go myfunc(){ //do something }() 但是对于一些长 ...
-
UVA - 11986 Save from Radiation
Description J Save from Radiation Most of you are aware of Nuclear Power Plant Explosion at Fukushim ...
-
多线程系列之五:Balking 模式
一,什么是Balking模式 如果现在不合适执行这个操作,或者没必要执行这个操作,就停止处理,直接返回.在Balking模式中,如果守护条件不成立,就立即中断处理. 二,例子: 定期将当前数据内容写入 ...
-
hdu 3911 Black And White (线段树 区间合并)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3911 题意: 给你一段01序列,有两个操作: 1.区间异或,2.询问区间最长的连续的1得长度 思路: ...
-
Android——SQLite/数据库 相关知识总结贴
android SQLite简介 http://www.apkbus.com/android-1780-1-1.html Android SQLite基础 http://www.apkbus.com/ ...
-
[日常] Go语言圣经--Channel习题
练习 8.3: 在netcat3例子中,conn虽然是一个interface类型的值,但是其底层真实类型是*net.TCPConn,代表一个TCP连接.一个TCP连接有读和写两个部分,可以使用Clos ...
-
通过mysql自动同步redis
在服务端开发过程中,一般会使用MySQL等关系型数据库作为最终的存储引擎,Redis其实也可以作为一种键值对型的数据库,但在一些实际场景中,特别是关系型结构并不适合使用Redis直接作为数据库.这俩家 ...
-
mybatis批量插入:oracle和mysql的区别
一.oracle批量插入 <insert id="save" parameterType="java.util.List"> insert into ...