Python+大数据-Python进阶(四)
1. 闭包和装饰器
1.1 闭包
闭包的定义:
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
通过闭包的定义,我们可以得知闭包的形成条件:
1.在函数嵌套(函数里面再定义函数)的前提下
2.内部函数使用了外部函数的变量(还包括外部函数的参数)
3.外部函数返回了内部函数
# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
f(3)
结果是: 3
结果是: 4
1.2 装饰器
- 装饰器的定义
就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
# 装饰器
# def decorator(fn): # fn:被装饰的目标函数.
# def inner():
# '''执行函数之前'''
# fn() # 执行被装饰的目标函数
# '''执行函数之后'''
# return inner
1. 装饰器的使用场景
函数执行时间的统计
输出日志信息
#定义一个闭包函数完成登录验证
def func1():
def func2(func):
#func参数,接受一个函数
print('登录验证成功')
func()
return func2
#f=func1()
#f(show_user)
#装饰器使用的语法格式
@func1()
def show_user():
#增加登录验证逻辑
print('展示用户信息')
登录验证成功
展示用户信息
#带参数的装饰器
def func1():
def func2(f,num1,num2):
print('计算正在执行')
f(num1,num2)
return func2
#增加显示正在执行的业务
def add(num1,num2):
#加法函数
data=num1+num2
print(data)
#参数分开接收
def func3(f):
#外部参数负责接受参数
def func4(num1,num2):
#内部参数负责接受计算操作的数据
print('计算正在执行')
f(num1,num2)
return func4
@func3
def add1(num1,num2):
#加法函数
data=num1+num2
print(data)
# f=func3(add1)
# f(1,2)
# add(1,2)
#不定长参数
def func5(f):
def func6(*args,**kwargs):
#*args,**kwargs可以接受所有数据类型
print(kwargs)
print(args)
#print(args[0])
f(args[0]['num1'],args[0]['num2'],args[0]['num3'])
return func6
@func5
def chengfa(num1,num2,num3):
data=num1*num2*num3
print(data)
data={'num1':2,'num2':3,'num3':4}
chengfa(data)
结果:
{}
({'num1': 2, 'num2': 3, 'num3': 4},)
24
2. 正则表达式
2.1 with语句
# 1、以写的方式打开文件
with open("1.txt", "w") as f:
# 2、读取文件内容
f.write("hello world")
#将二进制数据写入文件
#with 实现文件读写 无论是否读写成功都会帮助关闭文件
with open('a.log','r')as f:
data=f.read()
print(data)
2.2 深拷贝和浅拷贝
copy函数是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象。
deepcopy函数是深拷贝, 只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。
- 总结
- 浅拷贝使用copy.copy函数
- 深拷贝使用copy.deepcopy函数
- 不管是给对象进行深拷贝还是浅拷贝,只要拷贝成功就会开辟新的内存空间存储拷贝的对象。
- 浅拷贝和深拷贝的区别是:
- 浅拷贝最多拷贝对象的一层,深拷贝可能拷贝对象的多层。
#变量数据赋值
#可变类型数据和不可变类型数据
#不可变类型数据的浅拷贝
a=10
b='python'
print(id(a))
print(id(b))
#数据浅拷贝
import copy
copy_a=copy.copy(a)
copy_b=copy.copy(b)
print(copy_a)
print(copy_b)
print('-'*20)
print('可变类型的浅拷贝')
c=[1,2,3,'a','e','f']
d={1:1,'name':'daidai','a_list':[4,5,1,2]}
print(id(c))
print(id(d))
print('-'*20)
copy_c=copy.copy(c)
copy_d=copy.copy(d)
e=d
print(id(copy_c))
print(id(copy_d))
print(id(e))
#copy浅拷贝时 会开辟新的存储空间,让后复制原始数据到新空间
print('-'*20)
print('修改拷贝后的数据')
copy_c[3]='ccsd'
print(copy_c)
print(c)
#进行浅拷贝是,如果有多层数据嵌套,修改拷贝给的嵌套数据会影响原始嵌套的数据
copy_d['a_list'[0]]='aa'
print(copy_d)
print(d)
# deepcopy使用深拷贝,解决浅拷贝多层数据嵌套的问题
print('-'*28)
print('深拷贝')
deepcopy_d=copy.deepcopy(d)
print(id(deepcopy_d))
deepcopy_d['a_list'][1]='wuyu'
print(deepcopy_d)
print(d)
结果:
2098178949648
2098180243120
10
python
--------------------
可变类型的浅拷贝
2098180611392
2098180243008
--------------------
2098180611136
2098180243264
2098180243008
--------------------
修改拷贝后的数据
[1, 2, 3, 'ccsd', 'e', 'f']
[1, 2, 3, 'a', 'e', 'f']
{1: 1, 'name': 'daidai', 'a_list': [4, 5, 1, 2], 'a': 'aa'}
{1: 1, 'name': 'daidai', 'a_list': [4, 5, 1, 2]}
深拷贝
1689062105920
{1: 1, 'name': 'daidai', 'a_list': [4, 'wuyu', 1, 2]}
{1: 1, 'name': 'daidai', 'a_list': [4, 5, 1, 2]}
Process finished with exit code 0
2.3re模块介绍
- re.match() 根据正则表达式从头开始匹配字符串数据
import re
# 使用match方法进行匹配操作
result = re.match("itcast","itcast.cn")
# 获取匹配结果
info = result.group()
print(info)
2.4 正则表达式
- 在实际开发过程中经常会有查找符合某些复杂规则的字符串的需要,比如:邮箱、图片地址、手机号码等,这时候想匹配或者查找符合某些规则的字符串就可以使用正则表达式了。
- 正则表达式就是记录文本规则的代码
- 匹配单个字符
代码 | 功能 |
. | 匹配任意1个字符(除了\n) |
[ ] | 匹配[ ]中列举的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即 空格,tab键 |
\S | 匹配非空白 |
\w | 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字 |
\W | 匹配特殊字符,即非字母、非数字、非汉字 |
- 匹配多个字符
代码 | 功能 |
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
# 先导入正则模块
import re
a = ' 中1#zhangsan@163.com'
b = 'lisi@qq.com'
c = '010-9188912'
d = 'http://www.baidu.com'
e = '<a> 百度 </a>'
# match 从开头进行匹配,如果开头没有匹配到就不再匹配
# 第一个参数 正则表达式,第二个参数 匹配的字符串数据
res = re.match('za',a)
# res = re.match('a',a)
# 获取匹配的数据内容
try:
print(res.group())
except:
print('未匹配到数据')
# 单个字符匹配的表达式
# . 符号表达式,表示匹配任意字符
# res = re.match('.....',a)
# res = re.match('..',c)
res = re.match('..',e)
# 获取匹配的数据内容
try:
print(f'. 匹配任意字符数据:{res.group()}')
except:
print('未匹配到数据')
# [] 范围指定
# res = re.match('[zl][hi]',a)
res = re.match('[a-z0-9][a-z0-9]',c)
# 获取匹配的数据内容
try:
print(f'[] 范围匹配数据:{res.group()}')
except:
print('未匹配到数据')
# \d 匹配数字
res = re.match('\d\d',c)
# 获取匹配的数据内容
try:
print(f'\d 数字匹配数据:{res.group()}')
except:
print('未匹配到数据')
# \D 匹配非数字
res = re.match('\D\D',e)
# 获取匹配的数据内容
try:
print(f'\D 非数字匹配数据:{res.group()}')
except:
print('未匹配到数据')
# \S 非空格数据 \s匹配空格
res = re.match('\s\S\S\S',a)
# 获取匹配的数据内容
try:
print(f'\S 字母匹配数据:{res.group()}')
except:
print('未匹配到数据')
# \w字母和数字 \W 非字母和数字
res = re.match('\W',e)
# 获取匹配的数据内容
try:
print(f'\w 字母匹配数据:{res.group()}')
except:
print('未匹配到数据')
# 匹配多个字符数据
# * 匹配0个或多个数据 * 匹配不到数据会把数据赋值为空
# res = re.match('\w*',b)
res = re.match('\d*',b)
# 获取匹配的数据内容
try:
print(f'. 字母匹配数据:{res.group()}')
except:
print('未匹配到数据')
# + 匹配1个或多个数据 +数据匹配不到抛出异常
# res = re.match('\w+',b)
res = re.match('\d+',b)
# 获取匹配的数据内容
try:
print(f'. 字母匹配数据:{res.group()}')
except:
print('未匹配到数据')
# {} 指定匹配的个数 {2,5}可以指定匹配个数范围
# res = re.match('\w{5}',b)
res = re.match('\w{2,5}',b)
# 获取匹配的数据内容
try:
print(f'匹配数据:{res.group()}')
except:
print('未匹配到数据')
# ? 匹配一次或0次
res = re.match('\d?',b )
# 获取匹配的数据内容
try:
print(f'?匹配数据:{res.group()}')
except:
print('未匹配到数据')
未匹配到数据
. 匹配任意字符数据:<a
[] 范围匹配数据:01
\d 数字匹配数据:01
\D 非数字匹配数据:<a
\S 字母匹配数据: 中1#
\w 字母匹配数据:<
. 字母匹配数据:
未匹配到数据
匹配数据:lisi
?匹配数据:
3. mini_Web
3.1 web开发流程
关系说明:
- Web服务器接收浏览器发起的请求,如果是动态资源请求找Web应用
- Web应用负责处理浏览器的动态资源请求,把处理的结果发送给Web服务器
- Web服务器再把响应结果发生给浏览器
面向对象动态服务器
import socket
import threading
import json
class WebServer():
def __init__(self):
# 1、创建服务端对象
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2、绑定ip和端口
self.server.bind(('127.0.0.1', 8001))
# 3、监听
self.server.listen(5)
def clinet_request(self,clinet_data):
# 接受客户端(浏览器)数据
recv_data = clinet_data.recv(1024)
# 解决方案一 提前判断数据是为空
# if len(recv_data) == 0:
# clinet_data.send('not data')
# 解决方案二,捕获数据处理异常,捕获到异常就结束函数的业务逻辑执行
try:
# 读取请求报文中的请求路径,根据不同的请求路径返回不同的页面
# 1、将bytes类型转为字符串
str_data = recv_data.decode()
# 2、字符串切割 按照\r\n进行切割 得到请求行、请求头数据、请求体数据 GET /register HTTP/1.1\r\nHost: 127.0.0.1:8009\r\nConnection: keep-alive\r\n
data_list = str_data.split('\r\n')
# 3、从切割后的列表中提取请求行数据 ,再次按照空格切割请求行数 GET /register HTTP/1.1
request_line = data_list[0]
# 4、从切割后的请求行列表数据中提取请求路径
url_path = request_line.split(' ')[1]
except Exception as e:
# 输出异常信息
print(e)
clinet_data.send(b'not data')
return None
# 5、根据不同的请求路径,查询不同的数据表内容返回给浏览器
if url_path == '/user':
# 查询user用户数据库表
data_dict = {'name': 'python', 'age': 19, 'gender': 'boy'}
# 返回查询到user数据表内容
# 借助josn模块 将字典转为字符串 可以在请求体中进行字符串拼接
send_data = json.dumps(data_dict)
elif url_path == '/goods':
# 查询goods商品数据库表
goods_dict = {'name':'iphone11','price':5000,'stock':100}
# 返回查询到goods数据表内容
# 借助josn模块 将字典转为字符串 可以在请求体中进行字符串拼接
send_data = json.dumps(goods_dict)
else:
send_data ='error'
# 构建报文数据
# 响应行
response_line = 'HTTP/1.1 200 ok\r\n'
# 响应头
response_header = 'Server:itcast\r\n\r\n'
# 响应体
response_body = send_data
respose_data = response_line + response_header + response_body
clinet_data.send(respose_data.encode())
def start(self):
# 循环等待客户端连接
print('服务器启动。。。')
while True:
clinet_data, addr = self.server.accept()
print('请求的客户端:', clinet_data)
# 创建线程处理客户端请求
t = threading.Thread(target=self.clinet_request, args=(clinet_data,))
# 启动线程
t.start()
web = WebServer()
web.start()
3.2应用程序开发
- 动态资源的判断通过请求资源路径来完成
- 处理客户端的动态资源请求
- 接收web服务器的动态资源请求
- Web应用程序处理动态资源请求并把处理结果返回给web服务器
- web服务器把处理结果组装成响应报文发送给浏览器
4.数据埋点
4.1 埋点介绍
埋点是数据采集的专用术语,在数据驱动型业务中,如营销策略、产品迭代、业务分析、用户画像等,都依赖于数据提供决策支持,希望通过数据来捕捉特定的用户行为,如按钮点击量、阅读时长等统计信息。因此,数据埋点可以简单理解为:针对特定业务场景进行数据采集和上报的技术方案。
- 埋点验证
埋点的验证很关键,如果上线后才发现问题,那么历史数据是无法追溯的。
验证有两种方式,一种是实时的功能验证,一种是离线的日志验证。
实时功能验证,指功能开发好后,在灰度环境上测试相应的埋点功能是否正常,比如点击相应的业务模块,日志是否会正确的打印出来。通常而言,我们需要验证如下三个类型的问题:
1、记录正确:APP发生相应的动作,检查日志是否打印正确,如:打开页面(行为埋点)、点击按钮(事件埋点)时,是否日志会记录;
2、位置正确:查看SPM、SCM码与平台申请的是否一致;
3、内容正确:设备ID、用户ID等必须记录的内容是否正确,行为、事件记录内容是否与页面实际发生的一致。
离线的日志验证,我们需要把日志写到测试环境中,查看数据上报的过程是否正确,以及对上报后的数据进行统计,侧面验证记录的准确性,如统计基本的PV、UV,行为、事件的发生数量。
很多时候,数据是需要多方验证的,存在一定的上下游信息不同步问题,比如对某个默认值的定义有歧义,日志统计会有效的发现这类问题。