python-模块,异常,环境管理器

模块 Module

  什么是模块:

    1、模块是一个包含有一系列数据,函数,类等组成的程序组

    2、模块是一个文件,模块文件名通常以.py结尾

  作用:

    1、让一些相关数据,函数,类等有逻辑的组织在一起,使逻辑结构更加清晰

    2、模块中的数据,函数和类等可提供给其它模块或程序使用

  模块的分类:(同一个模块在不同的操作系统下可能数不同类型的模块)

    1、内置模块(builtins),一般是用C语言来写的,在解析器的内部可以直接使用。模块说明中有builtins关键字

    2、标准库模块,安装python时已经安装且可直接使用。

    3、第三方模块(通常为开源),需要自己安装

    4、用户自己完成的模块(可以作为其他人的第三方模块)

模块的导入语句

  import语句

    语法:import 模块名1 [as 模块新名1][,模块名2[ as 模块新名2]],....

    作用:将某模块整体导入到当前模块

    用法:模块.属性名

  dir(obj)函数返回模块所有属性的字符串列表

  help(obj)可以查看模块相关的文档字符串

  from  import语句

    语法: from 模块名 import 模块属性名1[as 属性新名1] [,模块属性名2[as 属性新名2],....]

    作用:将模块内的一个或多个属性导入到当前模块的作用域 

    说明:属性是模块内的所有全局变量

  from import * 语句 (一般不用)

    语法:from 模块名 import *

    作用:将某模块的所有属性都导入到当前模块

dir函数:

  dir([对象])返回一个字符串列表

  作用:1、如果没有参数调用,则返回当前作用域内的所有变量的列表

     2、如果给定一个对象作为参数,则返回这个对象的所有变量的列表

      1)对于一个模块,返回这个模块的全部属性

      2)对于一个类对象,返回这个类对象的所有变量,亲递归基类对象的所有变量

      3)对于其它对象,返回所有的变量,类变量,基类变量

数学模块 math

  变量:

    math.e  自然对数的底e

    math.pi  圆周率pi

  函数:

    math.ceil(x)          对x向上取整,比如x = 1.2 ,返回2

    math.floor(x)           对x向下取整,比如x = 1.8 ,返回1

    math.sqrt(x)          返回x的平方根

    math.factorial(x)        求x的阶乘

    math.log(x,[base])        返回以base为底的x的对数,如果不给base,则以自然对数e为底

    math.log10(x)          求以10为底x的对数

    math.pow(x,y)             返回x**y

    math.fabs(x)          返回浮点数x的绝对值,比如x = -1,返回1.0

  角度degrees和弧度radians互换

    math.degrees(x)          将弧度x转换为角度

    math.radians(x)         将角度x转换为弧度

  三角函数,下面的x都是以弧度为单位的

    math.sin(x)           返回x的正弦

    math.cos(x)             返回x的余弦

    math.tan(x)           返回x的正切

    math.asin(x)            返回x的反正弦(返回值为弧度)

    math.acos(x)           返回x的反余弦(返回值为弧度)

    math.atan(x)            返回x的反正切(返回值为弧度)

时间模块 time

此模块提供了时间相关的函数,且一直可用

  时间简介

    1、公元纪年是从公元0000年1月1日0时开始的

    2、计算机元年是从1970年1月1日0时开始的,此时间为0,之后每过一秒时间+1

    3、UTC时间(Coordinated Universal Time )是从Greenwich时间开始计算的,UTC时间不会因时区问题而产生错误

    4、DST阳光节约时间(Daylight Saving Time ),又称夏玲时,是一个经过日照时间修正后的时间(中国已经不用了)

  时间元组

    时间元组是一个9个整型元素组成的,这九个元素自前至后一次为

      1、四位的年(如:1992)

      2、月(1-12)

      3、日(1-31)

      4、时(0-23)

      5、分(0-59)

      6、秒(0-59)

      7、星期几(0-6,周一是0)

      8、元旦开始日(1-366)

      9、夏令时修时间(-1,0 or 1),对中国来说是0

    注:如果年份小于100,则会自动转换为加上1900后的值

  变量

    time.altzone    夏令时时间与UTC时间差(秒为单位),-32400秒

    time.daylight      夏令时校正时间,在中国为0

    time.timezone   本地区时间与UTC时间差(秒为单位),-28800秒

    time.tzname    时区名字的元组,第一个名字为未经夏令时修正的时区,第二个名字为经夏令时修正后的时间("CST","CST")

    注:CST为中国标准时间(China Standard Time UTC+8:00)

  函数:

    time.time()     返回从计算机元年至当前时间的秒数的浮点数(UTC时间为准)

    time.sleep(secs)    让程序按给定秒数的浮点数睡眠一段时间

    time.gmtime([secs]) 用给定秒数转换为用UTC表达的时间元组(默认返回当前时间元组)

    time.asctime([tuple]) 将时间元组转为日期时间字符串,默认为当前时间

    time.ctime([secs])  将时间戳转发为日期时间字符串,默认是当前时间

    time.mktime(tuple)    将本地日期时间元组转为新纪元秒数时间(UTC为准)

    time.localtime([secs])   将UTC秒数时间转为日期元组(以本地时间为准),默认为当前时间

    注:time.mktime(time.localtime()) - time.mktime(time.gmtime()) = 28800.0 说明当时时间比UTC时间早8个小时

    time.clock()     返回处理器时间,3.0版本开始已经废弃,改成time.process_time()

    time.strftime(formt[,tuple]) 返回可读字符串时间,格式有参数format决定。

    time.strptime()

    time.tzset()

  python中时间日期格式化符号

    %y      表示两位数的年份(00-99)

    %Y      表示四位数的年份(0000-9999)

    %m      月份(01-12)

    %d      月内中的一天(0-31)

    %H      24小时制小时数(0-23)

    %I       12小时制小时数(01-12)

    %M      分钟数(00-59)

    %S      秒数(00-59)

    %a      本地简化星期名称

    %A      本地完整星期名称

    %b      本地简化月份名称

    %B      本地完整月份名称

    %c      本地相应的的日期表示和时间表示

    %j       年内的一天(001-366)

    %p      本地A.M.或P.M.的等价符

    %U      一年中的星期数(00-53)星期日为星期的开始

    %w      星期(0-6),星期天为星期的开始

    %W      一年中的星期数(00-53)星期一为星期的开始

    %x      本地相应的日期表示

    %X      本地相应的时间表示

    %Z      当前时区的名称

    %%      %号本身

import time
print(time.localtime())#time.struct_time(tm_year=2018, tm_mon=7, tm_mday=19, tm_hour=0, tm_min=44, tm_sec=5, tm_wday=3, tm_yday=200, tm_isdst=0)
print(time.strftime("%Y-%m-%d  %H:%M:%S",time.localtime()))#2018-07-19  00:44:05
print(time.strftime("%y-%m-%d  %I:%M:%S",time.localtime()))#18-07-19  12:44:05
print(time.strftime("%a-%A-%b-%B-%c-%j-%p-%U-%w-%W-%x-%X-%Z-%%",time.localtime()))
#              Thu-Thursday-Jul-July-Thu Jul 19 00:44:05 2018-200-AM-28-4-29-07/19/18-00:44:05-?D1¨²¡À¨º¡Á?¨º¡À??-%
#               a    A       b   B             c               j   p  U w W     x        X         Z                 %

 

系统模块sys

此模块全部是运行时系统相关的信息

  作用:用于获取和设置与系统相关的信息

  变量

    sys.path      返回模块的搜索路径,path[0]是当前脚本程序的路径,初始化时使用pythonPath环境变量的值

    sys.modules    返回已加载模块的字典,字典的键为模块名,值为已加载的模块

    sys.version      返回python版本信息的字符串:'3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]'

    sys.version_info   返回python版本信息的命名元组:sys.version_info(major=3, minor=5, micro=4, releaselevel='final', serial=0)

    sys.platform    操作系统平台的名称信息(Win32)

    sys.argv        命令行参数argv[0]代表当前脚本程序的路径名,绑定用户启动程序时命令参数的列表

    sys.copyright    获得python版权相关信息

    sys.builtin_module_names  获得python内建模块的名称(字符串元组)

    标准输入输出时会用到

    sys.stdin      标准输入文件对象,多用于input()

    sys.stdout       标准输出文件对象,多用于print()

    sys.stderr        标准错误输出文件对象,用于输出错误信息

  函数:

    sys.exit([arg])    退出程序,正常退出时sys.exit(0)

    sys.getrecursionlimit()  得到递归嵌套层次限制(栈的深度)

    sys.setrecursionlimit(n)  得到和修改递归嵌套层次限制(栈的深度),你代表递归的次数,脚本停止,则这个限制还原

自定义模块

  自定义模块模块名必须符合“标识符”的命名规则(同变量名),模块有各自独立的作用域

  模块化编程的优点:

    1、有利于多人合作开发

    2、使代码更易于维护

    3、提高代码的复用率

    4、有利于解决变量名冲突问题

import 语句  搜索模块的路径顺序

  1、搜索程序运行时的路径(当前路径)

  2、sys.path提供的路径

  3、搜索内置模块

  说明:sys.path是一个存储模块搜索路径的列表

      1、可以把自定义的模块放在相应的路径下可以导入

      2、可以把自己模块的路径添加在sys.path列表中

模块的加载过程:

  1、在模块导入时,模块的所有语句会执行

  2、如果一个模块已经导入,则再次导入时不会重新执行模块内的语句

模块的重新加载:

  当已经被导入的模块内容被修改后,可以通过重新执行当前的程序重新加载,或者通过import imp;imp.reload(模块名)的方法重新加载

模块被导入和执行的过程

  1、先搜索相关的路径找到模块(.py)

  2、判断是否有此模块对应的.pyc文件,如果存在pyc文件且比.py文件新,则直接加载.pyc文件

  3、否则用.py文件生成.pyc文件后再进行加载

pyc 模块的编译文件:

  mymod1.py----编译(compile)------→mymod1.pyc------解释执行-----→python3

模块的属性

  属性的实质是变量(是模块内的全局变量)

模块内预置的属性

  __doc__属性

    作用:用来绑定模块的文档字符串

  模块内第一个没有赋值给任何变量的字符串为模块的文档字符串,(如果没有赋值的字符串出现在第二个语句,则文档字符串为空)

  __file__属性

    用来绑定模块对应的文档路径名

      1、对于内建模块,不绑定路径(没有__file__属性)

      2、对于其它模块,绑定路径名的字符串(相对路径:模块名.py)

  __name__属性

    此属性用来记录模块的自身名字

    作用:1、记录模块名

       2、用来判断是否为主模块(最先运行的模块)

    说明:1、当此模块为主模块时,__name__绑定"__main__"

       2、当此模块不是主模块时,此属性绑定模块名

模块的__all__列表(不属于模块的属性)

  模块中的__all__列表是一个用来存放可导出属性的字符串列表

  作用:当用from import * 语句导入时,只导入__all__列表内的属性,如果没有次列表则模块的所有属性都被导入

     不会影响import 模块名 和  from 模块名 import 属性名 两个语句的导入

模块的隐藏属性:

  模块中以"_"下划线(不仅仅是单下划线,多个下划线)开头的属性,在from import *语句导入时,将不被导入,通常称这些属性为隐藏属性。

  如果该属性被放入__all__列表中,将会被导入

随机模块 random

作用:用于模拟或生成随机输出的模块

  函数(import random as R)

  R.random()            返回一个[0,1)之间的随机实数

  R.randint(a,b)           返回一个 [a~b]之间的随机整数,包括a,b

  R.uniform(a,b)            返回[a,b)区间内的随机实数

  R.randrange([start,] stop[,step])   返回range(start,stop,step)中的随机数

  R.choice(seq)           从序列中返回随机数

  R.shuffle(seq, [,random])      随机指定序列的顺序(乱序序列),函数的第二个参数代表必须传入random模块中无参的函数目前只有random函数,可以使用         R.random代替,目前版本没有什么用

  R.sample(seq,n)            从序列中选择n个随机且不重复的元素

import random
L = [i for i in range(10)]
L5 = random.sample(L,5)
print(L)
print(L5)

json&pick模块

序列化:

  我们把对象(变量)从内存中变成可存储的或传输的过程称之为序列化,反过来,把变量内容从序列化后的对象重新读到内存中称为反序列化

  说明:json不能讲高级的对象序列化,比如:函数、类,如果要序列化函数 或者 类时需要使用pickle模块

  json中的函数:

    1、dumps():序列化

    2、loades():反序列化

import json
dic = {'name':"xdl",'age':25}
#将字典对象序列,使之变为可存储的对象
dic = json.dumps(dic)
f = open('json_test','w')
f.write(dic)
import json
f = open('json_test','r')
dic = f.read()
#将读出的数据反序列化,并读入到内存中
dic = json.loads(dic)
print(dic['name'])

  pickle中的函数

    1、dumps():序列化对象

    2、loads():反序列化对象

import pickle
def foo():
    print('ok')
#将字典对象序列,使之变为可存储的对象,是字节串,需要使用wb写入
foo = pickle.dumps(foo)
f = open('json_test','wb')
f.write(foo)
f.close()
import pickle
def foo():
    print('ok')
f = open('json_test','rb')
data = f.read()
#将读出的数据反序列化,并读入到内存中
#反序列化之前要创建一个与读取的函数名相同的函数
#否则会报错:AttributeError: Can't get attribute 'foo' on <module '__main__' from '
# /home/tarena/PycharmProjects/xdl/module/pickle_load.py'>
data = pickle.loads(data)
data()

logging模块(日志模块)

使用步骤:

  1、获取logger实例对象,如果参数为空则返回root logger,

    logger = logging.getLogger()

  2、指定logger的格式

    formatter = logging.Formatter('自定义格式')

  3、创建具体的日志handler,并将日志格式添加到处理器上,包括文件日志和终端日志

    1)文件日志:

      file_handler = logging.FileHandler('文件名称.log')

      file_handler.setFormatter(formatter)

    2)终端日志(在终端上显示日志信息)

      console_handler = logging.StreamHandler(sys.stdout)

      console_handler.setFormatter(formatter)

  4、设置日志级别

    logger.setLevel(日志级别)

  5、把日志的handler对象添加到日志对象logger中

    logger.addHandler(file_handler)

    logger.addHandler(console_handler)

  6、写日志   

    logger.debug('this is debug info')
    logger.info('this is information')
    logger.warn('this is warning message')
    logger.error('this is error message')
    logger.fatal('this is fatal message, it is same as logger.critical')
    logger.critical('this is critical message')

  7、移除日志处理器

    logger.removeHandler(file_handler)
    logger.removeHandler(console_handler)

import logging
import sys
#1、获取logger的实例,<Logger testlog (INFO)>,(INFO)是自己设置的日志级别,如果不给参数,<RootLogger root (INFO)>
logger = logging.getLogger('testlog')
#2、指定logger的格式
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
#3、创建具体的日志handler,文件日志,终端日志
#3.1、文件日志
file_handler = logging.FileHandler('testLog.log')
file_handler.setFormatter(formatter)
#3.2、终端日志
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
#4、设置日志的级别,高于等于这个默认级别才会被显示
logger.setLevel(logging.INFO)
#5、把日志的handler添加到logger实例中
logger.addHandler(file_handler)
logger.addHandler(console_handler)
#6、写日志
logger.error("Test Error log")
logger.info("Test info log")
logger.debug("Test debug log")
#7、清空日志
logger.removeHandler(file_handler)
logger.removeHandler(console_handler)

将日志封装成一个模块

class LogerHelper:
    
    def __init__(self,name='LogerHelper',setLevel=logging.DEBUG):
        self.logger = logging.getLogger(name)
        self.formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
        self.file_handler = logging.FileHandler(name+'.log')
        self.file_handler.setFormatter(self.formatter)
        self.console_handler = logging.StreamHandler(sys.stdout)
        self.console_handler.setFormatter(self.formatter)
        self.logger.setLevel(setLevel)
        self.logger.addHandler(self.file_handler)
        self.logger.addHandler(self.console_handler)
        
    def writeLog(self,info,level='debug'):
        if level == 'critial':
            self.logger.critical(info)
        elif level == 'error':
            self.logger.error(info)
        elif level == 'warn':
            self.logger.warn(info)
        elif level == 'info':
            self.logger.info(info)
        elif level == 'debug':
            self.logger.debug(info)

    def removeLog(self):
        self.logger.removeHandler(self.file_handler)
        self.logger.removeHandler(self.console_handler)
        
if __name__ == '__main__':
    logger = LogerHelper()
    logger.writeLog("hello world",'critial')
    logger.writeLog("hello world",'error')
    logger.writeLog("hello world",'warn')
    logger.writeLog("hello world",'info')
    logger.writeLog("hello world",'debug')
    logger.removeLog()

 

包(模块包)package

  包是将模块以文件夹的组织形式进行分组管理的方法(包比其它普通文件夹多一个__init__.py)

  作用:将一系列模块进行分类管理,有利于防止命名冲突,可以在需要时加载一个或一部分模块而不是全部模块

  __init__.py文件

    常规包内必须存在的文件

    __init__.py会在包加载时被自动调用

    作用:1、编写此包的内容

       2、在内部填写文档字符串

       3、在__init__.py内可以加载此包所依赖的一些其它模块

  包的导入

    用三条import语句可以导入包(同模块的导入规则)

    import 包名 [as 包别名]

    import 包名.模块名 [as 模块新名]

    import 包名.子包名.模块名

 

    from 包名 import 模块名  [as 模块新名]

    from 包名.子包名 import 模块名 [as 模块新名]

    from 包名.子包名.模块名 import 属性名 [as  属性新名]

 

    from 包名 import *

    from 包名.模块名 import *

  包的__init__.py内的__all__列表

    作用:用来记录此包中有哪些子包或模块在用from 包 import * 语句导入时是否被导入

    说明:__all__列表只对from import * 语句起作用,如果没有此列表则,都不会被导入

包的相对导入

  包的相对导入是指包内模块的相互导入

  语法:1、from 相对路径包或模块 import 属性或模块名

     2、from 相对路径或模块 import *

  相对路径:

     1、.代表当前目录 如:a/b/c.py   其中.c.py代表b路径

     2、..代表上一级目录

     3、...代表上二级目录

     4、....依次类推

     注:相对导入时不能超出包的外部,如果超出包的外部会报错ValueError: attempted relative import beyond top-level package

包的加载路径:

  和模块的加载路径相同

     1、当前文件夹

     2、sys.path给出的路径

异常 exception

什么是错误:

  错误是指由于逻辑或语法等导致一个程序无法正常执行的问题

  特点:有些错误是无法预知的

什么是异常:

  异常是程序出错时标识的一种状态,

  当异常发生时,程序不会再向下执行,而转去调用此函数的地方待处理错误并恢复为正常状态

  作用:

    1、通知上层调用者有错误产生需要处理

    2、用作信号通知

try 语句的两种语法

  try - except  语句

    语法:try:

          可能触发异常的语句

       except 错误类型1 [as 变量1]:

          异常处理语句1

       except 错误类型2 [as 变量2]:

          异常处理语句2

       except(错误类型3,错误类型4,...)[as 变量3]:#前面必须是异常类型元组

          异常处理语句3

       ...

       except:                  #捕获所有错误类型

          异常处理语句other

       else:

          未发生异常时执行的语句,(try语句嵌套的try语句出现异常并被处理,也要执行,如果没有被处理将交给外部try语句进行处理)

       finally:

          最终执行语句(不管有没有发生异常都会执行该语句,发生异常没有被处理同样会执行该语句)

    作用:尝试捕获异常,将程序转为正常状态并继续执行

    说明:1、as 子句用于绑定错误对象的变量,可以省略不写

       2、except子句可以有一个或多个,但至少要有一个

       3、else子句最多只能有一个,也可以省略不写

       4、finally子句最多只能有一个,也可以省略不写

  try-finally语句

    语法:try:

        可能触发异常的语句

       finally:

        最终语句

    说明:1、finally子句不可以省略

       2、一定不存在except子句

    作用:通常try-finally语句来做触发异常时必须要处理的事情,无论异常是否发生,finally子句都会执行

    注:try-finally语句不会改变程序的(正常/异常)状态

raise语句

  作用:触发一个错误,让程序进入异常状态

  语法:1、raise 异常类型 :raise ZeroDivisionError

     2、raise 异常对象:rasie ZeroDivisionError("被零除了...")

assert 语句(断言语句)

  语法:assert 真值表达式,错误数据(通常是字符串)

  作用:当真值表达式为False时,用错误数据创建一个AssertionError类型的错误,并进入异常状态

  类似于:if   真值表达式 == False:

        raise AssertionError(错误数据)

                  

              

小结:

  接收错误消息:try- except

  做必须要处理的事情的语句:try-finally

  发错误消息的语句:1、raise 语句

           2、assert 语句

为什么要用异常处理处理机制:

  在程序调用层数较深时,向主调用函数传递错误信息需要用return语句层层传递比较麻烦,所以用异常处理机制

异常(高级)
    回顾异常相关的语句:
        try-exept         用来捕获异常通知
        try-finally        用来做一定要做的事情
        raise               用来发生异常通知
        assert             用来根据条件来发出AssertionError类型的异常通知
    with语句:
        语法:with 表达式1 [as 变量1],表达式2 [as 变量2]:
                        语句块
        作用:使用于对资源进行访问的场合,确定使用过程中不管是否发生异常,都会执行必须的‘清理’操作,并释放资源
            如:文件使用后自动关闭,线程中锁的自动获取和释放等
        说明:能够用于with语句进行管理的对象必须是环境管理器

'''此示例示意用try-except 和 try-finally 组合来对文件进行操作'''
def read_from_file(filename="info.txt"):
    try:
        f = open(filename)
        try:
            print("正在读取文件")
            n = int(f.read())
            print("n=",n)
        finally:
            f.close()
            print("文件已经关闭")
    except OSError:
        print("文件打开失败")
read_from_file()
View Code

环境管理器:
    1、类内有__enter__ 和 __exit__实例方法的类被称为环境管理器
    2、能用with语句管理的对象必须是环境管理器
    3、__enter__方法将在进入with语句时被调用,并返回由as变量管理的对象
    4、__exit__方法将在离开with语句时被调用,且可以用参数来判断在离开with语句时是否有异常发生并做出相应的处理

contextlib,给我们提供了一个装饰器,只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器

使用环境管理器(上下文管理器)的好处

  1.提高代码的复用率

  2.提高代码的优雅度

  3.提高代码的可读性

import contextlib

@contextlib.contextmanager
def open_func(file_name):
    #__enter__方法
    print('openfile',file_name,'in __enter__')
    file_handler = open(file_name,'r')

    #在被装饰的函数中,必须是一个生成器,而yield之前的代码相当于__enter__,之后的代码相当于__exit__
    try:
        yield file_handler
    except Exception as e:
        print(e)
    finally:
        print('closefile',file_name,'in __exit__')
        file_handler.close()

if __name__ == "__main__":
    with open_func('aa.txt') as f:
        print(f)
        1 / 0
def read_from_file(filename="info.txt"):
    try:
        with open(filename) as f:
            print("正在读取文件")
            n = int(f.read())
            print("n=",n)
            print("文件已经关闭")
    except OSError:
        print("文件打开失败")
read_from_file()
View Code
'''此示例示意环境管理器的定义及使用'''
class A:
    def __enter__(self):
        print("已经进入with语句")
        return self #返回的对象将由as 绑定
    def __exit__(self,exc_type,exc_val,exc_tb):
        '''此方法会在退出with语句时自动调用
            exc_type在没有异常时为None,在出现异常时为绑定异常类型
            exc_val在没有异常时为None,在出现异常时绑定错误对象
            exc_tb在没有异常时为None,在出现异常时绑定traceback(跟踪)'''
        if exc_type is None:
            print("正常离开with语句!")
        else:
            print("异常离开with语句!")
            print("异常类型是:",exc_type)
            print("错误对象是",exc_val)
            print("traceback是:",exc_tb)
with A() as a:
    print("我是with语句内的一条语句")
    int(input("输入一个数:"))
# 已经进入with语句
# 我是with语句内的一条语句
# 输入一个数:aa
# 异常离开with语句!
# 异常类型是: <class 'ValueError'>
# 错误对象是 invalid literal for int() with base 10: 'aa'
# traceback是: <traceback object at 0x7f443a0e2748>
# Traceback (most recent call last):
#   File "02_enter_exit.py", line 24, in <module>
#     int(input("输入一个数:"))
# ValueError: invalid literal for int() with base 10: 'aa'
View Code

转载于:https://www.cnblogs.com/xdl-smile/p/9322948.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值