Python中动态导入对象importlib.import_module()的使用

先入为主

背景

一个函数运行需要根据不同项目的配置,动态导入对应的配置文件运行。

解决

文件结构

a #文件夹
	│a.py
	│__init__.py
b #文件夹
	│b.py
	│__init__.py
	├─c#文件夹
		│c.py
		│__init__.py

# c.py 中内容
args = {'a':1}

class C:
    
    def c(self):
        pass

目的
向a模块中导入c.py 中的对象

解决方案

a.py

import importlib

params = importlib.import_module('b.c.c') #绝对导入
params_ = importlib.import_module('.c.c',package='b') #相对导入

# 对象中取出需要的对象
params.args #取出变量
params.C  #取出class C
params.C.c  #取出class C 中的c 方法

以上就是动态函数import_module的使用方法




又如:

https://blog.csdn.net/mouday/article/details/84062182

Python编程:importlib.import_module动态导入模块

环境:python 3.6

文件结构

├── clazz
│   ├── __init__.py
│   ├── a.py
│   └── b.py
└── main.py

a.py 的代码

def show():
    print("show A")

b.py 的代码

def show():
    print("show B")

从main中导入clazz包中的a 和b 模块

main.py

import importlib

# 绝对导入
a = importlib.import_module("clazz.a")
a.show()
# show A

# 相对导入
b = importlib.import_module(".b", "clazz")
b.show()
# show B

注意,相对导入有个一点., 类似路径。

一般来说,只有导入过,使用绝对导入或者直接导入都行,相对导入 b = importlib.import_module(".b", "clazz")才能生效。




更进一步:

Python -V,显示版本号,这里的V是大写的。
https://blog.csdn.net/defending/article/details/78095402
importlib包的目的是双重的。一个是在Python源代码中提供import语句(以及扩展名为__import__()函数)的实现。这提供了可以移植到任何Python解释器的import的实现。这也提供了比在除了Python之外的编程语言中实现的实现更容易理解的实现。
第二个目的是,实现import的组件在此包中公开,使用户更容易创建自己的自定义对象(通常称为importer)以参与导入处理。

importlib.__import__(name, globals=None, locals=None, fromlist=(), level=0)

内建__import__()函数的实现。

注意

程序化导入模块应使用import_module()而不是此函数。

importlib.import_module(name, package=None)

导入模块。name参数指定要以绝对或相对术语导入的模块。pkg.mod或…mod)。如果名称是以相对术语指定的,则包参数必须设置为包的名称,该名称将作为解析包名称的锚点。import_module(’… mod’, ‘pkg.subpkg’)将导入pkg.mod)。

import_module()函数用作importlib.__import__()的简化包装。这意味着函数的所有语义都派生自importlib.import()。这两个函数最重要的区别是import_module()返回指定的包或模块(例如,pkg.mod),而__import__()返回顶级包或模块pkg)。

如果您动态导入自解析器开始执行后创建的模块(例如,创建了一个Python源文件),则可能需要调用invalidate_caches()才能注意到新模块由进口系统。

在版本3.3中更改:父包会自动导入。

importlib.find_loader(name, path=None)

找到模块的加载器,可选择在指定的路径中。如果模块在sys.modules中,则返回sys.modules[name].__loader__(除非加载器None未设置,在这种情况下会引发ValueError)。否则,使用sys.meta_path的搜索完成。如果未找到加载程序,则返回None。

点名称没有父级的隐式导入,因为需要加载它们,这可能不是所希望的。要正确导入子模块,您需要导入子模块的所有父包,并使用正确的参数path。

版本3.3中的新功能。

在版本3.4中更改:如果__loader__未设置,则引发ValueError,就像属性设置为None

自版本3.4后弃用:改用importlib.util.find_spec()

importlib.invalidate_caches()

使存储在sys.meta_path的finder的内部缓存无效。如果finder实现invalidate_caches(),那么它将被调用以执行无效。如果在程序运行时创建/安装了任何模块,则应调用此函数,以确保所有查找程序都注意到新模块的存在。

版本3.3中的新功能。

importlib.reload(module)

重新载入先前导入的模块。参数必须是一个模块对象,因此必须先被成功导入。如果您已使用外部编辑器编辑了模块源文件,并希望在不离开Python解释器的情况下尝试新版本,这将非常有用。返回值是模块对象(如果重新导入会导致将不同的对象放置在sys.modules中,则可能不同)。

当执行reload()时:

Python模块的代码被重新编译并重新执行模块级代码,通过重用最初加载模块的loader来定义一组新的对象,这些对象被绑定到模块字典中的名称。扩展模块的init功能不再第二次调用。
与Python中的所有其他对象一样,旧对象只有在引用计数下降到零后才被回收。
模块命名空间中的名称将更新为指向任何新的或已更改的对象。
对旧对象的其他引用(例如模块外部的名称)不会重新引用来引用新对象,如果需要,必须在每个命名空间中对其进行更新。
还有一些其他警告:

当模块被重新加载时,它的字典(包含模块的全局变量)被保留。名称的重定义将覆盖旧的定义,因此这通常不是问题。如果模块的新版本未定义由旧版本定义的名称,则旧定义将保留。如果它维护一个全局表或对象缓存,使用try语句可以测试表的存在并跳过其初始化,如果需要,这个特性可以用于模块的优势:

try:
    cache
except NameError:
    cache = {}

重新加载内建或动态加载的模块通常不是很有用。不建议重新加载sys__main__builtins和其他关键模块。在许多情况下,扩展模块不会被设计为初始化多次,并且可能在重新加载时以任意方式失败。

如果模块使用from … import …从另一个模块导入对象,则对其他模块调用reload()其中一个方法是重新执行from语句,另一个方法是使用import和限定名称(module.name t15>)。

如果一个模块实例化一个类的实例,重新加载定义该类的模块不会影响实例的方法定义 - 它们继续使用旧的类定义。对于派生类同样如此。

版本3.4中的新功能。

详情 http://python.usyiyi.cn/translate/python_352/library/importlib.html#module-importlib




补充一个例子

https://blog.csdn.net/jiaming917/article/details/85792543

Importlib模块与python内置函数__import__详解

**Importlib模块与__import__都可以通过过字符串来导入另外一个模块,但在用法上和本质上都有很大的不同。

应该推荐使用 importlib 的方式

以一个例子为证:
以下为我的工程目录结构**
在这里插入图片描述

lib/test.py:

 name = "Jack"

def getName():
    print(name)
    return name

demo.py:

 name = "Amy"

def getName():
    print(name)
    return name

Importlib模块的例子:

testImportlib.py:

 import importlib

mName = "lib.test"
module = importlib.import_module(mName)
module.getName()

Importlib是python的一个库,通过导入importlib,调用import_module()方法,传入用户想要获取的模块对应的路径字符串,即可获取一个,模块module,module可以调用这个test模块下的所有属性和方法。

运行截图:
在这里插入图片描述

__import__的例子:

import__是python的一个内置方法,直接调用__import()即可获取一个模块.

testImport.py:

 mName = "demo"

module = __import__(mName)
module.getName()

在这里插入图片描述
此时调用的是相同目录下的demo模块,可顺利地获取这个模块,那么当不再同一个目录下呢?

修改testImport.py:

 mName = "lib.test"

module = __import__(mName)
module.getName()

此时的运行结果为:
在这里插入图片描述
出错提示为,当前的模块’lib’没有getName()这个方法,因此没有正确获取到test模块。
继续修改此文件:

mName = "lib.test"

module = __import__(mName,fromlist = ('test',))
module.getName()

运行结果为:
在这里插入图片描述
当加上fromlist属性即可正确获取。




深入理解

https://www.cnblogs.com/meishandehaizi/p/5863233.html
感觉这篇也太像了,但加了点东西。。https://blog.csdn.net/xc_zhou/article/details/80921546

介绍importlib

Python将importlib作为标准库提供。它旨在提供Pythonimport语法和(__import__()函数)的实现。另外,importlib提供了开发者可以创建自己的对象(即importer)来处理导入过程。

Python提供了importlib包作为标准库的一部分。目的就是提供Python中import语句的实现(以及__import__函数)。另外,importlib允许程序员创建他们自定义的对象,可用于引入过程(也称为importer)。

那么imp呢?还有一个imp模块提供了import语句接口,不过这个模块在Python3.4已经deprecated了。建议使用importlib来处理。

什么是imp?
另外有一个叫做imp的模块,它提供给Python import语句机制的接口。这个模块在Python 3.4中被否决,目的就是为了只使用importlib。

这个模块比较复杂,文中我们主要探讨如下主题:

  1. 动态导入
  2. 检查模块是否可以被导入
  3. 从源文件导入 ,引入源文件自身
  4. 第三方模块 import_from_github_com
    我们先从动态导入开始。

动态导入

importlib模块支持传递字符串来导入模块。我们先来创建一些简单模块一遍演示。我们在模块里提供了相同接口,通过打印它们自身名字来区分。我们分别创建了foo.pybar.py,代码如下:

def main():
    print(__name__)

现在我们尽需要使用importlib导入它们。我们来看看代码是如何实现的,确保该代码在刚才创建的两个文件的相同目录下。

#importer
import importlib

def dynamic_import(module):
    return importlib.import_module(module)


if __name__ == "__main__":
    module = dynamic_import('foo')
    module.main()

    module2 = dynamic_import('bar')
    module2.main()

这里我们导入importlib模块,并创建了一个非常简单的函数dynamic_import。这个函数直接就调用了importlib的import_module方法,并将要导入的模块字符串传递作为参数,最后返回其结果。然后在主入口中我们分别调用了各自的main方法,将打印出各自的name.

$ python3 importer.py 
foo
bar

也许你很少会代码这么做,不过在你需要试用字符串作为导入路径的话,那么importlib就有用途了。

模块导入检查

Python有个众所周知的代码风格EAFP: Easier to ask forgiveness than permission.它所代表的意思就是总是先确保事物存在(例如字典中的键)以及在犯错时捕获。如果我们在导入前想检查是否这个模块存在而不是靠猜。 使用mportlib就能实现。

import importlib.util

def check_module(module_name):
    """
    Checks if module can be imported without actually
    importing it
    """
    module_spec = importlib.util.find_spec(module_name)
    if module_spec is None:
        print("Module: {} not found".format(module_name))
        return None
    else:
        print("Module: {} can be imported".format(module_name))
        return module_spec

def import_module_from_spec(module_spec):
    """
    Import the module via the passed in module specification
    Returns the newly imported module
    """
    module = importlib.util.module_from_spec(module_spec)
    module_spec.loader.exec_module(module)
    return module

if __name__ == '__main__':
    module_spec = check_module('fake_module')
    module_spec = check_module('collections')
    if module_spec:
        module = import_module_from_spec(module_spec)
        print(dir(module))

这里我导入了importlib的子模块util。check_module里面调用find_spec方法, 传递该模块字符串作为参数。当我们分别传入了一个不存在和存在的Python模块。你可以看到当你传入不存在的模块时,find_spec函数将返回 None,在我们代码里就会打印提示。如果存在我们将返回模块的specification。

我们可以通过该模块的specification来实际导入该模块。或者你直接将字符串作为参数调用import_module函数。不过我这里也学习如何试用模块specification方式导入。看看import_module_from_spec函数。它接受check_module提供的模块specification作为参数。然后我们将它传递给了module_from_spec函数,它将返回导入模块。Python文档推荐导入后然后执行模块,所以接下来我们试用exec_module函数执行。最后我们使用dir来确保得到预期模块。

从源代码导入

importlib的子模块有个很好用的技巧我想提提。你可以使用util通过模块的名字和路径来导入模块。

import importlib.util

def import_source(module_name):
module_file_path = module_name.file
module_name = module_name.name

module_spec = importlib.util.spec_from_file_location(
    module_name, module_file_path
)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
print(dir((module)))

msg = 'The {module_name} module has the following methods {methods}'
print(msg.format(module_name=module_name, methods=dir(module)))

if name == “main”:
import logging
import_source(logging)
在上面的代码中,我们实际导入logging模块,并将模块传递给了import_source函数。这样我们就可以通过导入的模块获取到实际的路 径和名字。然后我们将信息传递给sec_from_file_location函数,它将返回模块的说明specification。一旦我们获取到模块的说明,我们就可以像前面那样直接通过importlib导入了。

import_from_github_com

这个精巧的包叫做import_from_github_com,它可以用于发现和下载github上的包。为了安装他,你需要做的就是按照如下命令使用pip,

pip install import_from_github_com
1
这个包使用了PEP 302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要Python 3.2或者更高的版本,git和pip才能使用这个包。

一旦这些已经安装,你可以在Python shell中输入如下命令,

>>> from github_com.zzzeek import sqlalchemy
Collecting git+https://github.com/zzzeek/sqlalchemy
Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build
Installing collected packages: SQLAlchemy
Running setup.py install for SQLAlchemy ... done
Successfully installed SQLAlchemy-1.1.0b1.dev0
>>> locals()
{'__builtins__': <module 'builtins' (built-in)>, '__spec__': None,
'__package__': None, '__doc__': None, '__name__': '__main__',
'sqlalchemy': <module 'sqlalchemy' from '/usr/local/lib/python3.5/site-packages/\
sqlalchemy/__init__.py'>,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>}

你如果看了import_from_github_com的源码,你将会注意到它并没有使用importlib。实际上,它使用了pip来安装那些没有安装的包,然后使用Python的import()函数来引入新安装的模块。这段代码非常值得学习。

总结

目前,你知道如何在代码中使用importlib和import钩子。当然还有很多超出本文所覆盖的知识,这个模块内容非常多,如果你想自定义importer引入器或者loader下载器,那么你可以通过官方文档或者源代码了解更多,但你需要花费很多时间来阅读官方文档和源码。




这里加入一个引入源码的例子:

https://blog.csdn.net/hhczy1003/article/details/76662184

python 动态引入模块 importlib 和 import 不一样

如图所示,应该推荐使用 importlib 的方式。

scrapy 的源码也正是使用的这种方式
在这里插入图片描述
附scrapy 源码中 scrapy/utils/misc.py 中引入源码的load_object 实现:

def load_object(path):
    """Load an object given its absolute object path, and return it.
    object can be a class, function, variable or an instance.
    path ie: 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware'
    """
 
    try:
        dot = path.rindex('.')
    except ValueError:
        raise ValueError("Error loading object '%s': not a full path" % path)
 
    module, name = path[:dot], path[dot+1:]
    mod = import_module(module)
 
    try:
        obj = getattr(mod, name)
    except AttributeError:
        raise NameError("Module '%s' doesn't define any object named '%s'" % (module, name))
 
    return obj

在工程中,既然要自动化测试,少不了要导入测试用例。测试用例也使用Python编写,但只有在运行时,才知道所要加载的测试用例的名字。因此,需要在测试工具的框架代码中,加入动态导入某个Python代码的功能。

在Python3.4中,动态导入Module使用的是importlib包的import_module接口(也可以用内建函数__import__,但前一个更好理解)。

用法如下:

import importlib

name = input( 'Please input module name that will be imported:' )
_module = importlib.import_module( name )
_module.some_interface()

在这里,name可以是绝对或相对形式,如pkg.mod或者…mod,可以对应为某个package或者某个路径。例如,如果测试用例first.py在./cases/路径下,则可以输入:cases.first。




稍微介绍一下imp库,尽管现在这个已经用的很少了。

https://blog.csdn.net/qq_41553182/article/details/84798961
当我运行程序的时候,Python Shell给我显示:the imp module is deprecated in favour of importlib

上网搜一下原因,是因为Python3.4版本以上源代码中的引用由imp自动变为importlib.

解决方法只要按照程序报错中指示的路径,如C:\Python36-32\lib\sit-packages\sklearn\externals\joblib\externals\cloudpickle.py

用记事本或者Python编译器打开。
在这里插入图片描述
如图,把imp修改为importlib.

然后保存。

如果出现类似 Error 13:pemission denied.是因为这个Python文件对当前电脑登陆用户授予的权限中没有“修改”这一项。

右击“属性”,找到“安全”这一栏,选中User,在下面“修改”这一栏中的“允许”打上勾即可。

然后再回来修改cloudpickle文件的源代码,就不会再出现权限的问题。
在这里插入图片描述
在这里插入图片描述




https://img-blog.csdn.net/20160831203951063

imp库,python进入import内部

imp模块提供了一个可以实现import语句的接口。
使用imp可以用来导入模块和类。

imp.PY_SOURCE —-1
imp.PY_COMPILED——2
imp.C_EXTENSION——3

imp.get_suffixes()
返回一个列表,列表的元素是三元素元组。
Return a list of 3-element tuples, each describing a particular type of module. Each triple has the form (suffix, mode, type), where suffix is a string to be appended to the module name to form the filename to search for, mode is the mode string to pass to the built-in open() function to open the file (this can be ‘r’ for text files or ‘rb’ for binary files), and type is the file type, which has one of the values PY_SOURCE, PY_COMPILED, or C_EXTENSION, described below.
在这里插入图片描述
imp.find_module(name, [,path])
返回值是三元素元组file, pathname, description
file is an open file object positioned at the beginning, pathname is the pathname of the file found, and description is a 3-element tuple as contained in the list returned by get_suffixes() describing the kind of module found.

这里的file可以理解为通过open()打开的一个句柄
例如:a = open(‘/etc/test.sh’)的这个a

imp.load_module(name, file, pathname, description)

例子就是uts中env.py
这个是env.py这个文件里面定义一个类

class A(object)
    pass

下面是myB.py

import os
import sys
import imp
# dir就是env.py所在的目录
dir = os.path.dirname(os.path.abspath())
# 这里有个注意点,可以选择从多个目录中找[dir1, dir2],若果没有找到env会报ImportError
file, path_name, description = imp.find_module('env', [dir])
# 这一步就是导入env这个模块,让B成为A类的别名
B = imp.load_module('env', file,path_name, description).A

当我在其他文件中需要使用B类的时候

from myB import B

好处:我从myB导入了B,但B实际的定义是位于env.py中的A类,所以当我的env.py处于不同位置的时候,我可以导入不同的类B

官方例子,用完之后记得关闭文件
在这里插入图片描述




https://blog.csdn.net/foryouslgme/article/details/51734833

Python2与Python3的区别(四):imp与importlib

Deprecated since version 3.4: The imp package is pending deprecation in favor of importlib.
与之相关的:
1、系统函数_import()
2、exec

引用其它人博客

#Python的import不能接受变量,所以应该用 __import__函数来动态导入。
#如下的代码无法正常导入模块

modules = ['OpenSSL', 'Crypto', 'MySQLdb', 'sqlite3', 'zope.interface', 'pyasn1', 'twisted', 'django']  
for each in modules:  
    try:  
        import each  
    except Exception, e:  
        print e  
#这样导入会抛出 No module named each 的异常
将 import each 改为 __import__(each)就可以正常导入了。

importlib.find_loader(name, path=None)
Deprecated since version 3.4: Use importlib.util.find_spec() instead.




最后,总结一下:模块导入三种方式及中文注释

https://blog.csdn.net/sjin_1314/article/details/41969227
这里还有一个讲的贼相似的:http://www.cnblogs.com/allenblogs/archive/2011/11/15/2055149.html

Python 有三种模块导入函数

1、 使用import 导入模块
import modname : 模块是指一个可以交互使用,或者从另一Python 程序访问的代码段。只要导入了一个模块,就可以引用它的任何公共的函数、类或属性。模块可以通过这种方法来使用其它模块的功能。
用import语句导入模块,就在当前的名称空间(namespace)建立了一个到该模块的引用.这种引用必须使用全称,也就是说,当使用在被导入模块中定义的函数时,必须包含模块的名字。所以不能只使用 funcname,而应该使用 modname.funcname.
注意:可加入下面的使Python支持中文注释

#coding:gbk或#coding:utf-8或##-*- coding : gbk -*-

导入系统的模块

比如我在家目录下创建一个m.py的文件,我们导入了许多的系统模块,比如time,string等等。这边就类似c++里面的include

#!/bin/env python
 
import time
import string
import re
import socket
import threading
 
time.sleep(10)
 
print "java"

导入私有的模块

m.py文件

#!/bin/env python
#coding:gbk
#coding:utf-8
#上面的加载是支持中文注释
def plus(a ,b):
    #"""XXX""" 是函数的说明或使用
    """this is a test +"""
    a = a+b
    return a+b

使用import 加载m.py模块加载

#!/bin/env python
#coding:gbk
#coding:utf-8
"利用sys模块导入指定Python文件的路径"
import sys
sys.path.append("./")
"导入Python文件"
import m
 
print m.plus(12,3)
 
print help(m.plus)

2、 from modname import * 模式加载模块

from modname import funcname
from modname import fa, fb, fc
或者 from modname import *
与第1种方法的区别:funcname 被直接导入到本地名字空间去了,所以它可以直接使用,而不需要加上模块名的限定

  • 表示,该模块的所有公共对象(public objects)都被导入到 当前的名称空间,也就是任何只要不是以”_”开始的东西都会被导入。
    modname没有被定义,所以modname.funcname这种方式不起作用。并且,如果funcname如果已经被定义,它会被新版本(该导入模块中的版本)所替代。如果funcname被改成指向其他对象,modname不能不会觉察到。
    建议:

1)如果你要经常访问模块的属性和方法,且不想一遍又一遍地敲入模块名,使用 from module import
2)如果你想要有选择地导入某些属性和方法,而不想要其它的,使用 from module import
3)如果模块包含的属性和方法与你的某个模块同名,你必须使用import module来避免名字冲突
4)尽量少用 from module import * ,因为判定一个特殊的函数或属性是从哪来的有些困难,并且会造成调试和重构都更困难。

#!/bin/env python
#coding:gbk
#coding:utf-8
"利用sys模块导入指定Python文件的路径"
import sys
sys.path.append("./")
"另外一种导入Python文件"
from m import plus
print plus(12,3)
print help(plus)

3、使用内敛函数__import__

除了前面两种使用import关键字的方法以外,我们还可以使用内建函数 import() 来导入 module。两者的区别是,import 后面跟的必须是一个类型(type),而__import__() 的参数是一个字符串,这个字符串可能来自配置文件,也可能是某个表达式计算结果。例如
mymodule = import (’module_name’)
附注:
1)模块的内容都放在一个模块文件中,如 mymodule 的内容应该放在PYTHONPATH 目录下的一个mymodule.py中,C实现的除外
2)包可以将几个模块名称空间组织起来, 如A.b 就表示在包A中的一个子模块b

#!/bin/env python
#coding:gbk
#coding:utf-8
"利用sys模块导入指定Python文件的路径"
import sys
sys.path.append("./")
"另外一种导入Python文件"
my = __import__("m")
 
print my.plus(12,3)
 
print help(my.plus)

参考资料:
1、http://www.cnblogs.com/allenblogs/archive/2011/11/15/2055149.html
2、http://blog.csdn.net/chenguolinblog/article/details/11521611




这里还有一个比较有趣的玩法,介绍修改后实时导入的。。

https://blog.csdn.net/fantasysolo/article/details/82761176

无聊测试了下,发现:

1.python只import模块一次,在import某个模块后就存在内存中,此时修改该模块没有用

2.python3提供 importlib模块,reload方法重新载入模块.

3.但是该reload方法有延迟,似乎并不会等reload完毕,直接会继续执行

4.在reload后加延迟,起到预期效果,两个py互相调用和修改

test2.py:

import test1
import importlib
import time

def modify(ii):
    with open('test1.py','r') as f_r:
        lines = f_r.readlines()
    with open('test1.py','w') as f_w:
        i = 1
        for line in lines:
            if i == 19:
                num = int(line)+ii
                line = '\t\t'+str(num)+'\n'
            i = i+1
            f_w.write(line)
            
def f2():
    i2= \
        1
    print('i2 =',i2)
    modify(i2)
    importlib.reload(test1)
    time.sleep(0.2)
    if i2<10000:
        test1.f1()
    
if __name__=='__main__':
    f2()
    print("test2 end")

test1.py:

import test2
import importlib
import time

def modify(ii):
    with open('test2.py','r') as f_r:
        lines = f_r.readlines()
    with open('test2.py','w') as f_w:
        i = 1
        for line in lines:
            if i == 19:
                num = int(line)+ii
                line = '\t\t'+str(num)+'\n'
            i = i+1
            f_w.write(line)
            
def f1():
    i1=  \
        2
    print('i1 =',i1)
    modify(i1)
    importlib.reload(test2)
    time.sleep(0.2)
    if i1<10000:
        test2.f2()
    
if __name__=='__main__':
    f1()
    print("test1 end")
  • 106
    点赞
  • 374
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值