zoukankan      html  css  js  c++  java
  • 常用模块与面向对象

    一、常用模块

    1 xml模块

    xml指可扩展标记语言(eXtensible Markup Language),标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言

    xm通过<>节点来区别数据结构,格式如下:

    <?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank updated="yes">2</rank>
            <year>2008</year>
            <gdppc>141100</gdppc>
            <neighbor name="Austria" direction="E"/>
            <neighbor name="Switzerland" direction="W"/>
        </country>
        <country name="Singapore">
            <rank updated="yes">5</rank>
            <year>2011</year>
            <gdppc>59900</gdppc>
            <neighbor name="Malaysia" direction="N"/>
        </country>
        <country name="Panama">
            <rank updated="yes">69</rank>
            <year>2011</year>
            <gdppc>13600</gdppc>
            <neighbor name="Costa Rica" direction="W"/>
            <neighbor name="Colombia" direction="E"/>
        </country>
    </data>

    常用操作:

    import xml.etree.ElementTree as ET
    tree = ET.parse('test.xml')     #打开想xml文档
    root = tree.getroot()           #获得根节点
    
    #每个element对象都具有以下属性:
    # 1. tag:string对象,表示数据代表的种类。
    # 2. attrib:dictionary对象,表示附有的属性。
    # 3. text:string对象,表示element的内容。
    # 4. tail:string对象,表示element闭合之后的尾
    
    #1、遍历xml文档
    for child in root:
        print('========>', child.tag, child.attrib, child.attrib['name'])
        for i in child:
            print(i.tag, i.attrib, i.text)
    
    #2、查找element元素的三种方式之iter
    years = root.iter('year')             #从根节点下开始扫描所有树形结构
    for i in years:
        print(i)
        print(i.text)
    #查找element元素的三种方式之find
    res = root.find('country')                 #从根下一层开始查找,返回一个与之匹配的元素
    print(res)
    #查找element元素的三种方式之findall
    res = root.findall('country')               #从根下一层开始查找,返回所有与之匹配的元素
    print(res)
    
    #3、修改
    for node in root.iter('year'):
        new_year = int(node.text)+1             #拿出year里面的内容,转成整型再进行加1操作
        node.text = str(new_year)               #转成字符串类型,才能写入文件
        node.set('updated','yes')             #添加属性
        node.set('version','1.0')             #添加属性
    tree.write('test.xml')
    
    #删除
    for country in root.findall('country'):
        rank = country.find('rank')
        if int(rank.text) > 50:
            country.remove(rank)
    tree.write('test.xml')
    
    #添加
    for country in root.findall('country'):
        y = ET.Element('Yim')
        y.attrib = {'age':'25'}
        y.text = 'hello'
        country.append(y)
    tree.write('test.xml')

    2 shutil模块

    高级的文件、文件夹、压缩包处理模块

    import shutil
    
    #1、将文件内容拷贝到另一个文件中,shutil.copyfileobj(fsrc, fdst[, length])
    shutil.copyfileobj(open('a.txt','r'),open('b.txt','w'))
    
    #2、拷贝文件,shutil.copyfile(src, dst)
    shutil.copyfile('b.txt','c.txt')        #目标文件无须存在
    
    #3、仅拷贝权限。内容、组、用户均不变,shutil.copymode(src, dst)
    shutil.copymode('c.txt','d.txt')        #目标文件须存在
    
    #4、仅拷贝状态的信息(包括:mode bits, atime, mtime, flags),shutil.copystat(src, dst)
    shutil.copystat('d.txt','e.txt')         #目标文件须存在
    
    #5、拷贝文件和权限,shutil.copy(src, dst)
    shutil.copy('a.txt','f.txt')            #目标文件无须存在
    
    #6、拷贝文件和状态信息,shutil.copy2(src, dst)
    shutil.copy2('f.txt','g.txt')
    
    #7、递归拷贝文件夹,shutil.copytree(src, dst, symlinks=False, ignore=None)
    shutil.copytree('dir1', 'dir2',ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))      # 目标目录不能是已存在,注意对dir2目录父级目录要有可写权限,ignore的意思是排除
    
    #8、递归删除目录,shutil.rmtree(path[, ignore_errors[, onerror]])
    shutil.rmtree('dir1')
    
    #9、递归移动文件,类似于mv命令,其实就是重命名
    shutil.move('g.txt','g.mv.txt')
    
    #10、打包压缩,shutil.make_archive(base_name, format,...)
    #创建压缩包并返回文件路径,例如:zip、tar
    # base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
    # 如 data_bak                       =>保存至当前路径
    # 如:/tmp/data_bak =>保存至/tmp/
    # format:  压缩包种类,“zip”, “tar”, “bztar”,“gztar”
    # root_dir:    要压缩的文件夹路径(默认当前目录)
    # owner:   用户,默认当前用户
    # group:   组,默认当前组
    # logger:  用于记录日志,通常是logging.Logger对象
    ################################################
    # 将 /data 下的文件打包放置当前程序目录
    ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
    # 将 /data下的文件打包放置 /tmp/目录
    ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
    #zip压缩
    import zipfile
    z = zipfile.ZipFile('test.zip','w')
    z.write('a.txt')
    z.write('b.txt')
    z.close()
    #zip解压
    import zipfile
    z = zipfile.ZipFile('test.zip','r')
    z.extractall(path='/tmp')
    z.close()
    #tar压缩
    import tarfile
    t = tarfile.open('test.tar','w')
    t.add('a.txt',arcname='a.bak')
    t.add('b.txt',arcname='b.bak')
    t.close()
    #tar解压
    import tarfile
    t = tarfile.open('test.tar','r')
    t.extractall('/tmp')
    t.close()

    3 configparser模块

    configparser是用来读取配置文件的包。

    配置文件的格式如下:中括号“[ ]”内包含的为section。section 下面为类似于key-value 的配置内容

    [db]
    db_host = 127.0.0.1
    db_port = 22
    db_user = root
    db_pass = rootroot
    
    [concurrent]
    thread = 10
    processor = 20

    初始化:

    #使用ConfigParser 首先需要初始化实例,并读取配置文件
    import configparser
    cf = configparser.ConfigParser()
    cf.read('a.ini')

    读取:

    #1、获取所有section
    print(cf.sections())
    
    #2、获取指定section的options
    print(cf.options('db'))
    # print(cf.options(cf.sections()[0]))             #等同
    
    #3、获取指定section的配置信息
    print(cf.items('db'))
    
    #4、按照类型读取指定section的option信息(get、getint、getboolean、getfloat)
    print(cf.get('db','db_host'))       #字符串类型
    print(cf.getint('db','db_port'))    #整型
    # print(cf.getboolean())            #布尔型
    #print(cf.getfloat())               #浮点型

    修改:

    #1、设置某个option的值
    cf.set('db','db_pass','root123456')
    cf.write(open('a.ini','w'))
    
    #2、添加一个section
    cf.add_section('test')
    cf.set('test','bar','python')
    cf.write(open('a.ini','w'))
    
    #3、删除一个section或option
    cf.remove_option('test','bar')
    cf.remove_section('test')
    cf.write(open('a.ini','w'))

    判断:

    #1、判断某个section是否存在
    print(cf.has_section('db'))
    
    #2、盘点某个标题下的某个option是否存在
    print(cf.has_option('db','db_host'))

    4 hashlib模块

    hashlib提供了常见的摘要算法,如:SHA1, SHA224, SHA256, SHA384, SHA512,MD5

    摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)

    三个特征:

    • 内容相同则hash运算结果相同,内容稍微改变则hash值改变
    • 不可逆推
    • 相同算法:无论校验多长的数据,得到的hash值长度固定

    应用:

    import hashlib
    
    #1、md5加密
    hash = hashlib.md5()
    hash.update('hello'.encode('utf-8'))
    hash.update('Yim'.encode('utf-8'))              #这两条加起来等同于'hello Yim'
    print(hash.hexdigest())
    
    #2、sha1加密
    hash = hashlib.sha1()
    hash.update('hello Yim'.encode('utf-8'))
    print(hash.hexdigest())
    
    #3、添加自定义key进行加密
    hash = hashlib.sha1('python'.encode('utf-8'))
    hash.update('hello Yim'.encode('utf-8'))
    print(hash.hexdigest())
    
    #4、hmac加密
    #hmac内部对我们创建的key和内容进行处理后再加密
    import hmac
    h = hmac.new('python'.encode('utf-8'))
    h.update('hello Yim'.encode('utf-8'))
    print(h.hexdigest())
    
    #5、获取大文件的md5
    hash = hashlib.md5()
    with open('a.txt','rb') as f:
        for line in f:
            hash.update(line)
    print(hash.hexdigest())
    
    #6、模拟撞库破解密码
    import hashlib
    
    passwds = [
        'hello',
        'hello word',
        'hello yim',
        'hello Yim',
    ]
    
    def make_passwd_dict(passwds):
        dict = {}
        for i in passwds:
            hash = hashlib.sha1()
            hash.update(i.encode('utf-8'))
            dict[i] = hash.hexdigest()
        return dict
    
    def break_code(cryptograph,passwd_dic):
        for k,v in passwd_dic.items():
            if v == cryptograph:
                print('密码是:%s'%k)
    
    cryptograph = '9c68ef3cca853cd2c1c286bde3534693c9c11ed1'
    break_code(cryptograph,make_passwd_dict(passwds))

    5 subprocess模块

    subprocess模块用来生成子进程,并且可以通过管道连接它们的输入、输出、错误,以及获得它们的返回值

    subprocess包主要功能是执行外部的命令和程序,与shell类似

    import subprocess
    
    #执行系统命令
    subprocess.Popen(r'dir C:Users',shell=True)
    
    #管道,标准输出
    res = subprocess.Popen(r'dir C:Users',shell=True,stdout=subprocess.PIPE)
    print(res.stdout.read().decode('gbk'))
    
    #管道,错误输出
    res = subprocess.Popen(r'ls C:Users',shell=True,stderr=subprocess.PIPE)
    print(res.stderr.read().decode('gbk'))
    
    #管道,标准输入
    res1 = subprocess.Popen(r'dir F:PythonCode	est',shell=True,stdout=subprocess.PIPE)
    res2 = subprocess.Popen(r'findstr xml',shell=True,stdin=res1.stdout,stdout=subprocess.PIPE)
    print(res2.stdout.read().decode('gbk'))

    6 logging模块

    Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。

    logging模块与log4j的机制是一样的,只是具体的实现细节不同。模块提供logger,handler,filter,formatter。

    6.1 简单应用

    import logging
    
    logging.debug('debug message')
    logging.info('info message')
    logging.warning('warn message')
    logging.error('error message')
    logging.critical('critical message')
    
    #输出:
    WARNING:root:warn message
    ERROR:root:error message
    CRITICAL:root:critical message

    默认情况下,logging模块将日志打印到屏幕上(stdout),日志级别为WARNING(即只有级别高于WARNING的日志信息才会输出),日志格式如下:

     

    问题:日志级别等级及设置是怎样的?怎样设置日志的输出方式?比如输出到日志文件中?

    6.2 日志级别

    级别

    代号

    使用场景

    CRITICAL

    50

    严重错误,表明软件已不能继续运行了

    ERROR

    40

    由于更严重的问题,软件已不能执行一些功能了

    WARNING

    30

    发生了一些意外,或者不久的将来会发生问题(如磁盘满了)。软件还是在正常工作

    INFO

    20

    证明事情按预期工作

    DEBUG

    10

    详细信息,典型地调试问题时会感兴趣

    NOTSET

    0

    不设置

    6.3 简单配置

    为logging模块指定全局配置,针对所有logger有效,控制打印到文件中

    可在logging.basicConfig()函数中通过具体参数来更改logging模块默认行为,可用参数有:

      filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。

      filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。

      format:指定handler使用的日志显示格式。

      datefmt:指定日期时间格式。

      level:设置rootlogger(后边会讲解具体概念)的日志级别

      stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

    format参数中可能用到的格式化串:

    %(name)s

    Logger的名字

    %(levelno)s

    数字形式的日志级别

    %(levelname)s

    文本形式的日志级别

    %(pathname)s

    调用日志输出函数的模块的完整路径名,可能没有

    %(filename)s

    调用日志输出函数的模块的文件名

    %(module)s

    调用日志输出函数的模块名

    %(funcName)s

    调用日志输出函数的函数名

    %(lineno)d

    调用日志输出函数的语句所在的代码行

    %(created)f

    当前时间,用UNIX标准的表示时间的浮 点数表示

    %(relativeCreated)d

    输出日志信息时的,自Logger创建以 来的毫秒数

    %(asctime)s

    字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒

    %(thread)d

    线程ID。可能没有

    %(threadName)s

    线程名。可能没有

    %(process)d

    进程ID。可能没有

    %(message)s

    用户输出的消息

    使用:

    import logging
    
    #通过下面的方式进行简单配置输出方式与日志级别
    logging.basicConfig(filename='access.log',
                        format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S %p',
                        level=logging.INFO)
    
    logging.debug('debug message')
    logging.info('info message')
    logging.warning('warn message')
    logging.error('error message')
    logging.critical('critical message')
    
    #access.log内容:
    2017-10-26 19:47:38 PM - root - INFO -1 logging模块:  info message
    2017-10-26 19:47:38 PM - root - WARNING -1 logging模块:  warn message
    2017-10-26 19:47:38 PM - root - ERROR -1 logging模块:  error message
    2017-10-26 19:47:38 PM - root - CRITICAL -1 logging模块:  critical message

    6.4 logging模块对象

    Logger:产生日志的对象

    Filter:过滤日志的对象

    Handler:接收日志然后控制打印到不同的地方(FileHandler用来打印到文件中,StreamHandler用来打印到终端)

    Formatter:可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用,以此来控制不同的Handler的日志格式

    import logging
    
    # 1、logger对象:负责产生日志,然后交给Filter过滤,然后交给不同的Handler输出
    logger = logging.getLogger(__file__)
    # 2、Filter对象:不常用,略
    # 3、Handler对象:接收logger传来的日志,然后控制输出
    h1 = logging.FileHandler('t1.log')
    h2 = logging.FileHandler('t2.log')
    h3=logging.StreamHandler()      #打印到终端
    # 4、Formatter对象:日志格式
    formmater1=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p',)
    formmater2=logging.Formatter('%(asctime)s :  %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p',)
    formmater3=logging.Formatter('%(name)s %(message)s',)
    # 5、为Handler对象绑定格式
    h1.setFormatter(formmater1)
    h2.setFormatter(formmater2)
    h3.setFormatter(formmater3)
    # 6、将Handler添加给logger并设置日志级别
    logger.addHandler(h1)
    logger.addHandler(h2)
    logger.addHandler(h3)
    logger.setLevel(10)
    # 7、测试
    logger.debug('debug message')
    logger.info('info message')
    logger.warning('warn message')
    logger.error('error message')
    logger.critical('critical message')
    
    #t1.log
    2017-10-26 20:12:09 PM - F:/Python/Code/1 logging模块.py - DEBUG -1 logging模块:  debug message
    2017-10-26 20:12:09 PM - F:/Python/Code/1 logging模块.py - INFO -1 logging模块:  info message
    2017-10-26 20:12:09 PM - F:/Python/Code/1 logging模块.py - WARNING -1 logging模块:  warn message
    2017-10-26 20:12:09 PM - F:/Python /Code/1 logging模块.py - ERROR -1 logging模块:  error message
    2017-10-26 20:12:09 PM - F:/Python/Code/1 logging模块.py - CRITICAL -1 logging模块:  critical message
    #t2.log
    2017-10-26 20:12:09 PM :  debug message
    2017-10-26 20:12:09 PM :  info message
    2017-10-26 20:12:09 PM :  warn message
    2017-10-26 20:12:09 PM :  error message
    2017-10-26 20:12:09 PM :  critical message
    #终端
    F:/Python/Code/1 logging模块.py debug message
    F:/Python/Code/1 logging模块.py info message
    F:/Python/Code/1 logging模块.py warn message
    F:/Python/Code/1 logging模块.py error message
    F:/Python/Code/1 logging模块.py critical message

    6.5 Logger与Handler的级别

    logger是第一级过滤,然后才能到handler,我们可以给logger和handler同时设置level,但是需要注意的是:

    Logger is also the first to filter the message based on a level — if you set the logger to INFO, and all handlers to DEBUG, you still won't receive DEBUG messages on handlers — they'll be rejected by the logger itself. If you set logger to DEBUG, but all handlers to INFO, you won't receive any DEBUG messages either — because while the logger says "ok, process this", the handlers reject it (DEBUG < INFO).

    import logging
    
    form=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S %p',)
    
    ch=logging.StreamHandler()
    
    ch.setFormatter(form)
    # ch.setLevel(10)
    ch.setLevel(20)             #debug被过滤掉了
    
    l1=logging.getLogger('root')
    # l1.setLevel(20)
    l1.setLevel(10)
    l1.addHandler(ch)
    
    l1.debug('l1 debug')

    6.6 Logger的继承

    import logging
    
    # 3、Formatter对象:日志格式
    formatter=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p',)
    # 2、Handler对象:接收logger传来的日志,然后控制输出
    ch=logging.StreamHandler()
    # 4、为Handler对象绑定格式
    ch.setFormatter(formatter)
    #1、logger对象:负责产生日志,然后交给Filter过滤,然后交给不同的Handler输出
    logger1=logging.getLogger('root')
    logger2=logging.getLogger('root.child1')
    logger3=logging.getLogger('root.child1.child2')
    # 5、将Handler添加给logger并设置日志级别
    logger1.addHandler(ch)
    logger2.addHandler(ch)
    logger3.addHandler(ch)
    logger1.setLevel(10)
    logger2.setLevel(10)
    logger3.setLevel(10)
    # 6、测试
    logger1.debug('log1 debug')
    logger2.debug('log2 debug')
    logger3.debug('log3 debug')
    
    #终端
    2017-10-26 20:41:00 PM - root - DEBUG -1 logging模块:  log1 debug
    2017-10-26 20:41:00 PM - root.child1 - DEBUG -1 logging模块:  log2 debug
    2017-10-26 20:41:00 PM - root.child1 - DEBUG -1 logging模块:  log2 debug
    2017-10-26 20:41:00 PM - root.child1.child2 - DEBUG -1 logging模块:  log3 debug
    2017-10-26 20:41:00 PM - root.child1.child2 - DEBUG -1 logging模块:  log3 debug
    2017-10-26 20:41:00 PM - root.child1.child2 - DEBUG -1 logging模块:  log3 debug

    6.7 应用

    """
    logging配置
    """
    
    import os
    import logging.config
    
    # 定义三种日志输出格式 开始
    
    standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' 
                      '[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
    
    simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
    
    id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
    
    # 定义日志输出格式 结束
    
    logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目录
    
    logfile_name = 'all2.log'  # log文件名
    
    # 如果不存在定义的日志目录就创建一个
    if not os.path.isdir(logfile_dir):
        os.mkdir(logfile_dir)
    
    # log文件的全路径
    logfile_path = os.path.join(logfile_dir, logfile_name)
    
    # log配置字典
    LOGGING_DIC = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': standard_format
            },
            'simple': {
                'format': simple_format
            },
        },
        'filters': {},
        'handlers': {
            #打印到终端的日志
            'console': {
                'level': 'DEBUG',
                'class': 'logging.StreamHandler',  # 打印到屏幕
                'formatter': 'simple'
            },
            #打印到文件的日志,收集info及以上的日志
            'default': {
                'level': 'DEBUG',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
                'formatter': 'standard',
                'filename': logfile_path,  # 日志文件
                'maxBytes': 1024*1024*5,  # 日志大小 5M
                'backupCount': 5,
                'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
            },
        },
        'loggers': {
            #logging.getLogger(__name__)拿到的logger配置
            '': {
                'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
                'level': 'DEBUG',
                'propagate': True,  # 向上(更高level的logger)传递
            },
        },
    }
    
    
    def load_my_logging_cfg():
        logging.config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging配置
        logger = logging.getLogger(__name__)  # 生成一个log实例
        logger.info('It works!')  # 记录该文件的运行状态
    
    if __name__ == '__main__':
        load_my_logging_cfg()

    6.8 logger流示意图

    6.9 Django的配置

    #logging_config.py
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                          '[%(levelname)s][%(message)s]'
            },
            'simple': {
                'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
            },
            'collect': {
                'format': '%(message)s'
            }
        },
        'filters': {
            'require_debug_true': {
                '()': 'django.utils.log.RequireDebugTrue',
            },
        },
    
        'handlers': {
            #打印到终端的日志
            'console': {
                'level': 'DEBUG',
                'filters': ['require_debug_true'],
                'class': 'logging.StreamHandler',
                'formatter': 'simple'
            },
            #打印到文件的日志,收集info及以上的日志
            'default': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
                'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"),  # 日志文件
                'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
                'backupCount': 3,
                'formatter': 'standard',
                'encoding': 'utf-8',
            },
    
            #打印到文件的日志:收集错误及以上的日志
            'error': {
                'level': 'ERROR',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
                'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"),  # 日志文件
                'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
                'backupCount': 5,
                'formatter': 'standard',
                'encoding': 'utf-8',
            },
    
            #打印到文件的日志
            'collect': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
                'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
                'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
                'backupCount': 5,
                'formatter': 'collect',
                'encoding': "utf-8"
            }
        },
        'loggers': {
            #logging.getLogger(__name__)拿到的logger配置
            '': {
                'handlers': ['default', 'console', 'error'],
                'level': 'DEBUG',
                'propagate': True,
            },
            #logging.getLogger('collect')拿到的logger配置
            'collect': {
                'handlers': ['console', 'collect'],
                'level': 'INFO',
            }
        },
    }
    
    # -----------
    # 用法:拿到俩个logger
    logger = logging.getLogger(__name__) #线上正常的日志
    collect_logger = logging.getLogger("collect") #领导说,需要为领导们单独定制领导们看的日志

    二、面向对象

    面向过程与面向对象编程的区别:

    • 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
    • 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为

    面向过程与面向对象的优缺点:

    面向过程:

    • 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源
    • 缺点:没有面向对象易维护、易复用、易扩展

    面向对象:

    • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
    • 缺点:性能比面向过程低

    1 类与对象

    类(class):用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例

    类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用

    对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法

    1.1 类定义

    语法:

    class ClassName:
        <statement-1>
        .
        .
        .
        <statement-N>

    实例:

    class Student:                  #会产生一个类的名称空间,用来存放类内部的变量及函数
        school =  '哈尔滨佛学院'     #类的数据属性
        def learn(self):            #类的函数属性
            print('is learning')
    
    print(Student.__dict__)         #查看类的名称空间
    print(Student.school)           #实际上,创建一个类之后,可以通过类名访问其属性
    
    #执行结果:
    {'__module__': '__main__', 'school': '哈尔滨佛学院', 'learn': <function Student.learn at 0x000000000259B8C8>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
    哈尔滨佛学院

    操作类的属性(属性引用):

    class Student:
        school =  '哈尔滨佛学院'
        def learn(self):
            print('is learning')
    
    Student.x = 123456              #增加,实际等同Student.__dict__['x'] = 123456,但Python3不支持这种操作
    Student.y = 654321
    Student.school = '哈佛'         #修改
    del Student.y                   #删除
    print(Student.__dict__)
    
    #执行结果:
    {'__module__': '__main__', 'school': '哈佛', 'learn': <function Student.learn at 0x000000000271B8C8>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None, 'x': 123456}

    1.2 类对象

    实例化:

    class Student:
        school =  '哈尔滨佛学院'
        def learn(self):
            print('is learning')
    obj1 = Student()            #类名加括号,会产生一个该类的实际存在的对象,该过程称为实例化,其结果obj1也可被称为实例
    print(obj1)                 #obj1是一个空对象
    
    #执行结果:
    <__main__.Student object at 0x00000000028D97F0>

    很多类都倾向于将对象创建为有初始状态的。因此类可能会定义一个名为 __init__() 的特殊方法,像下面这样:

    def __init__(self):             #类定义了 __init__() 方法的话,类的实例化操作会自动调用 __init__() 方法
        self.data = []

    __init__() 方法可以有参数,参数通过 __init__() 传递到类的实例化操作上。例如:

    class Student:
        school = '哈尔滨佛学院'
        def __init__(self,name,age,sex):            #在实例化时,产生对象之后执行
            self.name = name
            self.age = age
            self.sex = sex
        def learn(self):
            print('is learning')
    obj1 = Student('Yim',25,'male')                  #1、先产生一个空对象obj1 2、Student.__init__(obj1,'Yim',25,'male')
    print(obj1.__dict__)                             #查看对象的名称空间
    obj1.id = '1'                                    #同样,对象属性也能被引用,等同obj1.__dict__['id'] = '1',Python3是支持的
    print(obj1.name,obj1.age,obj1.sex,obj1.id)
    
    #执行结果:
    {'name': 'Yim', 'age': 25, 'sex': 'male'}
    Yim 25 male 1

    1.3 属性查找

    class Student:
        school = '哈尔滨佛学院'
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
        def learn(self):
            print('%s is learning'%self.name)
    obj1 = Student('Yim',25,'male')
    obj2 = Student('Jim',18,'male')
    print(obj1.school)                  # obj1和obj2都能访问类里面的数据属性
    print(obj2.school)
    print(id(obj1.school))              #属性id也一致,类的数据属性是共享给所有对象的
    print(id(obj2.school))
    print(obj1.learn)                   #类的函数属性是绑定给所有对象的(绑定方法),内存地址也不一样
    print(obj2.learn)
    obj1.learn()                        #绑定给谁,就由谁来调用,谁来调用就把谁本身当做第一个参数传入(自动传值)
    
    #执行结果:
    哈尔滨佛学院
    哈尔滨佛学院
    40210192
    40210192
    <bound method Student.learn of <__main__.Student object at 0x00000000026F9128>>
    <bound method Student.learn of <__main__.Student object at 0x00000000026F9160>>
    Yim is learning
    class Student:
        school = '哈尔滨佛学院'
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
        def learn(self):
            print('%s is learning'%self.name)
    obj1 = Student('Yim',25,'male')
    obj2 = Student('Jim',18,'male')
    obj1.school = '哈佛'                #修改obj1属性
    print(obj1.school)                  #查找顺序:对象-->类
    print(obj2.school)                  #obj2不受影响
    
    #执行结果:
    哈佛
    哈尔滨佛学院

    给类加一个计数器功能,每实例化一次就加1

    class Student:
        count = 0
        school = '哈尔滨佛学院'
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
            Student.count += 1
        def learn(self):
            print('%s is learning'%self.name)
    obj1 = Student('Yim',25,'male')
    obj2 = Student('Jim',18,'male')
    print(Student.count)
    print(obj1.count)
    
    #执行结果:
    2
    2

    2 继承

    继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待

    继承的功能之一就是用来解决代码重用的问题

    语法:

    class DerivedClassName(BaseClassName1):
        <statement-1>
        .
        .
        .
        <statement-N>

    实例:

    class ParentClass1:         #定义父类(基类)
        pass
    
    class ParentClass2:         #定义父类(基类)
        pass
    
    class SubClass1(ParentClass1):      #单继承,基类是ParentClass1,派生类(子类)是SubClass1
        pass
    
    class SubClass2(ParentClass1,ParentClass2):     #多继承,用逗号分隔
        pass
    
    #查看继承
    print(SubClass1.__bases__)              #查看所有继承的父类
    print(SubClass2.__bases__)
    
    #执行结果:
    (<class '__main__.ParentClass1'>,)
    (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

    经典类与新式类:

    1.只有在python2中才分新式类和经典类,python3中统一都是新式类

    2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类

    3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类

    4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

    #python2
    >>> class Foo:pass
    ...
    >>> Foo.__bases__
    ()
    
    #python3
    >>> class Foo:pass
    ...
    >>> Foo.__bases__
    (<class 'object'>,)         #如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现

    解决代码重用实例:

    class People:
        school = '哈佛'
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
        def eat(self):
            print('%s is eating' %self.name)
    
    class Student(People):
        def learn(self):
            print('%s is learning' %self.name)
    
    class Teacher(People):
        def teach(self):
            print('%s is teachning' % self.name)
    
    S_obj1 = Student('Yim',25,'male')
    T_obj1 = Teacher('Jim',18,'male')
    
    print(S_obj1.name,S_obj1.age,S_obj1.sex)
    S_obj1.learn()
    T_obj1.teach()
    
    #执行结果:
    Yim 25 male
    Yim is learning
    Jim is teachning

    重用父类的方法:

    class Animal:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def eat(self):
            print('%s eat' %self.name)
        def talk(self):
            print('%s say' %self.name)
    
    class People(Animal):
        def __init__(self,name,age,sex,education):
            Animal.__init__(self,name,age,sex)          #调用父类方法,也可以不依赖于继承
            self.education=education
        def talk(self):
            Animal.talk(self)                           #调用父类方法,也可以不依赖于继承
            print('这是人在说话')
    
    peo1=People('Yim',25,'male','幼儿园毕业')
    print(peo1.__dict__)
    peo1.talk()
    
    #执行结果:
    {'name': 'Yim', 'age': 25, 'sex': 'male', 'education': '幼儿园毕业'}
    Yim say
    这是人在说话

    3 方法重写

    如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

    class Parent:        # 定义父类
       def myMethod(self):
          print ('调用父类方法')
    
    class Child(Parent): # 定义子类
       def myMethod(self):
          print ('调用子类方法')
    
    c = Child()          # 子类实例
    c.myMethod()         # 子类调用重写方法
    
    #执行结果:
    调用子类方法

    4 组合

    组合指的是:在一个类中以另外一个类的对象作为数据属性,称为类的组合

    实例:

    class People:
        school = '哈佛'
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    class Student(People):
        def __init__(self,name,age,sex):
            People.__init__(self,name,age,sex)
            self.course = []                #学生可以学习多名课程
        def tell_info(self):
            print('<name:%s age:%s sex:%s>' %(self.name,self.age,self.sex))
    
    class Teacher(People):
        def __init__(self,name,age,sex):
            People.__init__(self,name,age,sex)
            self.course = []                #老师可以教多名课程
            self.students = []              #老师可以教多个学生
    
    class Course:                   #单独定义一个course类
        def __init__(self,course_name,course_period,course_price):
            self.course_name=course_name
            self.course_period=course_period
            self.course_price=course_price
        def tell_info(self):
            print('<课程名:%s 周期:%s 价格:%s>' %(self.course_name,self.course_period,self.course_price))
    
    S_obj1 = Student('Yim',25,'male')
    S_obj2 = Student('Lin',22,'male')
    T_obj1 = Teacher('Jim',18,'male')
    python = Course('Python','6mons',100)
    java = Course('Java','10mons',200)
    
    T_obj1.course.append(python)        #把课程加进去
    T_obj1.course.append(java)
    T_obj1.students.append(S_obj1)      #把学生加进去
    T_obj1.students.append(S_obj2)
    S_obj1.course.append(python)
    
    for i in T_obj1.course:         #查看老师教的课程
        i.tell_info()           #Course('Python','6mons',100).tell_info()     Course('Java','10mons',200).tell_info()
    
    for i in T_obj1.students:       #查看老师有多少名学生
        i.tell_info()
    
    for i in S_obj1.course:         #查看学生学习的课程
        i.tell_info()
    
    #执行结果:
    <课程名:Python 周期:6mons 价格:100>
    <课程名:Java 周期:10mons 价格:200>
    <name:Yim age:25 sex:male>
    <name:Lin age:22 sex:male>
    <课程名:Python 周期:6mons 价格:100>

    5 继承实现原理

    继承顺序:

     

    实例:

    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        # def test(self):
        #     print('from F')
        pass
    f1=F()
    f1.test()
    print(F.__mro__)    #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    
    #新式类继承顺序:F->D->B->E->C->A
    #经典类继承顺序:F->D->B->A->E->C
    #python3中统一都是新式类
    #pyhon2中才分新式类与经典类

    6 子类中调父类的方法

    方法一:父类名.父类方法()

    class Animal:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def eat(self):
            print('%s eat' %self.name)
        def talk(self):
            print('%s say' %self.name)
    
    class People(Animal):
        def __init__(self,name,age,sex,education):
            Animal.__init__(self,name,age,sex)          #调用父类方法,也可以不依赖于继承
            self.education=education
        def talk(self):
            Animal.talk(self)                           #调用父类方法,也可以不依赖于继承
            print('这是人在说话')
    
    peo1=People('Yim',25,'male','幼儿园毕业')
    print(peo1.__dict__)

    方法二:super(),这种方法依赖于继承,从MRO列表当前的位置往后找

    class Animal:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def eat(self):
            print('%s eat' %self.name)
        def talk(self):
            print('%s say' %self.name)
    
    class People(Animal):
        def __init__(self,name,age,sex,education):
            super().__init__(name,age,sex)                      #python2:super(People.self).__init__()
            self.education=education
        def talk(self):
            super().__init__(self)
            print('这是人在说话')
    
    peo1=People('Yim',25,'male','幼儿园毕业')
    print(peo1.__dict__)

    7 绑定方法与非绑定方法

    1、绑定方法:绑定给谁,就由谁来调用,谁来调用就把谁本身当做第一个参数传入(自动传值)

    绑定到类的方法:用classmethod装饰器装饰的方法

    • 为类量身定制
    • 类.bound_method(),自动将类当做第一个参数传入
    • 其实对象也可调用,但仍将类当作第一个参数传入

    绑定到对象的方法:没有被任何装饰器装饰的方法

    • 为对象量身定制
    • 对象.bound_method(),自动将对象作为第一个参数传入
    • 属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说

    实例:

    #配置文件settings.py
    HOST='127.0.0.1'
    PORT=3306 
    import settings
    
    class Mysql:
        def __init__(self,host,port):
            self.host = host
            self.port = port
        @classmethod
        def from_conf(cls):                 #绑定给类
            return  cls(settings.HOST,settings.PORT)
        def fun1(self):                     #绑定给对象
            pass
    
    conn = Mysql.from_conf()
    print(conn.host,conn.port)
    
    #执行结果:
    127.0.0.1 3306

    2、非绑定方法:在类内部用staticmethod装饰器装饰的函数即非绑定方法,就是普通函数。statimethod不与类或对象绑定(应用场景),谁都可以调用,没有自动传值效果

    import uuid
    
    class Mysql:
        def __init__(self,host,port):
            self.host = host
            self.port = port
            self.id = self.create_uuid()        #调用create_uuid函数
        @staticmethod
        def create_uuid():                      #普通函数,没有自动传值效果
            return str(uuid.uuid1())
    
    conn = Mysql('127.0.0.1',3306)
    print(conn.id)
    
    #执行结果:
    ea5637e8-8199-11e7-962a-34de1a770c8f

    8 接口与归一化设计

    接口提取了一群类共同的函数,可以把接口当做一个函数的集合。让子类去实现接口中的函数

    在Python中没有一个叫做interface的关键字,如果模仿接口的概念,可以借助第三方模块:http://pypi.python.org/pypi/zope.interface,也可以使用继承

    实例:

    class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
        def read(self): #定接口函数read
            pass
    
        def write(self): #定义接口函数write
            pass
     
    class Txt(Interface): #文本,具体实现read和write
        def read(self):
            print('文本数据的读取方法')
    
        def write(self):
            print('文本数据的写方法')
    
    class Sata(Interface): #磁盘,具体实现read和write
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的写方法')
    
    class Process(Interface):
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的写方法')

    9 抽象类

    抽象类是一个特殊的类,只能被继承不能被实例化,在Python中需要借助模块实现。以下是一个实例:

    import abc
    
    class Interface(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def read(self):
            pass
        @abc.abstractmethod
        def write(self):
            pass
    
    class Txt(Interface):
        def read(self):                 #必须要有read
            print('文本数据的读取方法')
    
        def write(self):                #必须要有write
            print('文本数据的写方法')

    10 多态

    是允许将父对象设置成为和一个或多个它的子对象相等的技术,比如Parent:=Child; 多态性使得能够利用同一类(基类)类型的指针来引用不同类的对象,以及根据所引用对象的不同,以不同的方式执行相同的操作

    Python不直接支持多态,但可以间接实现

    实例:

    import abc
    
    class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal): #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): #动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    def func(animal):
        animal.talk()
    
    p = People()
    d = Dog()
    func(p)
    func(d)
    
    #执行结果:
    say hello
    say wangwang

    11 封装

    “封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员

    实例:

    #这种隐藏不是真正意义上的隐藏,其实这仅仅是一种变形操作,只在类定义阶段发生的
    #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式
    #在子类定义的__x不会覆盖在父类定义的__x
    class Foo:
        __N = 123456        #变形为_Foo__N
        def __init__(self,name):
            self.name = name
        def __f1(self):     #变形为_Foo__f1
            print('f1')
        def bar(self):
            self.__f1()     #只有在类内部才可以通过__f1的形式访问到
    f = Foo('Yim')
    print(Foo.__dict__)
    #print(Foo.__N)          #外部无法直接访问
    f.bar()
    
    #执行结果:
    {'__module__': '__main__', '_Foo__N': 123456, '__init__': <function Foo.__init__ at 0x00000000025FB8C8>, '_Foo__f1': <function Foo.__f1 at 0x00000000025FB950>, 'bar': <function Foo.bar at 0x00000000025FB9D8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
    f1
    class People:
        def __init__(self,name,age,sex):
            self.__name = name
            self.__age = age
            self.__sex = sex
        def tell_info(self):
            print('<名字:%s 年龄:%s 性别:%s>' %(self.__name,self.__age,self.__sex))
        def set_info(self,x,y,z):               #可以定制一些控制逻辑来控制使用者对数据的操作
            if not isinstance(x,str):
                raise TypeError
            if not isinstance(y,int):
                raise TypeError
            if not isinstance(z,str):
                raise TypeError
            self.__name = x
            self.__age = y
            self.__sex = z
    
    p = People('Yim',25,'male')
    p.set_info('Zim',18,'male')
    p.tell_info()
    #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    #隔离了复杂度,同时也提升了安全性
    
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('用户认证')
        def __input(self):
            print('输入取款金额')
        def __print_bill(self):
            print('打印账单')
        def __take_money(self):
            print('取款')
    
        def withdraw(self):                 #隔离复杂度
            self.__card()
            self.__auth()
            self.__input()
            self.__print_bill()
            self.__take_money()
    
    a=ATM()
    a.withdraw()

    静态属性property:property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

    #计算体质指数(BMI)
    #体质指数(BMI)=体重(kg)÷身高^2(m)
    
    class People:
        def __init__(self,name,weight,height):
            self.name = name
            self.weight = weight
            self.height = height
        @property
        def bmi(self):
            return self.weight / (self.height ** 2)
    
    p = People('Yim',70,1.80)
    # print(p.bmi())
    print(p.bmi)

    三 面向对象进阶

    1 isinstance和issubclass

    class Foo():
        pass
    
    obj = Foo()
    
    print(isinstance(obj,Foo))                      #检查obj是否是类cls的对象,返回BOOL值
    class Foo():
        pass
    
    class Bar(Foo):
        pass
    
    print(issubclass(Bar,Foo))                      #检查sub类是否是super类的派生类,返回BOOL值

    2 反射

    通过字符串的形式操作对象相关的属性

    class Foo:
        x = 1
        def __init__(self,name):
            self.name = name
        def f1(self):
            print('from f1')
    
    f = Foo('Yim')
    # print(f.name)
    
    print(hasattr(f,'name'))              #hasattr(object, name),判断一个对象里面是否有name属性或者name方法,返回BOOL值
    print(getattr(f,'name'))              #getattr(object, name[,default]),获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选
    setattr(f,'name','Jim')               #setattr(object, name, values),给对象的属性赋值,若属性不存在,先创建再赋值
    delattr(f,'name')                     #delattr(object, name),删除指定对象的指定名称的属性,和setattr函数作用相反

    3 item系列

    class testsetandget:
        kk = {};
        def __getitem__(self, key):
            return self.kk[key];
        def __setitem__(self, key, value):
            self.kk[key] = value;
    
    a = testsetandget()
    a['first'] = 1
    print(a['first'])
    
    #执行结果:
    1

    4 打印对象信息__str__

    class People:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex =sex
        def __str__(self):              #在对象被打印时触发执行
            return '<name:%s age:%s sex:%s>' %(self.name,self.age,self.sex)
    
    p = People('Yim',25,'male')
    print(p)
    
    #执行结果:
    <name:Yim age:25 sex:male>

    5 析构方法__del__

    典型的应用场景:

    创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

    当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

    class Foo:
        def __init__(self,x):
            self.x = x
        def __del__(self):          #在对象资源被释放时触发
            print('del...........')
    
    f = Foo(11111)
    
    #执行结果:
    del...........
  • 相关阅读:
    maven(一)maven自带的插件
    Faster RCNN 的细节补充
    leetcode 287
    一句话介绍python线程、进程和协程
    C++中指针和引用的区别
    C++ operator new和new operator的区别
    Faster RCNN算法训练代码解析(3)
    Faster RCNN算法训练代码解析(2)
    Faster RCNN算法训练代码解析(1)
    Faster RCNN算法demo代码解析
  • 原文地址:https://www.cnblogs.com/yanmj/p/7373354.html
Copyright © 2011-2022 走看看