文章目录
前言
又一段时间的学习,下面做些总结
一、装饰器与带参数的装饰器
装饰器的作用,在不改变函数内容的前提下丰富函数的功能。
1.闭包
闭包是装饰器的组成部分,其基本的语法要求为:
1、发生函数嵌套
2、外层函数返回内层函数名
3、内层函数使用外层函数参数
闭包还可以提高代码的可重用性,不需要再手动定义额外的功能函数。
def func_out(data):
def func_in():
print(data)
return func_in
fun = func_out(10)
fun()
2.利用@的闭包实现
def fun_out(func):
def func_in(num):
print(num)
func()
return func_in
@fun_out
def run():
print("跑步")
run(12)
3.装饰器与带参数的装饰器
装饰器的功能特点:
1、不修改已有函数的源代码
2、不修改已有函数的调用方式
3、给已有函数增加额外的功能
上述@形式名为语法糖,为装饰器的特点:
def load(func):
def success():
print("请先登录")
func()
return success
@load
def comment():
print("发表评论!")
comment()
带有参数的装饰器利用不定长参数传递参数
def func(fun):
def warp(*args, **kwargs):
print("这是装饰")
sum = fun(*args, **kwargs)
return sum
print("这也是装饰!")
return warp
@func
def fun():
print("跑起来")
@func
def add(a, b):
return a + b
fun()
print(add(1, 2))
实现不同函数异常写入不文件中,即在日志装饰器上层再加一个装饰器,与wsgi中实现动态资源路由表相同。
装饰器文件:
import traceback
def decorate(path):
def outer(func):
def inner(*args, **kwargs):
try:
result = func(*args, **kwargs)
except:
with open(path, 'a', encoding="utf-8") as f:
print(traceback.format_exc())
f.write(traceback.format_exc())
else:
return result
return inner
return outer
引用装饰器函数
from log import decorate
@decorate("local.log")
def fun():
a += 1
print(a)
@decorate("zero.log")
def chu(a):
result = a / 0
return result
fun()
chu(10)
二、正则表达式
1.自定义上下文管理器
with open的语法实现具体操作:
扩展函数的注解:https://blog.youkuaiyun.com/weixin_43790276/article/details/90216525
https://www.cnblogs.com/keye/p/8572353.html
class File(object):
def __init__(self, file_name, file_type):
self.file_name = file_name
self.file_type = file_type
def __enter__(self):
print("进入上文的方法")
self.file = open(self.file_name, self.file_type)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print("进入下文的方法")
self.file.close()
if __name__ == '__main__':
with File("test.txt", 'r') as file:
data = file.read()
print(data)
2.正则表达式
正则表达式就是记录文本规则的代码
特点:
1、正则表达式的语法很令人头疼,可读性差
1、正则表达式通用行很强,能够适用于很多编程语言
在python中使用正则表达式需要导入re模块
# 导入re模块
import re
# 使用match方法进行匹配操作
result = re.match(正则表达式, 要匹配的字符串)
# 如果上一步匹配到数据的话,可以使用group方法来提取数据
result.group()
3.正则表达式:匹配单字符
导入模块
import re
. 匹配任意1个字符(除了\n)
result = re.match('.', 'dog')
print(result.group())
# d
[ ] 匹配[ ]中列举的字符
result = re.match("...[do]p", "dppdppo")
print(result.group())
# dppdp
# 只从第一个字符匹配
result = re.match("[dp]", "depppo")
print(result.group())
# d
ret = re.match("[0123456789]Hello Python", "7Hello Python")
print(ret.group())
# 7Hello Python
# 匹配0到9第二种写法
ret = re.match("[0-9]Hello Python", "7Hello Python")
print(ret.group())
# 7Hello Python
# 匹配0-3,5-9
ret = re.match("[0-35-9]Hello Python", "7Hello Python")
print(ret.group())
# 7Hello Python
\d 匹配数字,即0-9
result = re.match('今年\d\d\d\d', '今年2021')
print(result.group())
# 今年2021
ret = re.match("嫦娥\d号", "嫦娥3号发射成功")
print(ret.group())
# 嫦娥3号
\D 匹配非数字,即不是数字
result = re.match('今年\d\d\d1\D\D', '今年2021年!')
print(result.group())
# 今年2021年!
\s 匹配空白,即 空格,tab键
result = re.match('今年[0-9]\d\d[1]年\D\s啊', '今年2021年! 啊')
print(result.group())
# 今年2021年! 啊
\S 匹配非空白
result = re.match('今年[0-9]\d\d[1]年\D\s啊\S\S\D', '今年2021年! 啊。。-')
print(result.group())
# 今年2021年! 啊。。-
\w 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字
result = re.match('今年[0-9]\d\d[1]年\D\s啊\S\D\D\w\w', '今年2021年! 啊。。-_A')
print(result.group())
# 今年2021年! 啊。。-_A
\W 匹配特殊字符,即非字母、非数字、非汉字
result = re.match('今年[0-9]\d\d[1]年\D\s啊\S\D\D\w\w\D\S\W', '今年2021年! 啊。。-_A%$#')
print(result.group())
# 今年2021年! 啊。。-_A%$#
4.正则表达式:匹配多字符
* 匹配前一个字符出现0次或者无限次,即可有可无
需求:匹配出一个字符串第一个字母为大小字符,后面都是小写字母并且这些小写字母可有可无
ret = re.match("[A-Z][a-z]*", "M")
print(ret.group())
# M
ret = re.match("[A-Z][a-z]*", "MnnfM")
print(ret.group())
# Mnnf
ret = re.match("[A-Z][a-z]*", "Aabcdef")
print(ret.group())
# Aabcdef
+ 匹配前一个字符出现1次或者无限次,即至少有1次
需求:匹配一个字符串,第一个字符是t, 最后一个字符串是o, 中间至少有一个字符
match_obj = re.match("t.+o", "tdeo")
if match_obj:
print(match_obj.group())
else:
print("匹配失败")
# tdeo
? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有
需求:匹配出这样的数据,但是https 这个s可能有,也可能是http 这个s没有
match_obj = re.match("https?", "http")
if match_obj:
print(match_obj.group())
else:
print("匹配失败")
# http
{m} 匹配前一个字符出现m次
{m,n} 匹配前一个字符出现从m到n次
需求:匹配出,8到20位的密码,可以是大小写英文字母、数字、下划线
ret = re.match("[a-zA-Z0-9_]{6}", "12a3g45678")
print(ret.group())
# 12a3g4
ret = re.match("[a-zA-Z0-9_]{8,20}", "1ad12f23s34455ff66sfef")
print(ret.group())
# 1ad12f23s34455ff66sf
5.正则表达式:练习
以()为分组内容,可以用.group(1)获取分组内容
# 分组
match_obj = re.match("[a-zA-Z0-9_]{4,20}@(163|126|qq|sina|yahoo)\.com", "hello@163.com")
if match_obj:
print(match_obj.group())
# 获取分组数据
print(match_obj.group(1))
else:
print("匹配失败")
# hello@163.com
# 163
^匹配以什么开头,$匹配以什么结尾
# 匹配开头和结尾
try:
str1 = re.match('^[0-9].*', "345hello")
content = str1.group()
except:
print("匹配不成功")
else:
print(content)
# 345hello
# 需求: 匹配以数字结尾的数据
str1 = re.match('\S*\d$', "dadw345")
print(str1.group())
# dadw345
# 需求: 匹配以数字开头中间内容不管以数字结尾
# .*表示任意字符,任意个数
str1 = re.match('^\d.*\d$', "12hudhw93")
print(str1.group())
# 12hudhw93
[^指定字符]: 表示除了指定字符都匹配
# 需求: 第一个字符除了aeiou的字符都匹配
str1 = re.match('[^aeiou].*', "hdaw")
print(str1.group())
# hdaw
# 1.匹配出163的邮箱地址,且@符号之前有4到20位,例如hello@163.com
str1 = re.match("[a-zA-Z0-9_]{4,20}@163\.com$", "hello@163.com")
print(str1.group())
# 2.匹配出11位手机号码
str1 = re.match('[0-9]{11}', "92345678909")
print(str1.group())
# 3.匹配出微博中的话题, 比如: #幸福是奋斗出来的#
str1 = re.match('^#.*#$', "#幸福是奋斗出来的#")
print(str1.group())
| 匹配其中一个
# 需求:在列表中["apple", "banana", "orange", "pear"],匹配apple和pear
list1 = ["apple", "banana", "orange", "pear"]
for i in list1:
try:
str1 = re.match("apple|pear", i)
result = str1.group()
except:
print(f"{i}匹配失败")
else:
print(f"{result}是我要的")
. 转译小数点
# 需求:匹配出163、126、qq等邮箱
str1 = re.match('[A-Za-z0-9_]{8,20}@(qq|163|126)\.com', "1065365791@qq.com")
print(str1.group(), str1.group(1))
# 需求: 匹配qq:10567这样的数据,提取出来qq文字和qq号码
str1 = re.match('^(qq):([0-9]{4,10})', "qq:1075365791")
print(str1.group(), str1.group(1), str1.group(2))
# 需求:匹配出<html>hh</html>
str1 = re.match('<[a-zA-Z1-6]+>.*</[a-zA-Z1-6]+>', "<html>hh</html>")
print(str1.group())
利用分组匹配相同内容
# 需求:匹配出<html><h1>www.baidu.com</h1></html>
str1 = re.match('<([a-zA-Z1-6]+)><([a-zA-Z1-6]+)>.*</\\2></\\1>', "<html><h1>www.baidu.com</h1></html>")
print(str1.group())
# 需求:匹配出<html><h1>www.baidu.com</h1></html>
str1 = re.match('<(?P<s1>[a-zA-Z1-6]+)><(?P<s2>[a-zA-Z1-6]+)>.*</(?P=s2)></(?P=s1)>',
"<html><h1>www.baidu.com</h1></html>")
print(str1.group())
三、web编程框架、深浅拷贝、扩展模块
1.回顾TCP、IP:PORT、HTTP
tcp:传输控制协议,数据传输的规则,主要理解三次握手、四次挥手,其核心封装成socket套接字。编程中只需要使用套接字可完成网络连接。
服务端:
1、创建套接字对象
2、设置端口复用
3、绑定IP和端口,注意bind()里的参数是个元组
4、设置客户端套接字监听数量
5、等待客户端建立连接,接收客户端套接字和ip_port,accept()
6、接收客户端数据,解码
7、发送服务端数据,编码
8、关闭客户端套接字、关闭服务端套接字
客户端:
1、创建套接字对象
2、连接服务端connection()
3、发送数据,编码
4、接收数据,解码
5、关闭套接字对象
IP:PORT:IP即计算机在网络中的地址,分为公网IP,内网IP,域名通常绑定公网IP。IPv4:十进制组成端口,IPv6:16进制。PORT指向当前计算机运行的某一程序,端口有限,1-1023知名端口(22,23),动态端口,默认程序端口(2181,1433)
2.FasTAPI框架
框架:即封装好的功能,可以通过装饰器等方式对其功能进行扩展,完善自己的功能。FastAPI封装了,web编程中网络连接、分析请求、接收发数据等功能,只需对响应资源进行处理返回即可实现web程序。
https://blog.youkuaiyun.com/s_daqing/article/details/108294763
https://www.jianshu.com/p/4bc36027049d
# 导入 FastAPI 类
from fastapi import FastAPI, Query
# 导入 uvicorn
import uvicorn
# 对返回内容进行处理
from fastapi import Response
# 对请求参数进行处理
from enum import Enum
from pydantic import BaseModel, Field
# 创建 FastAPI 对象
app = FastAPI()
# 定义业务处理函数并设置对应的 URL 地址
# get:表示请求方式
# /index:表示请求的 URL 地址
@app.get('/index')
def index():
# 'Hello World'是响应体的内容
return 'NOT FOUND!'
# TODO:定义处理函数,访问 / 和 /gdp.html 地址时,返回 gdp.html 内容
@app.get('/')
@app.get('/gdp.html')
def gdp():
with open("./sources/html/gdp.html", 'rb') as f:
content = f.read()
return Response(content, media_type='html')
# TODO:定义处理函数,访问 /render.html 地址时,返回 render.html 内容
@app.get('/render.html')
def render():
with open("./sources/html/render.html", 'rb') as f:
content = f.read()
return Response(content, media_type='html')
# 路径参数设置枚举值类
class GenderNum(str, Enum):
male = "male"
female = "female"
# 参数设置枚举值
@app.get("/gender/{gender_name}/")
def gender(gender_name: GenderNum):
return {"gender_name": gender_name}
# 验证必传, 不传会报错
@app.get("/search/")
def search(q: str = Query(..., max_length=5)):
return {"msg": "查询参数为{}".format(q)}
# 设置表单请求体参数验证模型
class ArticleModel(BaseModel):
title: str
author: str
content: str = None # 如果没有传入这个参数,则默认为None
description: str = Field(max_length=5) # 限制最大长度
@app.post("/article/")
def add_article(article: ArticleModel):
print(article)
return {"article": article}
if __name__ == '__main__':
# 启动 Web 服务器
uvicorn.run("05-FastAPI 基本使用-返回html内容:app", host='127.0.0.1', port=8080, reload=True)
FastAPI调试地址:http://127.0.0.1:8000/docs
3.深、浅拷贝
在python中,赋值的操作都是将内存中内容的引用赋予相应的变量。对于可变类型来说,容器中内容的改变并不会改变可变容器的引用地址,即
a=[1,2,3]
b =a
a.append(7)
print(b) # -->[1,2,3,7] b和a还是指向同一个引用地址
对于不可变类型来说,值得的改变即引用地址的边改变
浅拷贝:对于不可变类型来说,浅拷贝不会创建新的引用,因为只拷贝第一层引用,不可变嵌套可变也不会创建新引用。对于可变类型来说,浅拷贝只拷贝可变类型的第一层。
深拷贝:拷贝对象的每一层引用创建新引用。对于不可变来说,第一层为不可变不创建新引用,第二层为可变,会创建新引用。对于可变类型来说,拷贝每一层,创建新引用。
例题:
import copy
a = [[1, 2, 3], [4, 5, 6]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a.append(7)
a[1][2] = 0
print(a, b, c, d)
4.扩展模块
加密模块
import hashlib
list1 = []
def encode_pass(method):
def decorate(func):
def inner(*args, **kwargs):
print(f"开始使用{method}加密:")
result = func(*args, **kwargs)
print(f"{method}加密完成")
return result
return inner
return decorate
# md5加密
@encode_pass("md5")
def encode_md5(password):
md5 = hashlib.md5()
md5.update(password.encode("utf-8"))
result = md5.hexdigest()
return result
# sha1 加密
@encode_pass("sha1")
def encode_sha1(password):
sha1 = hashlib.sha1()
sha1.update(password.encode("utf-8"))
result = sha1.hexdigest()
return result
# sha256 加密
@encode_pass("sha256")
def encode_sha256(password):
sha256 = hashlib.sha256()
sha256.update(password.encode("utf-8"))
result = sha256.hexdigest()
return result
# shasha384 加密
@encode_pass("sha384")
def encode_sha384(password):
sha384 = hashlib.sha384()
sha384.update(password.encode("utf-8"))
result = sha384.hexdigest()
return result
# 注意:hashlib 加密啊的字符串类型为二进制编码,直接加密字符串会报如下错误:
# shaa1 = hashlib.sha1()
# shaa1.update(string.encode('utf-8'))
# res = shaa1.hexdigest()
# print("sha1采用encode转换加密结果:", res)
# 或者使用byte转换为二进制
# shab1 = hashlib.sha1()
# shab1.update(bytes(string, encoding='utf-8'))
# res = shab1.hexdigest()
# print("sha1采用byte转换的结果:", res)
low = hashlib.md5()
low.update('ab'.encode('utf-8'))
res = low.hexdigest()
print("普通加密:", res)
high = hashlib.md5(b'beyondjie')
high.update('ab'.encode('utf-8'))
res = high.hexdigest()
print("采用key加密:", res)
# 输出结果:
# 普通加密: 187ef4436122d1cc2f40dc2b92f0eba0
# 采用key加密: 1b073f6b8cffe609751e4c98537b7653
时间模块
import time
# a、timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移
# b、struct_time 时间元组,共有九个元素组.
# c、format time 格式化时间,已格式化的结构使时间更具可读性.包括自定义格式和固定格式
# 生成时间戳
print(time.time())
# struct_time to timestamp
print(time.mktime(time.localtime()))
# 生成struct_time
print(time.localtime())
print(time.localtime(time.time()))
# timestamp to struct_time
print(time.gmtime())
print(time.gmtime(time.time()))
# format_time to struct_time
print(time.strptime('2011-05-08 16:37:06', '%Y-%m-%d %X'))
# 生成format_time
# struct_time to format_time
print(time.strftime('%Y-%m-%d %X', time.localtime()))
# 生成固定格式的时间表示格式
print(time.asctime(time.localtime()))
print(time.ctime(time.time()))
import datetime
# datetime模块
# 1 date类 年月日
d1 = datetime.date(2012, 3, 4)
print(d1.year)
# 2 time 时分秒
h1 = datetime.time(15, 30, 46)
print(h1.hour)
# 3 datetime 上述组合
日志模块
# logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;
# 相比print,具备如下优点:
"""
1 可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息;
2 print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出;
"""
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(filename)s -%(lineno)d - %(message)s',
filename='info.txt',
filemode='w'
)
logging.info("这是一个日志")
logging.warning("这是高一点的日志")
logging.debug("这是测试")
logging.error("这是高级日志")
# 我们将logger的级别改为DEBUG,再观察一下输出结果,控制台输出,可以发现,输出了debug的信息。
# logging.basicConfig函数各参数:
# filename:指定日志文件名;
# datefmt:指定时间格式,同time.strftime();
# level:设置日志级别,默认为logging.WARNNING;
# stream:指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,
# 当stream和filename同时指定时,stream被忽略;
# filemode:和file函数意义相同,指定日志文件的打开模式,'w'或者'a';
# format:指定输出的格式和内容,format可以输出很多有用的信息:
# 参数:作用
# %(levelno)s:打印日志级别的数值
# %(levelname)s:打印日志级别的名称
# %(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
# %(filename)s:打印当前执行程序名
# %(funcName)s:打印日志的当前函数
# %(lineno)d:打印日志的当前行号
# %(asctime)s:打印日志的时间
# %(thread)d:打印线程ID
# %(threadName)s:打印线程名称
# %(process)d:打印进程ID
# %(message)s:打印日志信息
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(filename)s -%(lineno)d - %(message)s',
filename='info.txt',
filemode='w'
)
# logger中添加StreamHandler,可以将日志输出到屏幕上,
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(level=logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s -%(lineno)d - %(message)s')
handler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(handler)
logger.addHandler(console)
# handler名称:位置;作用
#
# StreamHandler:logging.StreamHandler;日志输出到流,可以是sys.stderr,sys.stdout或者文件
# FileHandler:logging.FileHandler;日志输出到文件
# BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式
# RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚
# TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件
# SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets
# DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets
# SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址
# SysLogHandler:logging.handlers.SysLogHandler;日志输出到syslog
# NTEventLogHandler:logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志
# MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer
# HTTPHandler:logging.handlers.HTTPHandler;通过"GET"或者"POST"远程输出到HTTP服务器
# 日志回滚
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
# 定义一个RotatingFileHandler,最多备份3个日志文件,每个日志文件最大1K
rHandler = RotatingFileHandler("log.txt",maxBytes=1*1023,backupCount=3)
rHandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s -%(lineno)d - %(message)s')
rHandler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
logger.addHandler(rHandler)
logger.addHandler(console)
# 设置消息等级
# 日志等级:使用范围
# FATAL:致命错误
# CRITICAL:特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
# ERROR:发生错误时,如IO操作失败或者连接问题
# WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误
# INFO:处理请求或者状态变化等日常事务
# DEBUG:调试过程中使用DEBUG等级,如算法中每个循环的中间状态
# 也可以使用logger.exception(msg,_args),它等价于logger.error(msg,exc_info = True,_args),
#
# 将 logger.error("Faild to open sklearn.txt from logger.error",exc_info = True)
# 替换为,logger.exception("Failed to open sklearn.txt from logger.exception")
josn日志模块,yaml日志模块
import json
import logging.config
import os
def setup_logging(default_path="log_josn.josn", default_level=logging.INFO, env_key="LOG_CFG"):
path = default_path
value = os.getenv(env_key, None)
if value:
path = value
if os.path.exists(path):
with open(path, "r") as f:
config = json.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level=default_level)
def func():
logging.info("start func")
logging.info("exec func")
logging.info("end func")
if __name__ == "__main__":
setup_logging(default_path="log_josn.josn")
import yaml
import logging.config
import os
def setup_logging(default_path="./YAML.yml", default_level=logging.INFO, env_key="LOG_CFG"):
path = default_path
value = os.getenv(env_key, None)
if value:
path = value
try:
with open(path, "r", encoding="utf-8") as f:
config = yaml.load(f, Loader=yaml.FullLoader)
logging.config.dictConfig(config)
except Exception:
logging.basicConfig(level=default_level)
def func():
logging.info("start func")
logging.info("exec func")
logging.info("end func")
if __name__ == "__main__":
setup_logging(default_path="./YAML.yml")
func()
https://www.cnblogs.com/wf-linux/archive/2018/08/01/9400354.html
总结
光阴似水,人生逆旅矣。