18--常用模块03:logging日志、re正则、collections模块、paramiko模块

1.日志模块

1.1 日志基本配置

日志级别默认是30

import logging

# 一:日志基本配置(实际开发不使用这个)
logging.basicConfig(
    # 1.日志输出位置:终端、文件
    filename='access.log',  # 默认打印到终端

    # 2.日志输出格式
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

    # 3.时间格式
    datefmt='%Y-%m-%d %H:%M:%S %p',

    # 4.日志级别 (设置日志级别数,只有大于或者等于该level的日志,才会打印出来)
    # 默认级别数
      critical 	=> 50
      error 	=> 40
      warning	=> 30
      info 		=> 20
      debug 	=> 10
    level=30,    # 默认级别为warning 30
)

# 二:输出日志
logging.debug('调试debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('错误error')
logging.critical('严重critical')



打印结果:
# 注意下面的root是默认的日志名字

WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical

1.2 日志配置字典

直接使用该配置字典

# 日志配置字典:LOGGING_DIC   (使用时,可以直接用该字典,小的改动即可!)

# 1.日志格式化输出中,可能用到的格式化串如下:
%(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			用户输出的消息

 
# 2.定义三种日志输出格式    强调:其中的%(name)s为getlogger时指定的名字-->日志产生者的名字

# 完整的标准输出格式 (用'[]' 或 '-' 格式隔开)
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'

# 测试输出格式
test_format = '%(asctime)s - %(message)s'


# 3.日志配置字典 (重点是日志产生者:loggers、日志接受者:handlers、日志输出格式:formatters)
LOGGING_DIC = {
 'version': 1,
 'disable_existing_loggers': False,
 
 # 三种日志输出格式,依次配置在这里
 'formatters': {
     'standard': {
         'format': standard_format
     },
     'simple': {
         'format': simple_format
     },
     'test': {
         'format': test_format
     },
 },
 
 'filters': {},  # 过滤日志的对象
 
 # handlers是日志的接收者,不同的handler会将日志处理输出到不同的位置
 'handlers': {
     # 打印到终端的日志
     'console': {
         'level': 'DEBUG',		# 输出哪种级别以上的日志
         'class': 'logging.StreamHandler',  # 打印到屏幕
         'formatter': 'simple'   # 哪种格式输出
     },
     # 打印到文件'a1.log'的日志
     'default': {
         'level': 'DEBUG',
         'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,日志轮转 (rotating:旋转)
         'maxBytes': 1024*1024*5,  # 日志大小 5M (字节 'B'为单位)
         'backupCount': 5,  # 旧的日志文件备份份数 (超过即删掉最旧的那份)
         'filename': 'a1.log',  # 实际位置应在项目文件/log/下os.path.join(os.path.dirname(os.path.dirname(__file__)),'log','a2.log')
         'encoding': 'utf-8',  # 存储到文件里的日志,应该指定字符编码
         'formatter': 'standard',
     },
     # 打印到文件'a2.log'的日志
     'other': {
         'level': 'DEBUG',
         'class': 'logging.FileHandler',  # 保存到文件
         'filename': 'a2.log', 
         'encoding': 'utf-8',  
         'formatter': 'test',
     },
 },
 
 # loggers是日志的产生者,产生的日志会传递给handler然后控制输出
 'loggers': {
     # logging.getLogger(__name__)拿到的logger配置,不同的文件__name__不同,
       这保证了打印日志时标识信息不同 (在日志输出时,日志名为实际调用的文件名),
       但是拿着该名字去loggers里找key名时却发现找不到,于是默认使用key=''的配置

     '': {
         'handlers': ['default','console'],  # 即log数据既写入a1.log文件又打印到屏幕
         'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
         'propagate': False,  # 默认为True,是否向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递 (了解)
     },
     
     # 若需要额外的采集,再自定义日志产生者
     '专门的采集': {
         'handlers': ['other',],
         'level': 'DEBUG',
         'propagate': False,
     },
 },
}

1.3 日志使用

# 1.日志模块导入 (logging包下的config 和 getlogger)
import settings
from logging import config,getLogger

# 2.加载配置 (将自定义的日志配置字典,添加到logging配置中)
config.dictConfig(settings.LOGGING_DIC)  

# 3.输出日志
logger1=getLogger('kkk')  # 获取日志产生者
logger1.info('这是一条info日志')  # 产生日志信息: 产生者.日志级别('日志信息') 

logger2=getLogger('终端提示')
logger2.info('logger2产生的info日志')

logger3=getLogger('用户交易')
logger3.info('logger3产生的info日志')

logger4=getLogger('用户常规')
logger4.info('logger4产生的info日志')


# 补充两个重要知识点
  1.日志名的命名
    日志名是区别日志业务归属的一种非常重要的标识

  2.日志轮转
    日志记录着程序员运行过程中的关键信息
    
    日志轮转也叫日志切割,通俗来讲就是对日志文件进行大小的控制。
    每次记录的日志文件都是同一个文件,随着时间的推移,日志的容量自然会越来越大,读写速度会越来越慢。
    那为了控制日志文件的大小,就需要对日志文件进行控制。
    同时是把目前的日志文件改名,紧接着创建一个新的日志文件,名称和原来的一样,
    重启服务进程,让其向新的日志文件中写日志。
    
#   简述:定期(按照日志文件大小)将记录日志文件重命名,然后再新建记录日志文件来存储日志文件。	


# 强调:
1.logging是一个包,需要使用其下的config、getLogger,可以如下导入
from logging import config, getLogger
 
# 也可以使用如下导入
import logging.config    # 这样连同logging都一起导入了  ('.'左侧的包或模块也会被导入)

2.正则表达式

2.1 什么是正则

# 一:什么是正则?
  正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。
  或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过 re 模块实现。
  正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

生活中处处都是正则:
    比如我们描述:4条腿
      你可能会想到的是四条腿的动物或者桌子,椅子等
    继续描述:4条腿,活的
        就只剩下四条腿的动物这一类了

2.2 常用匹配模式

2.2.1 元字符匹配

import re

print(re.findall('\w','aAbc123_*()-='))
print(re.findall('\W','aAbc123_*()-= '))
print(re.findall('\s','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\S','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\d','aA\rbc\t\n12\f3_*()-= '))
print(re.findall('\D','aA\rbc\t\n12\f3_*()-= '))

print(re.findall('\Aalex',' alexis alex sb'))   # 在同一行,以什么开头(了解,常用'^')
print(re.findall('sb\Z',' alexis alexsb sb'))   # 在同一行,以什么结尾(了解, 常用'$')
print(re.findall('sb\Z',"""alexsb     		# 针对多行,'\Z'不起作用
alexis
sb
"""))

print(re.findall('^alex','alexis alex sb'))     # 字符串以什么开头 (不管字符串单行还是多行都适用)
print(re.findall('sb$','alexis alex sb'))	# 字符串以什么结尾 
print(re.findall('sb$',"""alex
alexis
alex
sb
"""))

print(re.findall('^alex$','alexis alex sb'))    # '^'和'$'组合使用,则是固定匹配这个词 
print(re.findall('^alex$','al       ex'))		
print(re.findall('^alex$','alex'))		# ['alex'] 

2.2.2 重复(数量)匹配

# 重复匹配:'.'  '*'  '+'  '?'  '{n,m}'  '.*'  '.*?'   

# 1. '.':匹配除了\n之外任意一个字符,指定re.DOTALL之后才能匹配换行符
print(re.findall('a.b','a1b a2b a b abbbb a\nb a\tb a*b')) 
  # ['a1b', 'a2b', 'a b', 'abb', 'a\tb', 'a*b']

print(re.findall('a.b','a1b a2b a b abbbb a\nb a\tb a*b',re.DOTALL))
  # ['a1b', 'a2b', 'a b', 'abb', 'a\nb', 'a\tb', 'a*b']


# 2. '*':左侧字符重复0次或无穷次,性格贪婪
print(re.findall('ab*','a ab abb abbbbbbbb bbbbbbbb'))
  # ['a', 'ab', 'abb', 'abbbbbbbb']


# 3. '+':左侧字符重复1次或无穷次,性格贪婪
print(re.findall('ab+','a ab abb abbbbbbbb bbbbbbbb'))
  # ['ab', 'abb', 'abbbbbbbb']


# 4. '?':左侧字符重复0次或1次,性格非贪婪 (满足一次就马上获取,不会在匹配到后续内容)
print(re.findall('ab?','a ab abb abbbbbbbb bbbbbbbb'))
  # ['a', 'ab', 'ab', 'ab']


# 5. '{n,m}':左侧字符重复n次到m次
    {0,}  => *
    {1,}  => +
    {0,1} => ?
    {n} 单独一个n代表只出现n次,多一次不行少一次也不行
    
print(re.findall('ab{2,5}','a ab abb abbb abbbb abbbbbbbb bbbbbbbb'))
  # ['abb','abbb','abbbb','abbbbb]
    
# 练习:匹配所有包含小数在内的数字    
print(re.findall( '\d+\.?\d*',"asdfasdf123as1111111.123dfa12adsf1asdf3"))
  # ['123', '1111111.123', '12', '1', '3']   
    
# 这个模式若是被匹配的字符串是"11.fa" 会被取到,但不是正则的问题,是字符串本身的问题,可以取到后进一步优化


### 贪婪与非贪婪
# 6. '.*' 组合使用代表:任意字符(数量不限)    默认为贪婪匹配
print(re.findall('a.*b','a1b22222222b'))   # ['a1b22222222b']


# 7. '.*?'组合使用代表:任意字符(数量不限)    为非贪婪匹配(推荐使用)
print(re.findall('a.*?b','a1b22222222b'))   # ['a1b']

2.2.3 或、非、转义匹配

# 1.或匹配:[]  匹配一个 中括号中的指定字符  
  括号内都是普通字符了,且如果-没有被转义的话,应该放到[]的开头或结尾
    
print(re.findall('a\db','a111b a3b a4b a9b aXb a b a\nb',re.DOTALL))
print(re.findall('a[0-9]b','a111b a3b a4b a9b aXb a b a\nb',re.DOTALL))
  # ['a3b', 'a4b', 'a9b']

# a和b之间出现小于5的数字,[0-5]或者[012345]
print(re.findall('a[501234]b','a111b a3b a4b a9b aXb a b a\nb',re.DOTALL))
print(re.findall('a[0-5]b','a111b a3b a1b a0b a4b a9b aXb a b a\nb',re.DOTALL)) 
  # ['a3b', 'a4b']

# a和b之间是数字或字母
print(re.findall('a[0-9a-zA-Z]b','a111b axb a3b a1b a0b a4b a9b aXb a b a\nb',re.DOTALL))
  # ['axb', 'a3b', 'a1b', 'a0b', 'a4b', 'a9b', 'aXb'] 

    
# 2.或匹配: a|b  匹配a或b
print(re.findall('company|companies', 'Too many companies and the next one is my company'))
  # ['companies', 'company']
    
    
# 3.非匹配:[]内的^代表取反  
print(re.findall('a[^0-9a-zA-Z]b','a1111111b axb a3b a1b a0b a4b a9b aXb a b a\nb',re.DOTALL))
  # ['a b', 'a\nb']
print(re.findall('a-b','a-b aXb a b a\nb',re.DOTALL))   
  # ['a-b']
print(re.findall('a[-0-9\n]b','a-b a0b a1b a8b aXb a b a\nb',re.DOTALL))   
  # ['a-b', 'a0b', 'a1b', 'a8b', 'a\nb']


# 4.转义: '\'  匹配原始符号: '\'
  在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等
  如果要在正则中匹配正常的"\n"而不是"换行符",就需要对"\"进行转义,变成'\\'

  在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的
  而在字符串中\也有特殊的含义,本身还需要转义
  所以如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了
  这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了

print(re.findall('a\\c','a\c'))  # 报错

# 'r' 代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义
print(re.findall(r'a\\c','a\c'))  
print(re.findall('a\\\\c','a\c')) # 同上面的意思一样,和上面的结果一样都是['a\\c']

2.2.4 分组匹配

# ():分组

print(re.findall('ab+', 'ababab123'))  
  # ['ab', 'ab', 'ab']
    
# findall方法的结果不是匹配的全部内容,而是组内的内容
print(re.findall('(ab)+123', 'ababab123'))  
  # ['ab']  匹配到末尾的ab123中的ab

# 加上 '?:' 可以让结果为匹配的全部内容,包含分组括号外的正则内容
print(re.findall('(?:ab)+123', 'ababab123')) 
  # ['ababab123']
    
print(re.findall('href="(.*?)"', '<a href="http://www.baidu.com">点击</a>'))
  # ['http://www.baidu.com']
print(re.findall('href="(?:.*?)"', '<a href="http://www.baidu.com">点击</a>'))
  # ['href="http://www.baidu.com"']

print(re.findall('compan(?:y|ies)','Too many companies and the next one is my company'))
  # ['companies', 'company']

2.3 常用方法

# 0.匹配结果
group()   : 获取到匹配的值
            # 若正则中是分组匹配,则group(0)是原始字符串,group(1)、group(n) 表示分组匹配后的第1、2、…个子串
    
groups()  : 分组匹配后,以元祖的形式,返回一个包含所有组的字符串(也就是子存储的值),从1到所含的小组号


# 1.finall()  匹配所有
ret = re.findall('a', 'eva jason yuan')  # 返回所有满足匹配条件的结果,放在列表里
print(ret)  # 结果: ['a', 'a']


# 2.search()  搜索
ret = re.search('a', 'eva jason yuan').group()
print(ret)  # 结果:  'a'
  # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象
    该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None

    
# 3.match()  匹配以什么开头
ret = re.match('a', 'abc').group()  # 同search,不过在字符串开始处进行匹配 search+^代替match
print(ret)  # 结果: 'a'


# 4.split()  分割
ret = re.split('[ab]', 'abcd')  # 按'a'和'b'分割
print(ret)  # 结果:['', '', 'cd']

ret=re.split("([ab])",'abcd')
print(ret)  # 结果 : ['', 'a', '', 'b', 'cd']

# 在匹配部分加上()之后所切出的结果是不同的:
  没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项
    

# 5.sub()  替换   默认替换所有
ret = re.sub('\d', 'H', 'eva3jason4yuan4', 1) # 将数字替换成'H',参数1表示只替换1次
print(ret)  # 结果: 'evaHjason4yuan4'

ret = re.subn('\d', 'H', 'eva3jason4yuan4') # 返回元组 (替换的结果,替换了多少次)
print(ret)  ('evaHjasonHyuanH', 3)


# 6.compile()  编译成正则表达式对象,再进行模式匹配
obj = re.compile('\d{3}')   # 规则要匹配的是3个数字
ret = obj.search('abc123eeee') # 正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())  # 结果 : 123

print(obj.findall('abc123eeee'))  # ['123']


# 7.finditer()  返回一个存放匹配结果的迭代器
ret = re.finditer('\d', 'ds3sy4784a')  
print(ret)  # <callable_iterator object at 0x10195f940>
print(next(ret).group())  # 查看第一个结果
print(next(ret).group())  # 查看第二个结果
print([i.group() for i in ret])  # 查看剩余的左右结果


# 8.特定的字符:  修正符    作为上面函数的 第三位flags 参数 
re.I   :不区分大小写匹配
re.M   :多行匹配     # 影响了  ^ 和 $ 的功能
re.S   :匹配任意字符  # 使.可以匹配换行符  

# eg: 
ret = re.findall('a', 'eva jAson yuan', re.I)   # 多个修正符用 '|'连接   eg: re.S|re.I|re.M 
print(ret)  # 结果: ['a', 'A', 'a']

3.collections模块

在内置数据类型(dictlistsettuple)的基础上
collections模块 提供额外的数据类型:
  Counter、deque、defaultdict、namedtuple和OrderedDict等
    
    
1.namedtuple: 生成可以使用名字来访问元素内容的tuple

2.deque: 双端队列,可以快速的从另外一侧追加和推出对象

3.OrderedDict: 有序字典

4.defaultdict: 带有默认值的字典
    
5.Counter: 计数器,主要用来计数

3.1 namedtuple

# namedtuple: 生成可以使用名字来访问元素内容的tuple

tuple可以表示不变集合
  例如:一个点的二维坐标就可以表示成: p = (1, 2)
  但是 看到(1, 2),很难看出这个tuple是用来表示一个坐标的

# eg:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
p.x  # 1
p.y  # 2

3.2 deque

# deque: 双端队列,可以快速的从另外一侧追加和推出对象

使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了
因为list是线性存储,数据量大的时候,插入和删除效率很低

deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

# eg:
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x')  # 尾部插入
q.appendleft('y')  # 首部插入
q  # deque(['y', 'a', 'b', 'c', 'x'])

deque除了实现list的append()和pop()外,还支持appendleft()和popleft()

3.3 OrderedDict

# OrderedDict: 有序字典

使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序

# eg:
from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)])
d  # dict的Key是无序的  {'a': 1, 'c': 3, 'b': 2}

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
od  # OrderedDict的Key是有序的  
# OrderedDict([('a', 1), ('b', 2), ('c', 3)])

# 注意:
  OrderedDict的Key会按照插入的顺序排列,不是Key本身排序

3.4 defaultdict

# defaultdict: 带有默认值的字典

# 语法格式:
  collections.defaultdict([default_factory[, …]])

  第一个参数 default_factory 是工厂函数 (factory function),默认为 None
  可选 listtuplesetstrint 等
  其作用在于:
     当用 defaultdict 创建的默认字典的 key 不存在时,
     将返工厂函数 default_factory 的默认值。
     例如: list 对应 []、tuple 对应 ()、str对应 ''int对应0

        
# eg:
有如下值集合 [11,22,33,44,55,66,77,88,99,90...],
返回:{'k1': 大于66 , 'k2': 小于66}

# 原生字典解决:
values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]
my_dict = {}
for value in values:
    if value > 66:
        if 'k1' in my_dict:
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if 'k2' in my_dict:
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]
            
print(my_dict)  # {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99, 90]}


# defaultdict字典解决方法
from collections import defaultdict

values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]
my_dict = defaultdict(list)
for value in values:
    if value > 66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)
        
print(my_dict)  
# defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99, 90]})


# eg:
from collections import defaultdict

dd = defaultdict(lambda: 'N/A')
dd['key1'] = 'abc'

print(dd['key1'])  # key1存在 'abc'
print(dd['key2'])  # key不存在 返回 'N/A'

3.5 Counter

# Counter: 计数器,主要用来计数
  Counter类的目的是用来跟踪值出现的次数
  它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value
  计数值可以是任意的Interger(包括0和负数)
  Counter类和其他语言的bags或multisets很相似

# eg:
c = Counter('abcdeabcdabcaba')
print c
# Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

# 其他详细内容 http://www.cnblogs.com/Eva-J/articles/7291842.html

4.paramiko模块

# psutils模块: 获取系统运行的进程和系统利用率
# django channels:实现WebSocket协议 服务器主动推送消息  eg:群聊
  websocket:https://www.cnblogs.com/liuqingzheng/p/10151572.html

# paramiko 基于SSH用于 连接远程服务器 模块
import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='10.0.0.100', port=22, username='root', password='123')

# 执行命令
stdin, stdout, stderr = ssh.exec_command('yum install vim -y')
# 获取命令结果
result = stdout.read()
print(result.decode('utf-8'))
# 关闭连接
ssh.close()
posted @   Edmond辉仔  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
点击右上角即可分享
微信分享提示