python开发之模块

模块

1、什么是模块

用来从逻辑上组织python代码(变量、函数、类、逻辑:实现一个功能),本质就是以.py结尾的python文件(文件名是test.py,对应的模块就是test)。

2、导入方法

import   module_name

from module_name import *   #不建议这么用

from module_name import logger  #建议这样做

from module_name import logger as log   

3、import本质(路径搜索和搜索路径)

导入模块的本质就是把python文件解释一遍

import sys
print(sys.path)
['D:\\pythonstudy\\模块', 'D:\\pythonstudy\\模块', 'C:\\Users\\AppData\\Local\\Programs\\Python\\Python36-32\\python36.zip', 
'C:\\Users\\AppData\\Local\\Programs\\Python\\Python36-32\\DLLs',
'C:\\Users\\AppData\\Local\\Programs\\Python\\Python36-32\\lib',
'C:\\Users\\AppData\\Local\\Programs\\Python\\Python36-32',
'C:\\Users\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\site-packages' ]

4、导入优化

from module_name import 函数名/类名

time模块

时间的三种表示方式

在python中通常有三种方式表示时间:时间戳、元组(Struct_time)、格式化的时间字符串:

1、时间戳(timestamp):通常来说,时间戳表示的是从1997年1月1日00:00:00开始按秒计算的偏移量,我们运算type(time.time()),返回的是float类型。

2、格式化的时间字符串(Format string):”1999-12-02”

3、元组(struct_time):struct_time元组中一共有九个元素,(年、月、日、时分秒,一年中第几天、一年中第几周等)

import time
print(time.time()) #1533351403.4826412
print(time.strftime('%Y-%m-%d %X')) #2018-08-04 10:57:18
print(time.strftime('%Y/%m/%d %X')) #2018/08/04 11:03:28
print(time.strftime('%Y/%m/%d %a %X')) #2018/08/04 Sat 11:04:08 %a表示星期几
print(time.strftime('%Y/%m/%d %a %j %X')) #2018/08/04 Sat 216 11:04:35 %j表示过了多少天
print(time.strftime('%H:%M:%S')) #11:03:06

struct_time = time.localtime() #time.struct_time(tm_year=2018, tm_mon=8, tm_mday=4, tm_hour=11, tm_min=5, tm_sec=38, tm_wday=5, tm_yday=216, tm_isdst=0)
print(struct_time.tm_year)
print(struct_time.tm_mon)

# 时间戳和结构化时间
t = time.time()
print(time.localtime(t)) #time.struct_time(tm_year=2018, tm_mon=8, tm_mday=4, tm_hour=11, tm_min=10, tm_sec=56, tm_wday=5, tm_yday=216, tm_isdst=0)
print(time.gmtime(t)) #time.struct_time(tm_year=2018, tm_mon=8, tm_mday=4, tm_hour=3, tm_min=10, tm_sec=56, tm_wday=5, tm_yday=216, tm_isdst=0)

print(time.mktime(time.localtime())) #1533352450.0
# 格式化和结构化
print(time.strptime('2018-08-03','%Y-%m-%d')) #time.struct_time(tm_year=2018, tm_mon=8, tm_mday=3, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=215, tm_isdst=-1)
print(time.strftime('%Y-%M-%d',time.localtime(122222222))) #1973-37-15

random模块

import random

print(random.random()) # 大于0小于1
print(random.uniform(1,3)) # 大于1小于3的小数
print(random.randint(1,3)) # 大于等于1 且小于等于3的整数
print(random.randrange(1,10)) # 左包含右不包含
# 随机选择一个返回
print(random.choice([1,2,'大波',[11,22]]))
# 随机选择多个返回,返回的个数为函数的第二个参数
print(random.sample([1,2,'23',[4,5]],1)) # [2]
print(random.sample([1,2,'23',[4,5]],2)) # ['23', [4, 5]]
print(random.sample([1,2,'23',[4,5]],3)) # [1, '23', [4, 5]]
# 打乱列表的顺序
res = list(range(10))
print(res) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
random.shuffle(res)
print(res) #[5, 9, 6, 2, 7, 4, 0, 3, 8, 1]

练习:生成随机验证码

import random
####### 1
res = ''
for i in range(5):
    num = random.randint(0,9)
    str1 = chr(random.randint(65,90))
    rad_choice = random.choice([str(num),str1])
    res += rad_choice

print(res)
###### 2
def v_code():
    code = ''
    for i in range(5):
        num = random.randint(0,9)
        alf = chr(random.randint(65,90))
        add = random.choice([num,alf])
        code = "".join([code,str(num)])
    return code
print(v_code())

#######3
import random
def v_code():
    code = ''
    for i in range(5):
        ret = list(str(j) for j in range(0, 10))+list(chr(i) for i in range(65,91))
        add=random.choice(ret)
        code="".join([code,str(add)])
    return code
print(v_code())

os模块

os模块是与操作系统交互的一个接口

'''
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.curdir  返回当前目录: ('.')
os.pardir  获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息
os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  运行shell命令,直接显示
os.popen("bash command).read()  运行shell命令,获取执行结果
os.environ  获取系统环境变量

os.path
os.path.abspath(path) 返回path规范化的绝对路径 os.path.split(path) 将path分割成目录和文件名二元组返回 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。
                        即os.path.split(path)的第二个元素
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  如果path是绝对路径,返回True
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
'''
import os
print(os.getcwd()) # 获取当前文件目录
print(os.curdir)
print(os.pardir)
os.makedirs('a')
os.removedirs('a')
print(os.environ)
os.mkdir('a')
print(os.listdir('a'))
os.rmdir('a')
print(os.stat('os模块.py'))
print(os.name)
print(os.sep)
print(os.linesep)
print(os.popen('dir').read())
print(os.system('dir'))

print(os.path.abspath(__file__))
print(__file__)
print(os.path.split(os.getcwd()))
print(os.path.dirname(os.path.abspath(__file__)))
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
print(os.path.getsize('os模块.py'))

 sys模块

sys模块是与python解释器交互的一个接口

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        获取Python解释程序的版本信息
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称

序列化模块

什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化

序列化的目的

1、以某种存储形式使自定义对象持久化
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。

json

Json模块提供了四个功能:dumps、dump、loads、load

import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)  #序列化:将一个字典转换成一个字符串
print(type(str_dic),str_dic)  #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的

dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic2),dic2)  #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}


list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) #也可以处理嵌套的数据类型 
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
import json
f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()

f = open('json_file')
dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
f.close()
print(type(dic2),dic2)

字符编码 ensure_ascii=False,ensure_ascii:,当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示。

import json
f = open('file','w',encoding='utf8')
json.dump({'国籍':'中 国'},f,ensure_ascii=False)
f.close()

pickle

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换

pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load  (不仅可以序列化字典,列表...可以把python中任意的数据类型序列化

这里我们要说明一下,json是一种所有的语言都可以识别的数据结构。
如果我们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用。
但是如果我们用pickle进行序列化,其他语言就不能读懂这是什么了~
所以,如果你序列化的内容是列表或者字典,我们非常推荐你使用json模块
但如果出于某种原因你不得不序列化其他的数据类型,而未来你还会用python对这个数据进行反序列化的话,那么就可以使用pickle

import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)  #一串二进制内容

dic2 = pickle.loads(str_dic)
print(dic2)    #字典

import time
struct_time  = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb')
pickle.dump(struct_time,f)
f.close()

f = open('pickle_file','rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)

hashlib

Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

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

摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同.

我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

import hashlib

md5 = hashlib.md5()
md5.update(b'alex3714')
print(md5.hexdigest()) # aee949757a2e698417463d47acac93df

如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:

md5 = hashlib.md5()
md5.update('egon')
md5.update('3714')
print md5.hexdigest()

MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

import hashlib

sha1 = hashlib.sha1()
sha1.update(b'alex3714')
print(sha1.hexdigest()) # 8a003668a9c990f15148f9e4046e1410781533b6

SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长

由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

hashlib.md5("salt".encode("utf8"))
import hashlib

md5 = hashlib.md5('salt'.encode('utf8'))
md5.update(b'alex3714')
print(md5.hexdigest()) # 29520817dd17b99ea7af32890f7698fb
                       # aee949757a2e698417463d47acac93df 没加盐之前

经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

登录小练习

login.py

import hashlib

username = input('username:')
pwd = input('password:')

with open('userinfo.txt') as f:
    for line in f:
        user,password,role = line.split('|')
        md5 = hashlib.md5()
        md5.update(bytes(pwd,encoding='utf8'))
        md5_pwd = md5.hexdigest()
        if user==username and md5_pwd==password:
            print('登录成功')
        else:
            print('用户名或密码错误!')

userinfo.txt

alex|aee949757a2e698417463d47acac93df|IT

logging

默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。

函数式简单配置

import logging  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')

灵活配置日志级别,日志格式,输出位置:

import logging  
logging.basicConfig(level=logging.DEBUG,  
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  
                    datefmt='%a, %d %b %Y %H:%M:%S',  
                    filename='/tmp/test.log',  
                    filemode='w')  
  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:

filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为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用户输出的消息

logger对象配置

import logging

logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8') 

# 再创建一个handler,用于输出到控制台 
ch = logging.StreamHandler() 
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)

fh.setFormatter(formatter) 
ch.setFormatter(formatter) 
logger.addHandler(fh) #logger对象可以添加多个fh和ch对象 
logger.addHandler(ch) 

logger.debug('logger debug message') 
logger.info('logger info message') 
logger.warning('logger warning message') 
logger.error('logger error message') 
logger.critical('logger critical message')

  

logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过

fh.setLevel(logging.Debug)单对文件流设置某个级别。

*********************************************************************************

做开发开不开日志,以下是我在工作中写Django项目常用的logging配置。

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'],  # 只有在Django debug为True时才在屏幕打印日志
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            '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 * 50,  # 日志大小 50M
            '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 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'collect',
            'encoding': "utf-8"
        }
    },
    'loggers': {
       # 默认的logger应用如下配置
        '': {
            'handlers': ['default', 'console', 'error'],  # 上线之后可以把'console'移除
            'level': 'DEBUG',
            'propagate': True,
        },
        # 名为 'collect'的logger还单独处理
        'collect': {
            'handlers': ['console', 'collect'],
            'level': 'INFO',
        }
    },
}

paramiko

 该模块是基于SSH用于连接远程服务器并执行相关的操作

SSHClient  

Linux上利用ssh登录,ssh python@192.168.8.128 

# 在Linux上传文件 
scp -rp -P22  test.txt   python@192.168.8.128:/tmp/ 
python@python:~$ more ~/.ssh/known_hosts 
|1|yS6NyW6WkZpzBbCuSVizIVlN4wA=|YzH4wOLuOmlFKQqTWsKRfu3iIxo= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJ
NlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3
skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==

用于连接远程服务器并执行基本命令

基于用户名和密码连接

import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 连接服务器
ssh.connect(hostname='192.168.1.127',port=22,username='python',password='python')

# 执行命令
stdin,stdout,stderr = ssh.exec_command('df')
result = stdout.read()
print(result.decode()) ssh.close()
python@python:~$ python3 ssh_client.py 
Filesystem     1K-blocks    Used Available Use% Mounted on
udev              982536       0    982536   0% /dev
tmpfs             201804    6408    195396   4% /run
/dev/sda1       18447100 5000052  12486948  29% /
tmpfs            1009020     220   1008800   1% /dev/shm
tmpfs               5120       4      5116   1% /run/lock
tmpfs            1009020       0   1009020   0% /sys/fs/cgroup
tmpfs             201804      44    201760   1% /run/user/1000
/dev/sr0         1517760 1517760         0 100% /media/python/Ubuntu 16.04.2 LTS amd64

上传文件

import paramiko

transport = paramiko.Transport(('192.168.1.127',22))

transport.connect(username='python',password='python')
sftp = paramiko.SFTPClient.from_transport(transport)
# 上传
sftp.put('/tmp/test1.py','/tmp/')
# 下载
# sftp.get('/tmp','/home/python')
transport.close()

SSH 秘钥

python@python:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/python/.ssh/id_rsa): 
/home/python/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/python/.ssh/id_rsa.
Your public key has been saved in /home/python/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:DNYl/kScngqfogtoG2G1H5hFLb+hz9XNrxUxzACQ/2c python@python
The key's randomart image is:
+---[RSA 2048]----+
|     .. .o=o..   |
|    .. + =o   +  |
|   . .= o.o.   = |
|  . =..= oo.    o|
| o + ..oSoo +  . |
|... ..o.+. . + E.|
|.o.  oo..     +. |
|. o..  o      .. |
| .  ..       ..  |
+----[SHA256]-----+
python@python:~$ more ~/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwvJEWsOBVfXBwJ+B4WKy3ne60INysbt0ggpzn8ZSXvo4TiUq
vt2+SkKFT5HvhEwboCYSWPt0wgIF4clY9OW9LF5p7X/Nxsn/njhLNQp4LE6QOZ5h
YeItTyjbZJ+XrcCSTiB5u+9NTAfSedgveEOw3txdYl+Sls6MLmEpXxiEeaM4lI87
5hwEgeMiPMl/4e7F3jLkkTfQe2nkPkU2KxeUlUXQXyHfRMf/GODfkYHe6crrUYMY
em7WKDVAxG+kF5fXLX14sIpP2HCY6WCvmXdqftlhia0lNvY/+j79hBT11m1GVKa6-----END RSA PRIVATE KEY-----
python@python:~$ more ~/.ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDC8kRaw4FV9cHAn4HhYrLed7rQg3Kxu3SCCnOfxlJe+jhOJSq+3b5KQoVPke+ETBugJhJY+3TCAgXhyVj05b0sXmntf83Gyf+eOEs1CngsTpA5nmFh4i1PKNtkn5etwJJOIHm7701MB9J52C94Q7De3F1
iX5KWzowuYSlfGIR5oziUjzvmHASB4yI8yX/h7sXeMuSRN9B7aeQ+RTYrF5SVRdBfId9Ex/8Y4N+Rgd7pyutRgxh6btYoNUDEb6QXl9ctfXiwik/YcJjpYK+Zd2p+2WGJrSU29j/6Pv2EFPXWbUZUproYru55POjBmT283HSv5X/urc46cWAd3hahUHSj p

把公钥放在你要登录的用户的~/.ssh/authorized_keys下

然后再次 ssh python@192.168.8.128 就不需要输入密码了

 正则

正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则。官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

1、字符组

 正则 待匹配字符   匹配结果  说明
[0123456789]
 8  True  
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和"待匹配字符"相同都视为可以匹配
 
[0123456789]
 a  False  
由于字符组中没有"a"字符,所以不能匹配
 
[0-9]
 
7
 True  
也可以用-表示范围,[0-9]就和[0123456789]是一个意思
 
[a-z]
 
s
 True  
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示
 
[A-Z]
 
B
 
True
 
[A-Z]就表示所有的大写字母
 
[0-9a-fA-F]
 
e
 True  
可以匹配数字,大小写形式的a~f,用来验证十六进制字符

 2、字符:

元字符 匹配内容
. 除换行符之外的任意字符
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字
\n 匹配换行符
\t 匹配一个制表符
\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W
匹配非字母或数字或下划线
\D
匹配非数字
\S
匹配非空白符
a|b
匹配字符a或字符b
()
匹配括号内的表达式,也表示一个组
[...]
匹配字符组中的字符
[^...]
匹配除了字符组中字符的所有字符

3、量词:

量词 用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

.*      默认为贪婪匹配模式,会匹配尽量长的字符串

.*?    加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串

几个常用的非贪婪匹配Pattern

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现
import re

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

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

ret = re.match('a', 'abc').group()  # 同search,不过尽在字符串开始处进行匹配
print(ret)
#结果 : 'a'

ret = re.split('[ab]', 'abcd')  # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret)  # ['', '', 'cd']

ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#将数字替换成'H',参数1表示只替换1个
print(ret) #evaHegon4yuan4

ret = re.subn('\d', 'H', 'eva3egon4yuan4')#将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(ret)

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

import re
ret = re.finditer('\d', 'ds3sy4784a')   #finditer返回一个存放匹配结果的迭代器
print(ret)  # <callable_iterator object at 0x10195f940>
print(next(ret).group())  #查看第一个结果
print(next(ret).group())  #查看第二个结果
print([i.group() for i in ret])  #查看剩余的左右结果
原文地址:https://www.cnblogs.com/crazyforever/p/5065976.html