python模块导入的细节详解


Posted in Python onDecember 10, 2018

python模块导入细节

本文主要介绍了关于python模块导入的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

官方手册:https://docs.python.org/3/tutorial/modules.html

可执行文件和模块

python源代码文件按照功能可以分为两种类型:

  • 用于执行的可执行程序文件
  • 不用与执行,仅用于被其它python源码文件导入的模块文件

例如文件a.py和b.py在同一目录下,它们的内容分别是:

# b.py
x="var x in module b"
y=5

# a.py:
import b
import sys
print(b.x)
print(b.y)

a.py导入其它文件(b.py)后,就可以使用b.py文件中的属性(如变量、函数等)。这里,a.py就是可执行文件,b.py就是模块文件,但模块名为b,而非b.py。

python提供了一些标准库,是预定义好的模块文件,例如上面的sys模块。

在此有几个注意点,在后面会详细解释:

  1. 模块b的文件名为b.py,但import导入的时候,使用的名称为b,而非b.py
  2. a.py和b.py是在同一个目录下的,如果不在同目录下能否导入?
  3. 在a.py中访问b.py模块中的属性时,使用的是b.xb.y
  4. 上面都是直接以模块名导入的,python还支持更复杂的包导入方式,例如导入abc/b.py时,使用import abc.b。下一篇文章会详细解释包的导入方式

python模块搜索路径

在a.py中导入模块b的时候,python会做一系列的模块文件路径搜索操作:b.py在哪里?只有找到它才能读取、运行(装载)该模块。

在任何一个python程序启动时,都会将模块的搜索路径收集到sys模块的path属性中(sys.path)。当python需要搜索模块文件在何处时,首先搜索内置模块,如果不是内置模块,则搜索sys.path中的路径列表,搜索时会从该属性列出的路径中按照从前向后的顺序进行搜索,并且只要找到就立即停止搜索该模块文件(也就是说不会后搜索的同名模块覆盖先搜索的同名模块)。

例如,在a.py文件中输出一下这个属性的内容:

# a.py:
import sys
print(sys.path)

结果:

['G:\\pycode', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']

python模块的搜索路径包括几个方面,按照如下顺序搜索:

  • 程序文件(a.py)所在目录,即G:\\pycode
  • 环境变量PYTHONPATH所设置的路径(如果定义了该环境变量,则从左向右的顺序搜索)
  • 标准库路径
  • .pth文件中定义的路径

需要注意,上面sys.path的结果中,除了.zip是一个文件外,其它的搜索路径全都是目录,也就是从这些目录中搜索模块X的文件X.py是否存在。

程序所在目录

这个目录是最先搜索的,且是python自动搜索的,无需对此进行任何设置。从交互式python程序终输出sys.path的结果:

>>> sys.path
['', 'C:\\WINDOWS\\system32', 'C:\\Program Files (x86)\\Python36-32\\Lib\\idlelib', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']

其中第一个''表示的就是程序所在目录。

注意程序所在目录和当前目录是不同的。例如,在/tmp/目录下执行/pycode中的a.py文件

cd /tmp
python /pycode/a.py

其中/tmp为当前目录,而/pycode是程序文件a.py所在的目录。如果a.py中导入b.py,那么将首先搜索/pycode,而不是/tmp。

环境变量PYTHONPATH

这个变量中可以自定义一系列的模块搜索路径列表,这样可以跨目录搜索(另一种方式是设置.pth文件)。但默认情况下这个环境变量是未设置的。

在windows下,设置PYTHONPATH环境变量的方式:命令行中输入:SystemPropertiesAdvanced-->环境变量-->系统环境变量新建

python模块导入的细节详解

如果是多个路径,则使用英文格式的分号分隔。以下是临时设置当前命令行窗口的PYTHONPATH:

set PYTHONPATH='D:\pypath; d:\pypath1'

在unix下,设置PYTHONPATH环境变量的方式,使用冒号分隔多个路径:另外,必须得export导出为环境变量

export PYTHONPATH=/tmp/pypath1:/tmp/pypath2

如果要永久生效,则写入配置文件中:

echo 'export PYTHONPATH=/tmp/pypath1:/tmp/pypath2' >/etc/profile.d/pypth.sh
chmod +x /etc/profile.d/pypth.sh
source /etc/profile.d/pypth.sh

标准库路径

在Linux下,标准库的路径一般是在/usr/lib/pythonXXX/下(XXX表示python版本号),此目录下有些分了子目录。

例如:

['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']

其中/usr/lib/python3.5和其内的几个子目录都是标准库的搜索路径。

注意其中/usr/lib/python35.zip,它是ZIP文件组件,当定义此文件为搜索路径时,将自动解压缩该文件,并从此文件中搜索模块。

Windows下根据python安装位置的不同,标准库的路径不同。如果以默认路径方式安装的python,则标准库路径为C:\\Program Files (x86)\\Python36-32及其分类的子目录。

.pth文件自定义路径

可以将自定义的搜索路径放进一个.pth文件中,每行一个搜索路径。然后将.pth文件放在python安装目录或某个标准库路径内的sitepackages目录下即可。

这是一种替换PYTHONPATH的友好方式,因为不同操作系统设置环境变量的方式不一样,而以文件的方式记录是所有操作系统都通用的。

例如,windows下,在python安装目录C:\\Program Files (x86)\\Python36-32下新增一个mypath.pth文件,内容如下:

d:\pypath1
d:\pypath2

再去输出sys.path,将可以看到这两个路径已经放进了搜索列表中。

修改搜索路径

除了上面环境变量和.pth文件,还可以直接修改sys.path或者site.getsitepackages()的结果。

例如,在import导入sys模块之后,可以修改sys.path,向这个列表中添加其它搜索路径,这样之后导入其它模块的时候,也会搜索该路径。

例如:

import sys
sys.path.append('d:\\pypath3')
print(sys.path)

sys.path的最后一项将是新添加的路径。

导入模块的细节

导入模块时的过程

python的import是在程序运行期间执行的,并非像其它很多语言一样是在编译期间执行。也就是说,import可以出现在任何地方,只有执行到这个import行时,才会执行导入操作。且在import某个模块之前,无法访问这个模块的属性。

python在import导入模块时,首先搜索模块的路径,然后编译并执行这个模块文件。虽然概括起来只有两个过程,但实际上很复杂。

前文已经解释了import的模块搜索过程,所以这里大概介绍import的其它细节。

以前面的a.py中导入模块文件b.py为例:

import b

import导入模块时,搜索到模块文件b.py后:

1.首先在内存中为每个待导入的模块构建module类的实例:模块对象。这个模块对象目前是空对象,这个对象的名称为全局变量b。

注意细节:module类的对象,变量b。

输出下它们就知道:

print(b)
print(type(b))

输出结果:

<module 'b' from 'g:\\pycode\\b.py'>
<class 'module'>

因为b是全局变量,所以当前程序文件a.py中不能重新对全局变量b进行赋值,这会使导入的模块b被丢弃。例如,下面是错误的:

import b
b=3
print(b.x) # 已经没有模块b了

另外,因为import导入时是将模块对象赋值给模块变量,所以模块变量名不能是python中的一些关键字,比如if、for等,这时会报错。虽然模块文件名可以为list、keys等这样的内置函数名,但这会导致这些内置函数不可用,因为根据变量查找的作用域规则,首先查找全局变量,再查找内置作用域。也就是说,模块文件的文件名不能是这些关键字、也不应该是这些内置函数名。

File "g:/pycode/new.py", line 11
 import if
 ^
SyntaxError: invalid syntax

2.构造空模块实例后,将编译、执行模块文件b.py,并按照一定的规则将一些结果放进这个模块对象中。

注意细节,编译、执行b.py、将结果保存到模块对象中。

模块第一次被导入的时候,会进行编译,并生成.pyc字节码文件,然后python执行这个pyc文件。当模块被再次导入时,如果检查到pyc文件的存在,且和源代码文件的上一次修改时间戳mtime完全对应(也就是说,编译后源代码没有进行过修改),则直接装载这个pyc文件并执行,不会再进行额外的编译过程。当然,如果修改过源代码,将会重新编译得到新的pyc文件。

注意,并非所有的py文件都会生成编译得到的pyc文件,对于那些只执行一次的程序文件,会将内存中的编译结果在执行完成后直接丢弃(多数时候如此,但仍有例外,比如使用compileall模块可以强制编译成pyc文件),但模块会将内存中的编译结果持久化到pyc文件中。另外,运行字节码pyc文件并不会比直接运行py文件更快,执行它也一样是一行行地解释、执行,唯一快的地方在于导入装载的时候无需重新编译而已。

执行模块文件(已完成编译)的时候,按照一般的执行流程执行:一行一行地、以代码块为单元执行。一般地,模块文件中只用来声明变量、函数等属性,以便提供给导入它的模块使用,而不应该有其他任何操作性的行为,比如print()操作不应该出现在模块文件中,但这并非强制。

总之,执行完模块文件后,这个模块文件将有一个自己的全局名称空间,在此模块文件中定义的变量、函数等属性,都会记录在此名称空间中。

最后,模块的这些属性都会保存到模块对象中。由于这个模块对象赋值给了模块变量b,所以通过变量b可以访问到这个对象中的属性(比如变量、函数等),也就是模块文件内定义的全局属性。

只导入一次

假设a.py中导入了模块b和模块sys,在b.py中也导入了模块sys,但python默认对某个模块只会导入一次,如果a.py中先导入sys,再导入b,那么导入b并执行b.py的时候,会发现sys已经导入了,不会再去导入sys。

实际上,python执行程序的时候,会将所有已经导入的模块放进sys.module属性中,这是一个dict,可以通过下面的方式查看已导入的模块名:

>>> import sys
>>> list(sys.module.keys())

如果某个程序文件中多次使用import(或from)导入同一个模块,虽然不会报错,但实际上还是直接使用内存中已装载好的模块对象。

例如,b.py中x=3,导入它之后修改该值,然后再次导入,发现b.x并不会发生改变:

import b
print(b.x) # 3

b.x=33
print(b.x) # 33

import b 
print(b.x) # 33

但是python提供了reload进行多次重复导入的方法,见后文。

使用别名

import导入时,可以使用as关键字指定一个别名作为模块对象的变量,例如:

import b as bb
bb.x=3
print(bb.x)

这时候模块对象将赋值给变量bb,而不是b,b此时不再是模块对象变量,而仅仅只是模块名。使用别名并不会影响性能,因为它仅仅只是一个赋值过程,只不过是从原来的赋值对象变量b变为变量bb而已。

from导入部分属性

import语句是导入模块中的所有属性,并且访问时需要使用模块变量来引用。例如:

import b
print(b.x)

除了import,还有一个from语句,表示从模块中导入部分指定的属性,且使得可以直接使用这些属性的名称来引用这些属性,而不需要加上模块变量名。例如原来import导入时访问变量x使用b.x,from导入时只需使用x即可。实际上,from导入更应该称为属性的再次赋值(拷贝)。

例如,b.py中定义了变量x、y、z,同时定义了函数f()和g(),在a.py中导入这个模块文件,但只导入x变量和f函数:

# a.py文件内容:
from b import x,f

print(x)
f()

# b.py文件内容:
x=3
y=4
z=5
def f():
 print("function f in b.py")

def g():
 print("function g in b.py")

注意上面a.py中引用模块b中属性的方式没有加上b.X,而是直接使用x和f()来引用。这和import是不一样的。至于from和import导入时的变量名称细节,在下面的内容中会详细解释。

虽然from语句只导入模块的部分属性,但实际上仍然会完整地执行整个模块文件。

同样的,from语句也可以指定导入属性的变量别名,例如,将b.py中的属性x赋值给xx,将y赋值给yy:

from b import x as xx,y as yy
print(xx)
print(yy)

from语句还有一个特殊导入统配符号*,它表示导入模块中的所有属性。

# a.py文件:
from b import *
print(x,y,z)
f()
g()

多数时候,不应该使用from *的方式,因为我们可能会忘记某个模块中有哪些属性拷贝到了当前文件,特别是多个from *时可能会出现属性覆盖的问题。

重载模块:imp.reload()

无论时import还是from,都只导入一次模块,但使用reload()可以强制重新装载模块。

reload()是imp模块中的一个函数,所以要使用imp.reload()之前,必须先导入imp。

from imp import reload
reload(b)

reload()是一个函数,它的参数是一个已经成功被导入过的模块变量(如果使用了别名,则应该使用别名作为reload的参数),也就是说该模块必须在内存中已经有自己的模块对象。

reload()会重新执行模块文件,并将执行得到的属性完全覆盖到原有的模块对象中。也就是说,reload()会重新执行模块文件,但不会在内存中建立新的模块对象,所以原有模块对象中的属性可能会被修改。

例如,模块文件b.py中x=3,导入b模块,修改其值为33,然后reload这个模块,会发现值重新变回了3。

import b
print(b.x) # 3

b.x=33
print(b.x) # 33

from imp import reload
reload(b)

print(b.x) # 3

有时候reload()很有用,可以让程序无需重启就执行新的代码。例如,在python的交互式模式下导入模块b,然后修改python源码,再reload导入:

>>> import b
>>> b.x
3

# 不要关掉交互式解释器,直接修改源代码中的b=3333

>>> from imp import reload
>>> reload(b)
<module 'b' from 'G:\\pycode\\b.py'>
>>> b.x
3333

但正因为reload()重载模块会改变原始的值,这可能是很危险的行为,一定要清楚地知道它是在干什么。

导入模块时的变量名称细节

import导入的变量

import导入时,模块对象中的属性有自己的名称空间,然后将整个模块对象赋值给模块变量。

例如,在a.py中导入b:

import b
print(b.x)

这个过程唯一和当前文件a.py作用域有关的就是模块对象变量b,b.py中声明的属性和当前文件无任何关系。无论是访问还是修改,都是直接修改这个模块对象自身作用域中的值。所以,只要模块变量b不出现冲突问题,可以放心地修改模块b中的属性。

另一方面,因为每个进程都有自己的内存空间,所以在a.py、c.py中都导入b时,a.py中修改b的属性值不会影响c.py中导入的属性,a.py和c.py中模块对象所保存的属性都是执行b.py后得到的,它们相互独立。

from导入的变量

from导入模块时,会先执行完模块文件,然后将指定的部分属性重新赋值给当前程序文件的同名全局变量。

例如,在模块文件b.py中定义了x、y、z变量和f()、g()函数:

# b.py:
x=3
y=4
b=5
def f():
 print("function f in b.py")

def g():
 print("function g in b.py")

当在a.py中导入b模块时,如果只导入x、y和f():

# a.py:
from b import x, y, f

实际上的行为是构造模块对象后,将这个模块对象对应的名称空间中的属性x、y和f重新赋值给a.py中的变量x、y和f,然后丢弃整个模块对象以及整个名称空间。换句话说,b不再是一个有效的模块变量(所以和import不一样),来自b的x,y,z,f和g也都被丢弃。

这里有几个细节,需要详细解释清楚,只有理解了才能搞清楚它们是怎么生效的。

假设现在模块文件b.py的内容为,并且a.py中导入x,y,f属性:

# b.py:
x=3
y=[1,2]
z=5
def f():
 print("function f in b.py")

def g():
 print("function g in b.py")

# a.py:
from b import x,y,f

首先在执行模块文件b.py时,会构造好自己的模块对象,并且模块对象有自己的名称空间(作用域),模块对象构造完成后,它的名称空间大致如下:

python模块导入的细节详解

然后python会在a.py的全局作用域内创建和导入属性同名的全局变量x,y和f,并且通过赋值的方式将模块的属性赋值给这些全局变量,也就是:

x = b.x
y = b.y
f = b.f

上面的b只是用来演示,实际上变量b是不存在的。

赋值完成后,我们和构造的整个模块对象就失去联系了,因为没有变量b去引用这个对象。但需要注意,这个对象并没有被删除,仅仅只是我们无法通过b去找到它。

所以,现在的示意图如下:

python模块导入的细节详解

因为是赋值的方式传值的,所以在a.py中修改这几个变量的值时,是直接在模块对象作用域内修改的:对于不可变对象,将在此作用域内创建新对象,对于可变对象,将直接修改原始对象的值。

另一方面,由于模块对象一直保留在内存中,下次继续导入时,将直接使用该模块对象。对于import和from,是直接使用该已存在的模块对象,对于reload,是覆盖此模块对象。

例如,在a.py中修改不可变对象x和可变对象y,之后import或from时,可变对象的值都会随之改变,因为它们使用的都是原来的模块对象:

from b import x,y

x=33
y[0]=333

from b import x,y
print((x,y)) # 输出(3, [333, 2])

import b
print((b.x,b.y)) # 输出(3, [333, 2])

from导入时,由于b不再是模块变量,所以无法再使用reload(b)去重载对象。如果想要重载,只能先import,再reload:

from b import x,y
...CODE...

# 想要重载b
import b
from imp import reload
reload(b)

查看模块中的属性

内置函数dir可用于列出某模块中定义了哪些属性(全局名称空间)。完整的说明见help(dir)

import b
dir(b)

输出结果:

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'g', 'x', 'y', 'z']

可见,模块的属性中除了自己定义的属性外,还有一些内置的属性,比如上面以__开头和结尾的属性。

如果dir()不给任何参数,则输出当前环境下定义的名称属性:

>>> import b
>>> x=3
>>> aaa=333
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'aaa', 'b', 'x']

每个属性都对应一个对象,例如x对应的是int对象,b对应的是module对象:

>>> type(x)
<class 'int'>
>>> type(b)
<class 'module'>

既然是对象,那么它们都会有自己的属性。例如:

>>> dir(x)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

所以,也可以直接dir某个模块内的属性:

import b
dir(b.x)
dir(b.__name__)

dir()不会列出内置的函数和变量,如果想要输出内置的函数和变量,可以去标准模块builtins中查看,因为它们定义在此模块中:

import builtins
dir(buildins)

除了内置dir()函数可以获取属性列表(名称空间),对象的__dict__属性也可以获取对象的属性字典(名称空间),它们的结果不完全一样。详细说明参见dir()和__dict__属性区别。

总的来说,获取对象M中一个自定义的属性age,有以下几种方法:

M.age
M.__dict__['age']
sys.modules['M'].age
getattr(M,'age')

有妙用的__name__属性

前面说了,py文件分两种:用于执行的程序文件和用于导入的模块文件。当直接使用python a.py的时候表示a.py是用于执行的程序文件,通过import/from方式导入的py文件是模块文件。

__name__属性用来区分py文件是程序文件还是模块文件:

  • 当文件是程序文件的时候,该属性被设置为__main__
  • 当文件是模块文件的时候(也就是被导入时),该属性被设置为自身模块名

换句话说,__main__表示的是当前执行程序文件的默认模块名,想必学过其他支持包功能的语言的人很容易理解:程序都需要一个入口,入口程序所在的包就是main包,在main包中导入其它包来组织整个程序。python也是如此,只不过它是隐式自动设置的。

对于python来说,因为隐式自动设置,该属性就有了特殊妙用:直接在模块文件中通过if __name__ == "__main__"来判断,然后写属于执行程序的代码,如果直接用python执行这个文件,说明这个文件是程序文件,于是会执行属于if代码块的代码,如果是被导入,则是模块文件,if代码块中的代码不会被执行。

显然,这是python中非常方便的单元测试方式。

例如,写一个模块文件,里面包含一个函数,用来求给定序列的最大值和最小值:

def minmax(func,*args):
 res = args[0]
 for arg in args[1:]:
  if func(arg,res):
   res = arg
 return res

def lessthan(x,y): return x < y
def greatethan(x,y): return x > y

# 测试代码
if __name__ == "__main__":
 print(minmax(lessthan,3,6,2,1,4,5))
 print(minmax(greatethan,3,6,2,1,4,5))

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
用Python写的图片蜘蛛人代码
Aug 27 Python
python使用循环实现批量创建文件夹示例
Mar 25 Python
python 动态加载的实现方法
Dec 22 Python
python3如何将docx转换成pdf文件
Mar 23 Python
每天迁移MySQL历史数据到历史库Python脚本
Apr 13 Python
Python面向对象之类和实例用法分析
Jun 08 Python
Django中信号signals的简单使用方法
Jul 04 Python
基于Python实现ComicReaper漫画自动爬取脚本过程解析
Nov 11 Python
浅谈pytorch、cuda、python的版本对齐问题
Jan 15 Python
python pptx复制指定页的ppt教程
Feb 14 Python
Django admin 实现search_fields精确查询实例
Mar 30 Python
Python3爬虫中识别图形验证码的实例讲解
Jul 30 Python
pandas每次多Sheet写入文件的方法
Dec 10 #Python
Python用61行代码实现图片像素化的示例代码
Dec 10 #Python
Python从数据库读取大量数据批量写入文件的方法
Dec 10 #Python
对Python实现简单的API接口实例讲解
Dec 10 #Python
python中dir()与__dict__属性的区别浅析
Dec 10 #Python
python分块读取大数据,避免内存不足的方法
Dec 10 #Python
Python 文本文件内容批量抽取实例
Dec 10 #Python
You might like
改造一台复古桌面收音机
2021/03/02 无线电
PHP简单创建压缩图的方法
2016/08/24 PHP
PHP实现的简单AES加密解密算法实例
2017/05/29 PHP
php高清晰度无损图片压缩功能的实现代码
2018/12/09 PHP
PHP swoole和redis异步任务实现方法分析
2019/08/12 PHP
Json对象与Json字符串互转(4种转换方式)
2013/03/27 Javascript
JS鼠标滑过图片时切换图片实现思路
2013/09/12 Javascript
JS去掉第一个字符和最后一个字符的实现代码
2014/02/20 Javascript
简单选项卡 js和jquery制作方法分享
2014/02/26 Javascript
jQuery中before()方法用法实例
2014/12/25 Javascript
node.js利用redis数据库缓存数据的方法
2017/03/01 Javascript
JS html时钟制作代码分享
2017/03/03 Javascript
前端跨域的几种解决方式总结(推荐)
2017/08/16 Javascript
vue中遇到的坑之变化检测问题(数组相关)
2017/10/13 Javascript
使用Vue开发一个实时性时间转换指令
2018/01/17 Javascript
Js中将Long转换成日期格式的实现方法
2018/06/05 Javascript
layer.alert回调函数执行关闭弹窗的实例
2019/09/11 Javascript
python读写ini配置文件方法实例分析
2015/06/30 Python
python 捕获shell脚本的输出结果实例
2017/01/04 Python
对numpy和pandas中数组的合并和拆分详解
2018/04/11 Python
基于CentOS搭建Python Django环境过程解析
2020/08/24 Python
HTML5轻松实现全屏视频背景的示例
2018/04/23 HTML / CSS
选购世界上最好的美妆品:Cult Beauty
2017/11/03 全球购物
荷兰和比利时时尚鞋店:Van Dalen
2018/04/23 全球购物
Piercing Pagoda官网:耳环、戒指、项链、手链等
2020/09/28 全球购物
Hammitt官网:设计师手袋
2020/05/23 全球购物
高中生自我评语大全
2014/01/19 职场文书
汽车运用工程专业求职信
2014/06/18 职场文书
2014年人力资源工作总结
2014/11/19 职场文书
面试感谢信范文
2015/01/22 职场文书
优秀团员个人总结
2015/02/26 职场文书
暑期社会实践新闻稿
2015/07/17 职场文书
MySQL的安装与配置详细教程
2021/06/26 MySQL
MyBatis 动态SQL全面详解
2021/10/05 MySQL
7个关于Python的经典基础案例
2021/11/07 Python
Mybatis是这样防止sql注入的
2021/12/06 Java/Android