Python中import语句用法详解

news2024/11/20 15:22:11

一. 什么是模块(module)?

在实际应用中,有时程序所要实现功能比较复杂,代码量也很大。若把所有的代码都存储在一个文件中,则不利于代码的复用和维护。一种更好的方式是将实现不同功能的代码分拆到多个文件中分别进行存储,这样使得结构更加清晰,更易于维护。

为了实现这些需求,Python将一系列相关的代码组织在一起存储在一个文件中,这就是 模块(module)。最常见的模块是以.py“为后缀的文件(即用Python编写的代码文件),文件名就是模块名。当然模块还有一些其他的形式,例如”.so"文件、“.dll” 文件等,但初学者几乎接触不到。在一个模块中可以定义变量、函数、类等,也能包含可执行代码。Python中的模块可以分成3类:(1)Python内置的模块;(2)第三方模块;(3)用户自定义的模块。

看一个简单的例子,我们可以自定义一个加法模块add,文件名为“add.py”。

#
# @file add.py
#

def add(a,b):
    return a + b

然后在当前目录中,通过import语句导入该模块,格式为import 模块名,之后就可以使用模块中定义的add函数进行运算了。

>>> import add
>>> add.add(3, 5)
>>> 8

注:上图第二条语句中的第1个add表示的是add模块,第2个add表示的是该模块中定义的add函数。

二. 模块是如何组织的?

Python中包含各种各样功能的模块,数量繁多。若把所有模块都放到一个目录中则很难使用和维护,例如容易产生模块名冲突的问题。因此我们需要一种合理的组织方式来提高模块的结构性和可维护性。Python是通过来组织模块的。

什么是包(package)呢?包本质上就是一个目录(文件夹),目录名就是包名。 在包中可以包含多个子目录(子包)或模块。

例如我们可以将自定义的加法模块add、减法模块sub、乘法模块mul和除法模块dev组织成一个包arithmetic,包的架构如下图所示。

在这里插入图片描述
再比如在音频数据处理中,会用到很多模块来实现对音频数据的不同处理。我们就可以设计一个包——sound来组织这些模块,其下面又包含了3个子包effects、filters和formats,分别对应不同类型的功能模块,包的架构如下所示。

在这里插入图片描述
注:可以发现包中通常都包含一个__init__.py文件。在Python3.x中,这并不是必需的,__init__.py文件的作用将在后面进行介绍。

三. 如何导入模块?

要使用模块必须先进行导入,导入模块有两类方式:

方式一:import …

  • import […包].模块 [as 模块别名]
    例如,import add,import arithmetic.add,import sound.filters.equalizer
  • import 包 [as 包别名]
    例如,import arithmetic

方式二:from … import …

  • from 包.[…包] import 模块 [as 模块别名] / *
    例如,from arithmetic import add,from arithmetic import *
  • from […包].模块 import 变量/函数/类 [as 别名]/ *
    例如,from arithmetic.add import add,from arithmetic.add import *

四. 每种导入方式有何区别?

上述两类模块导入的方式有什么区别呢?我们在实际编程时,该选择哪种方式导入模块呢?要回答这个问题,我们就需要了解模块导入背后的运行机制。在讲解这个机制之前,先简单介绍一下命名空间的概念。

1. 命名空间

命名空间(namespace)是映射到对象的名称 ,大多使用字典来实现。命名空间是在不同的时刻创建的,且拥有不同的生命周期。在Python语言中,有3类命名空间:内置命名空间、全局命名空间和局部命名空间。

  • 内置命名空间(build-in namespace)
    内置命名空间记录了以下信息:
    在这里插入图片描述
    内置命名空间中最常用的就是内置函数了,如下所示:
    在这里插入图片描述
    内置命名空间在Python解释器启动时创建,直至Python解释器退出都有效。 因此,Python解释器一旦启动,所有的内置函数不需要import,就可直接使用。

  • 全局命名空间(global namespace)
    用来记录模块级的变量、常量、函数、类以及其他导入的模块,可通过 globals() 函数查看。全局命名空间在读取模块定义时创建,通常也会持续到Python解释器退出。
    在这里插入图片描述可以看到,在全局命名空间中已经包含了一些名称,例如:
    __name__
    __doc__
    __loader__
    __spec__
    __annotations__
    __buitins__
    这些名称都以双下划线开头和结尾,表示这是系统为每个模块自动生成的。这里重点介绍一下 __name__ 。它用来标识所在模块的名字,可能有两种取值:若该模块是当前正在运行的模块,则 __nam__ 的取值为 ‘__main__’; 若该模块不是正在运行的模块而是通过 import 语句导入的模块,则 __name__ 的取值为该模块的名称。在Python程序中,会经常看到如下语句:

if __name__ ==  'main':

    该语句的作用是:导入该模块时条件不成立,因而不会执行该语句后面的代码。

  • 局部命名空间(local namespace)
    用来记录函数或类中定义的变量、常量等,可通过 locals() 函数查看。
def func(a):
    b = 2
    c = a + b
    print(locals())
>>> func(3)
{'a': 3, 'b': 2, 'c': 5}

    函数的局部命名空间在调用该函数时创建,在函数返回或抛出不在函数内部处理的异常时销毁。

当Python解释器解释运行程序时,如遇到某个名称(如变量x)时,就会在所有命名空间中进行查找以确定其所映射的对象。查找的顺序如下:

  1. 查找局部命名空间。若找到,则停止。
  2. 若存在父函数,则查找父函数的局部命名空间(闭包)。若找到,则停止。
  3. 查找全局命名空间。若找到,则停止。
  4. 查找内置命名空间。若找到,则停止;若还找不到,Python将报错NameError 。

2. import … 的运行机制

  • import […包].模块 [as 模块别名]

这条语句的执行过程包括以下几个步骤:

(1)判断要导入的模块是否已被加载

导入模块时要分两种情况处理:第一情况是该模块之前已经被加载到内存中了;第二种情况时该模块尚未被加载到内存中。如何判断是哪种情况呢?这就需要用到数据结构 sys.modules

sys模块包含系统级的信息,像正在运行的Python版本(sys.version)、系统级选项、最大允许递归的深度(sys.getrecursionlimit() 和 sys.setrecursionlimit() ) 。sys.modules 是一个全局字典,Python程序启动时就被加载到了内存中。sys.modules记录当前所有已加载模块的信息,字典的键字就是模块名,键值就是模块对象。

>>> import sys
>>> sys.modules
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_io': <module '_io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'posix': <module 'posix' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from '/usr/lib/python3.10/codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from '/usr/lib/python3.10/encodings/aliases.py'>, 'encodings': <module 'encodings' from '/usr/lib/python3.10/encodings/__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from '/usr/lib/python3.10/encodings/utf_8.py'>, '_signal': <module '_signal' (built-in)>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from '/usr/lib/python3.10/abc.py'>, 'io': <module 'io' from '/usr/lib/python3.10/io.py'>, '__main__': <module '__main__' (built-in)>, '_stat': <module '_stat' (built-in)>, 'stat': <module 'stat' from '/usr/lib/python3.10/stat.py'>, '_collections_abc': <module '_collections_abc' from '/usr/lib/python3.10/_collections_abc.py'>, 'genericpath': <module 'genericpath' from '/usr/lib/python3.10/genericpath.py'>, 'posixpath': <module 'posixpath' from '/usr/lib/python3.10/posixpath.py'>, 'os.path': <module 'posixpath' from '/usr/lib/python3.10/posixpath.py'>, 'os': <module 'os' from '/usr/lib/python3.10/os.py'>, '_sitebuiltins': <module '_sitebuiltins' from '/usr/lib/python3.10/_sitebuiltins.py'>, 'apport_python_hook': <module 'apport_python_hook' from '/usr/lib/python3/dist-packages/apport_python_hook.py'>, 'sitecustomize': <module 'sitecustomize' from '/usr/lib/python3.10/sitecustomize.py'>, 'site': <module 'site' from '/usr/lib/python3.10/site.py'>, 'readline': <module 'readline' from '/usr/lib/python3.10/lib-dynload/readline.cpython-310-x86_64-linux-gnu.so'>, 'atexit': <module 'atexit' (built-in)>, '_ast': <module '_ast' (built-in)>, 'itertools': <module 'itertools' (built-in)>, 'keyword': <module 'keyword' from '/usr/lib/python3.10/keyword.py'>, '_operator': <module '_operator' (built-in)>, 'operator': <module 'operator' from '/usr/lib/python3.10/operator.py'>, 'reprlib': <module 'reprlib' from '/usr/lib/python3.10/reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'collections': <module 'collections' from '/usr/lib/python3.10/collections/__init__.py'>, 'types': <module 'types' from '/usr/lib/python3.10/types.py'>, '_functools': <module '_functools' (built-in)>, 'functools': <module 'functools' from '/usr/lib/python3.10/functools.py'>, 'contextlib': <module 'contextlib' from '/usr/lib/python3.10/contextlib.py'>, 'enum': <module 'enum' from '/usr/lib/python3.10/enum.py'>, 'ast': <module 'ast' from '/usr/lib/python3.10/ast.py'>, '_opcode': <module '_opcode' from '/usr/lib/python3.10/lib-dynload/_opcode.cpython-310-x86_64-linux-gnu.so'>, 'opcode': <module 'opcode' from '/usr/lib/python3.10/opcode.py'>, 'dis': <module 'dis' from '/usr/lib/python3.10/dis.py'>, 'collections.abc': <module 'collections.abc' from '/usr/lib/python3.10/collections/abc.py'>, 'importlib._bootstrap': <module '_frozen_importlib' (frozen)>, 'importlib._bootstrap_external': <module '_frozen_importlib_external' (frozen)>, 'warnings': <module 'warnings' from '/usr/lib/python3.10/warnings.py'>, 'importlib': <module 'importlib' from '/usr/lib/python3.10/importlib/__init__.py'>, 'importlib.machinery': <module 'importlib.machinery' from '/usr/lib/python3.10/importlib/machinery.py'>, '_sre': <module '_sre' (built-in)>, 'sre_constants': <module 'sre_constants' from '/usr/lib/python3.10/sre_constants.py'>, 'sre_parse': <module 'sre_parse' from '/usr/lib/python3.10/sre_parse.py'>, 'sre_compile': <module 'sre_compile' from '/usr/lib/python3.10/sre_compile.py'>, '_locale': <module '_locale' (built-in)>, 'copyreg': <module 'copyreg' from '/usr/lib/python3.10/copyreg.py'>, 're': <module 're' from '/usr/lib/python3.10/re.py'>, 'token': <module 'token' from '/usr/lib/python3.10/token.py'>, 'tokenize': <module 'tokenize' from '/usr/lib/python3.10/tokenize.py'>, 'linecache': <module 'linecache' from '/usr/lib/python3.10/linecache.py'>, 'inspect': <module 'inspect' from '/usr/lib/python3.10/inspect.py'>, 'rlcompleter': <module 'rlcompleter' from '/usr/lib/python3.10/rlcompleter.py'>}

除了我们自己导入过的模块,Python在启动时已经预先加载了一些模块,其中大多都是内置(build-in)模块。内置模块是用C语言编写的,Python程序员必须依靠他们来实现系统级的功能,例如文件I/O等。内置模块是Python标准库的重要组成部分。

字典sys.modules对于加载模块起到了缓冲的作用,当使用 import 语句导入模块时,首先会在这个字典中进行查找,确定该模块是否已被加载到内存中。第一种情况:若模块已被加载,则只需将最外层包的名字添加到 import 语句所在模块的局部命名空间中,模块导入的过程就此结束。 第二种情况:若要导入的模块尚未被加载,则继续执行以下步骤。

(2)搜索要导入的模块
Python解释器首先会在内置模块中搜索同名的模块 (注:所以我们自定义的模块不应该与内置模块重名)。若没有找到,则在sys.path中的目录中进行搜索。sys.path是模块查找的路径列表,初始化为系统环境变量PYTHONPATH。

>>> import sys
>>> sys.path
['', '/home/csl/test/py/2', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/csl/.local/lib/python3.10/site-packages', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

可以发现,sys.path列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,交互调用解释器或从标准输入读取脚本),则path[0]是空字符串。path[0]将引导Python首先在当前目录中进行搜索。

若在当前目录中没有找到,则会在sys.path包含其他目录中依次搜索,这些目录中包含了已安装的第三方模块。若仍然没有找到,则会报错ModuleNotFoundError。

(3)加载要导入的模块
搜索到模块之后,接下来就是将其加载到内存中。如何进行加载呢?这里举例进行说明。

现在我们需要在main.py中导入自定义的arithmetic包中的 add 模块,其目录结构如下所示。

在这里插入图片描述
在main.py中,可以采用语句 “import arithmetic.add“ 实现。该语句进行模块加载的过程如下:

  1. 首先运行arithmetic包中的 __init__.py 文件(如果存在的话,该模块用到的所有对象都收录到一块新开辟的内存空间中。创建一个变量指向这个内存空间,用来引用其内容。变量的名字就是包名——arithmetic,从这个意义上说包其实也相当于一个模块。将arithmetic添加到sys.modules中。将arithmetic添加到main模块的局部命名空间中。
  2. 然后运行包arithmetic中要导入的模块add,该模块用到的所有对象也收录到一块新开辟的内存空间中。创建一个变量指向这个内存空间,用来引用其内容。若给模块起了别名,变量名就是模块别名;否则,变量名就是模块名。这里变量名就是模块名——add。将变量arithmetic.add 添加到sys.modules中。将add添加到arithmetic(__init__)模块的局部命名空间中。

下面来验证一下这个过程,在main.py中添加如下代码:

#
# @file main.py
#

import arithmetic.add

print(dir(),'\n')
print(dir(arithmetic),'\n')
print(dir(arithmetic.add),'\n')

代码中的 dir() 是一个内置函数,用于查找模块定义的名称,返回的结果是经过排序的字符串列表。

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'arithmetic'] 

['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'add'] 

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add'] 

可发现确实是将arithmetic 添加到了main模块的命名空间中,将add添加到了arithmetic的命名空间中。整个过程如下图所示:


模块加载结束后,导入的模块就可以使用了。从上图也可以发现:在使用导入的模块中的对象时,前面必须加上包名和模块名。例如在main.py中使用add模块中定义的add函数,代码如下:

#
# @file main.py
#

import arithmetic.add

print(arithmetic.add.add(3,5))
  • import 包 [as 包别名]

这条语句的执行过程也是类似的。前面说过包本质上就是模块,导入包其实就是导入包下面的__init__模块

注:若包中没有__init__.py 文件或者该文件为空,则只会将包名或者包别名添加到import语句所在模块的局部命名空间中,而不会导入包中的其他模块。

那使用语句 “import 包 [as 包别名]” 导入模块时 __init__.py 该如何编写呢?举个例子,包arithmetic中的add、sub、mul 和 dev四个模块,分别实现了加、减、乘、除四则运算。现在需要在main.py 文件中同时使用这四种运算,若采用语句“import […包].模块 [as 模块别名]”导入模块,代码如下:

#
# @file main.py
#

import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev

也就是说当需要使用四则运算时,就需要把arithmeitc包中的模块逐个导入一遍,很麻烦!

可以采用“import 包 [as 包别名]” 导入模块,在 __init__.py 中添加如下代码:

#
# @file __init__.py
#

import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev

此时,在main.py 文件只需编写代码:

#
# @file main.py
#

import arithmetic

__init__.py 文件的作用不止于此。利用__init__.py,可以对外提供类型、变量和接口,对用户隐藏各个子模块的实现。一个模块的实现可能非常复杂,需要用很多个文件,甚至很多子模块来实现,但用户可能只需要知道一个类型和接口。就像我们的arithmetic例子中,用户只需要知道四则运算有add、sub、mul、dev四个接口,并不需要关心它们是怎么实现的,也没有必要去了解arithmetic中是如何组织各个子模块的。由于各个子模块的实现有可能非常复杂,而对外提供的类型和接口有可能非常的简单,我们就可以通过这个方式来对用户隐藏实现,同时提供非常方便的使用。

在编写__init__.py 文件时,应遵循以下原则:(1) 不要污染现有的命名空间。使用模块的一个目的就是为了避免命名冲突,如果在使用__init__.py时违背这个原则,则是反其道而为之,也没有使用模块的必要了。(2)只在__init__.py中编写必要的代码,不要做没必要的运算。

3. from … import … 的运行机制

第二类导入方式 “from … import …” 的运行包括以下步骤:

  1. 首先也是通过sys.modules判断要导入的模块是否已经加载过。
  2. 若尚未加载,则按照 “内置模块 --> sys.path路径列表中包含的模块” 的顺序进行搜索。
  3. 加载要导入的模块。

其中,前面两步与第一种方式 “import …” 是相同的;只是第3步,二者有所区别。因此,接下来只须介绍第3步模块加载这部分内容,仍用前面的例子进行说明。

在这里插入图片描述

  • from 包.[…包] import 模块 [as 模块别名] / *

在main.py中导入自定义的arithmetic包中的 add 模块,采用这种方式可写成 “from arithmetic import add”。该语句的模块加载过程如下:

  1. 首先运行arithmetic包中的 __init__.py 文件(如果存在的话),该模块用到的所有对象都收录到一块新开辟的内存空间中。创建一个变量指向这个内存空间,用来引用其内容。变量的名字就是包名——arithmetic。将arithmetic添加到sys.modules中。注:这里不会将arithmetic添加到main模块的局部命名空间中。

  2. 然后运行包arithmetic中要导入的模块add,该模块用到的所有对象也收录到一块新开辟的内存空间中。创建一个变量指向这个内存空间,用来引用其内容。若给模块起了别名,变量名就是模块别名;否则,变量名就是模块名。这里变量名就是模块名——add。将变量arithmetic.add 添加到sys.modules中。将add添加到main模块的局部命名空间中。

同样可以验证一下这个过程:

#
# @file main.py
#

import from arithmetic import add

print(locals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f0f9af417e0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/csl/test/py/2/main.py', '__cached__': None, 'add': <module 'arithmetic.add' from '/home/csl/test/py/2/arithmetic/add.py'>}

可以发现该执行后,main模块的局部命名空间中确实增加了"add"这个名称,其所对应的就是arithmetic包中的add模块。因而,在main模块中,可以直接使用模块名来引用模块中的对象。

#
# @file main.py
#

import from arithmetic import add

print(add.add(3,5))

如果要在main中导入arithmetic包中的add、sub、mul、dev 四个模块,使用 “from … import …” 语句也是可以的。除了逐个进行导入的方法,还可以采用如下方法实现:
(1)在main.py 文件中添加以下代码:

#
# @file main.py
#

import from arithmetic import *

在包arithmetic中的__init__.py文件中,将变量 __all__ 设置为要导入的模块列表:

#
# @file __init__.py
#

__all__ = ['add', 'sub', 'mul', 'dev']

__all__ 通常是与 * 配合使用的。若在__init__.py文件中没有设置__all__变量,语句 “import from arithmetic import *” 仅会执行包arithmetic中的__init__.py文件。

  • from […包].模块 import 变量/函数/类 [as 别名]/ *

使用“from … import …” 语句不仅可以精确地从包中导入某些模块,还可以精确地从模块中导入某些对象(如变量、函数、类等)。例如在main.py文件中,需要使用arithmetic包里面add模块中定义的加法函数add(), 就可以写成“import from arithmetic.add import add” 。该语句会直接将add模块中的add() 函数的名字直接添加到main模块的局部命名空间中。

#
# @file main.py
#

import from arithmetic.add import add
print(locals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f62f74697e0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/csl/test/py/2/main.py', '__cached__': None, 'add': <function add at 0x7f62f74d5fc0>}

因此,在main中可以直接使用函数名add完成加法运算:

#
# @file main.py
#

import from arithmetic.add import add
print(add(3,5))

注:如果要导入一个模块中所有的对象,可以写成 “from […包].模块 import *

五. 导入模块时的一些注意事项

1. 绝对导入与相对导入

第四部分在介绍 “import 包 [as 包别名]” 时,说过在main.py 中导入arithmetic中的add、sub、mul、dev四个模块可以采用如下方式实现:
在这里插入图片描述

#
# @file __init__.py
#

import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev
#
# @file main.py
#

import arithmetic

其中,__init__.py文件就是使用绝对路径名来导入模块的,称为绝对导入。这种导入方式中,要导入模块的路径都是从"根节点"开始的。“根节点的位置”由sys.path中的路径决定。

在这个例子中,__init__.py 要导入模块的绝对路径就是:“sys.path中的当前路径/arithmetic/add”, “sys.path中的当前路径/arithmetic/sub” …

与绝对导入相对的是相对导入,即使用相对路径名导入模块。在这个例子中,__init__.py 要导入的add 、sub、mul、dev模块都位于同一个路径下。因此,__init__.py采用相对导入的代码如下:

#
# @file __init__.py
#

from . import add
from . import sub
from . import mul
from . import dev

其中, “.” 就表示当前路径。另外,“..” 表示上一级路径。

使用相对导入的好处是:只需关心要导入模块的相对位置。因此,即便将包arithmetic的名字修改,采用相对导入的方式仍然可以正确运行。而若采用绝对导入,则必须同步修改__init__.py的代码。需要注意的是:采用相对导入的模块不能在包的内部直接运行(会报错)。关于相对导入的更多信息,可参考使用相对路径名导入包中子模块。

2. 重新加载模块

导入模块后,模块就已经被加载到内存中了,此后若对模块代码进行了改动,读取的内容仍然是内存中原来的结果。此时,需要使用imp.reload()来重新加载先前加载的模块。

>>> import arithmetic.add
>>> import imp
>>> imp.reload(arithmetic.add)

3. 模块的循环导入问题

模块的导入必须是单链的,而不能有循环导入,否则就会出错。
在这里插入图片描述
具体原理参考Python 3.x | 史上最详解的 导入(import)

六. 参考资料

以上是我学习Python中import语句相关知识的总结(运行环境: 操作系统ubuntu22.04, Python版本 3.10.6),其中可能存在错误。在学习的过程中主要参考了以下资料:

Python–模块与包(https://article.itxueyuan.com/lMj6K)

__init__.py的神奇用法(https://zhuanlan.zhihu.com/p/115350758)

Python 3.x | 史上最详解的 导入(import)(https://blog.csdn.net/weixin_38256474/article/details/81228492)

Python Cookbook 3rd Edition Documentation(https://python3-cookbook.readthedocs.io/zh_CN/latest/index.html)

Modules(https://docs.python.org/3/tutorial/modules.html)

python 内置命名空间、标准库、模块相关概念 (https://www.cnblogs.com/goldsunshine/p/15085333.html)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/124454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

案例丨多元业态管理服务厂商如何走通数字化转型之路

对于多元业态管理服务厂商来说&#xff0c;不同业态客户的使用习惯不一样&#xff0c;从而导致服务过程中的服务有所区别&#xff0c;是这类服务厂商数字化转型的核心需求。下面就以全国领先的阳光智博为例&#xff0c;看下他们是怎样数字化转型的。 一、企业介绍 阳光智博服务…

ASEMI整流二极管A7二极管和M7二极管能代换吗

编辑-Z A7二极管和M7二极管不仅外观封装很像&#xff0c;各项参数也是非常接近的&#xff0c;那么A7二极管和M7二极管能代换吗&#xff1f;我们先来看看他们的详细参数对比&#xff1a; A7二极管参数&#xff1a; 型号&#xff1a;A7二极管 封装&#xff1a;SOD-123 最大重…

Docker- 7.1、跨主机网络-macvlan

一、macvlan介绍 macvlan 本身是 linxu kernel 模块&#xff0c;其功能是允许在同一个物理网卡上配置多个 MAC 地址而实现虚拟多块网卡&#xff0c;即多个 interface&#xff0c;每个 interface 可以配置自己的IP。macvlan 本质上是一种网卡虚拟化技术。macvlan 的最大优点是性…

教你这样找到Mac“其他”文件并删除它

当我们通过「关于本机」>「存储空间」查看硬盘的空间占用情况时。系统会将存储空间根据不同文件类别所占的空间大小显示在条状图上&#xff0c;大部分类型看文字都比较好理解&#xff0c;但对于“其他”这一类很多小伙伴都感觉很困惑&#xff0c;会产生一些问题如&#xff1…

如何在PPT中嵌入交互式图表?LightningChart助力炫酷展示

我们在PPT演示文稿中嵌入图表很容易&#xff0c;但嵌入交互式图表似乎就没听说过了&#xff0c;接下来我们就一起来看看通过交互式图表在PPT中展示病人心跳的效果&#xff1a; PPT中展示病人心跳下方是一个实时地图在PPT中的展现实例 LightningChart2以上在PPT中展示实时交互的…

Nacos服务注册发现、配置管理

Nacos服务注册发现 引入依赖 <dependencyManagement><dependencies><!-- nacos管理依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>…

T-SQL程序练习03

目录 一、利用 &#x1d446;&#x1d452;&#x1d44e;&#x1d460;&#x1d45c;&#x1d45b; 表&#xff0c;参照结果&#xff0c;写一个存储过程&#x1d446;&#x1d452;&#x1d44e;&#x1d460;&#x1d45c;&#x1d45b;&#x1d43f;&#x1d44e;&#x1d4…

【机器学习】ID3_C4.5_CART算法总结与对比

问题 ID3、C4.5、CART算法总结与对比 前言 ID3、C4.5、CART算法是三种不同的决策树算法&#xff0c;区别主要在最优划分属性的选择上&#xff0c;下面把之前在随机森林中汇总过的复制过来&#xff0c;然后再总结下三者的不同。 三种算法所用的最优属性选择方法详述 信息增…

知识图谱系统课程笔记(二)——知识抽取与挖掘

知识图谱系统课程笔记&#xff08;二&#xff09;——知识抽取与挖掘 文章目录知识图谱系统课程笔记&#xff08;二&#xff09;——知识抽取与挖掘OWL、RDF与RDFS关系知识抽取任务定义和相关比赛知识抽取技术知识获取关键技术与难点知识抽取的子任务实体抽取命名实体识别非结构…

【微服务架构实战】第2篇之Spring Cloud Gateway介绍

我们的项目采用 Spring Cloud Gateway gateway 的版本为&#xff1a;2.2.8.RELEASE 简介&#xff1a; Spring Cloud Gateway&#xff08;2.2.8.RELEASE&#xff09; 是 Spring 公司基于 Spring 5.0&#xff0c; Spring Boot 2.0 和 Project Reactor 等技术开发的网关&#xf…

Linux系统运行时参数命令--网络IO性能监控

目录 5 网络IO性能监控 5.1 性能指标 5.2 网络信息 5.2.1 套接字信息 5.2.2 协议栈统计信息-netstat命令 5.2.3 网络吞吐-sar命令 5.2.4 连通性和延时 5.3 其他常用的网络相关命令 telnet nc mtr连通性测试 nslookup traceroute iptraf强大的网络监控 tcpdump- …

(二十五)Vue之mixin混入/合

文章目录mixin局部混入全局混入Vue学习目录 上一篇&#xff1a;&#xff08;二十四&#xff09;Vue之props配置项 下一篇&#xff1a;&#xff08;二十六&#xff09;Vue之插件 mixin 在有些业务逻辑中&#xff0c;发现在多个组件的功能业务逻辑一样&#xff0c;可以把多个…

秋招荆棘之路

找工作期间&#xff0c;在牛客网上面看到很多前辈的面经和经验总结&#xff0c;给我面试带来了很多的帮助&#xff0c;本着互助原则&#xff0c;我也总结下自己秋招的经验&#xff0c;算是一种反馈了&#xff0c;给即将毕业的学弟学妹问一些经验。 1.1 背景 首先介绍下我的背景…

Project 2: CS61BYoG

Project 2主要分为两个部分&#xff1a;phase 1&#xff08;World Generation&#xff09;和 phase 2&#xff08;Interactivity&#xff09;。 最难的个人认为是phase 1中的生成随机地图的算法。这里也主要总结以下phase1。 自己一开始在这个位置卡了一天&#xff0c;一开始做…

Android动态代理源码分析

前言 前面我们简单介绍了代理模式的使用Android设计模式详解之代理模式&#xff0c;我们也明白了动态代理模式的重要性&#xff0c;那动态代理究竟是如何实现将方法交给InvocationHandler.invoke执行的呢&#xff1f;本文就从源码的角度一步步去探究这个问题&#xff1b; 源码…

开发工具与低代码开发平台丨上海道宁联合Grapecity为您提供各类软件开发工具和服务

Grapecity 提供软件开发技术和低代码平台 通过各类软件开发工具和服务 提升开发效率 推动软件产业发展 葡萄城研发各类开发控件和 商业智能报表工具 提供控件技术和数据分析工具 基于专业控件的技术积累 发布低代码开发平台 开发商介绍 葡萄城成立于1980年&#xff0c…

C# XML基本操作

一 XML的内容 二 XML的处理方式 1 DOM 文档对象模型&#xff08;Document Object Model&#xff09; 2 SAX XML解析的简单API&#xff08;Simple API for XML&#xff09; 3 .NET提供了XML支持&#xff1a;System.XML名称空间 三 常用的XML类 1 XmlDocument .LoadXml()…

linux安装mongodb的详细教程

目录 一、下载MongoDB Community Server(社区版) 三、启动mongodb 四、利用配置文件启动mongodb服务 一、下载MongoDB Community Server(社区版) 1.1、下载地址 Download MongoDB Community Server | MongoDB 1.2、选择安装版本 二、上传下载后的mongodb安装包&#xff0c;如…

vue路由写法小结

咱们先说路由的匹配语法&#xff0c;动态路由在项目中经常用到&#xff0c;在某些情况下我们要对路径参数进行正则表达式校验。 基础用法&#xff1a; 使用正则表达式后&#xff08;仅匹配数字&#xff09;&#xff1a; 加入*传入多个params参数&#xff0c;此时参数可以传多个…

使用Nordic的nrf52832进行主从机连接抓包分析

主机使用例程&#xff1a;nRF5_SDK_17.1.0_ddde560\examples\ble_central\ble_app_blinky_c\pca10040\s132\arm5_no_packs 从机使用例程&#xff1a;nRF5_SDK_17.1.0_ddde560\examples\ble_peripheral\ble_app_blinky\pca10040\s132\arm5_no_packs nrf528321. 空中数据包格式1.…