一 文件操作
一 介绍
计算机系统分为:计算机硬件,操作系统,应用程序三部分。
我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众所周知,应用程序是无法直接操作硬件的,这就用到了操作系统。操作系统把复杂的硬件操作封装成简单的接口给用户/应用程序使用,其中文件就是操作系统提供给应用程序来操作硬盘虚拟概念,用户或应用程序通过操作文件,可以将自己的数据永久保存下来。
有了文件的概念,我们无需再去考虑操作硬盘的细节,只需要关注操作文件的流程:
#1. 打开文件,得到文件句柄并赋值给一个变量
#2. 通过句柄对文件进行操作
#3. 关闭文件
二 在python中
#1. 打开文件,得到文件句柄并赋值给一个变量
f=open('a.txt','r',encoding='utf-8') #默认打开模式就为r
#2. 通过句柄对文件进行操作
data=f.read()
#3. 关闭文件
f.close()
三 f=open('a.txt','r')的过程分析
#1、由应用程序向操作系统发起系统调用open(...)
#2、操作系统打开该文件,并返回一个文件句柄给应用程序
#3、应用程序将文件句柄赋值给变量
四 强调!!!
#强调第一点:
打开一个文件包含两部分资源:操作系统级打开的文件+应用程序的变量。在操作完毕一个文件时,必须把与该文件的这两部分资源一个不落地回收,回收方法为:
1、f.close() #回收操作系统级打开的文件
2、del f #回收应用程序级的变量
其中del f一定要发生在f.close()之后,否则就会导致操作系统打开的文件还没有关闭,白白占用资源,
而python自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close()
虽然我这么说,但是很多同学还是会很不要脸地忘记f.close(),对于这些不长脑子的同学,我们推荐傻瓜式操作方式:使用with关键字来帮我们管理上下文
with open('a.txt','w') as f:
pass with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
data=read_f.read()
write_f.write(data)
#强调第二点:
f=open(...)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。
这就用到了上节课讲的字符编码的知识:若要保证不乱码,文件以什么方式存的,就要以什么方式打开。
f=open('a.txt','r',encoding='utf-8')
五 python2中的file与open
#首先在python3中操作文件只有一种选择,那就是open()
#而在python2中则有两种方式:file()与open()
两者都能够打开文件,对文件进行操作,也具有相似的用法和参数,但是,这两种文件打开方式有本质的区别,file为文件类,用file()来打开文件,相当于这是在构造文件类,而用open()打开文件,是用python的内建函数来操作,我们一般使用open()打开文件进行操作,而用file当做一个类型,比如type(f) is file
总结:
#流程分析:
#1:向操作系统发起系统调用
#2:操作系统打开这个文件,返回一个文件句柄给应用程序
#3: 在应用程序中把文件句柄赋值给一个变量
#注意两点:
#1:打开一个文件对应两部分,一个Python级别的文件句柄,另外一个是操作系统打开的文件(默认
#打开文件的编码是以操作系统的编码为准的,除非open()指定encoding='编码' )
#2:当文件操作完毕后,应该回收两部分资源,
#del f:回收应用程序资源(python解释器自动的垃圾回收机制已经替我们做了)
#f.close:回收操作系统
二 打开文件的模式
文件句柄 = open('文件路径', '模式')
模式可以是以下方式以及他们之间的组合:
Character | Meaning |
‘r' | open for reading (default) |
‘w' | open for writing, truncating the file first |
‘a' | open for writing, appending to the end of the file if it exists |
‘b' | binary mode |
‘t' | text mode (default) |
‘+' | open a disk file for updating (reading and writing) |
‘U' | universal newline mode (for backwards compatibility; should not be used in new code |
#1. 打开文件的模式有(默认为文本模式):
r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
w,只写模式【不可读;不存在则创建;存在则清空内容】
a, 之追加写模式【不可读;不存在则创建;存在则只追加内容】 #2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)
rb
wb
ab
注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码 #3. 了解部分
"+" 表示可以同时读写某个文件
r+, 读写【可读,可写】
w+,写读【可读,可写】
a+, 写读【可读,可写】 x, 只写模式【不可读;不存在则创建,存在则报错】
x+ ,写读【可读,可写】
xb
常用操作与了解操作
#r:默认的打开模式,只读,文件不存在则报错
# f=open('a.txt',encoding='utf-8')
# print('===>',f.read()) #读所有,bytes---decode('utf-8')--->str
# print('===>',f.read()) # print(f.readlines()) #读所有,结果放入列表中 # print(f.readline(),end='') #一次读一行
# print(f.readline(),end='')
# print(f.readline(),end='')
# print(f.readline(),end='') # f.close() #w:只写模式,如果文件存在则清空,如果文件不存在则新建
# f=open('b.txt',mode='w',encoding='utf-8')
# f.write('11111\n') #unicode---encode-->bytes
# f.write('2222\n')
# f.write('333333\n') # l=['444\n','55555\n','66666\n']
# for line in l:
# f.write(line) # f.writelines(['444\n','55555\n','66666\n'])
# f.close() #a:追加写模式,如果文件存在则把光标移动到文件末尾,如果文件不存在则新建
# f=open('c.txt','a',encoding='utf-8')
# f.write('333333\n')
# f.write('444444\n')
# f.writelines(['5555\n','666\n'])
#
# f.close() #遍历文件
# with open('a.txt',encoding='utf-8') as f:
# #不推荐使用
# # lines=f.readlines()
# # for line in lines:
# # print(line,end='')
# #推荐使用
# for line in f:
# print(line,end='') #b:以bytes的形式去操作文件内容,不能指定编码 # with open('yuanhao.jpg',mode='rb') as f:
# print(f.read().decode('utf-8'))
#
# with open('a.txt',mode='rb') as f:
# data=f.read()
# print(data.decode('utf-8')) # with open('d.txt',mode='wb') as f:
# f.write('哈哈哈hello'.encode('utf-8')) with open('d.txt', mode='ab') as f:
f.write('哈哈哈hello'.encode('utf-8')) #了解部分
# print(f.readable())
# print(f.writable())
三 操作文件的方法
#掌握
f.read() #读取所有内容,光标移动到文件末尾
f.readline() #读取一行内容,光标移动到第二行首部
f.readlines() #读取每一行内容,存放于列表中 f.write('1111\n222\n') #针对文本模式的写,需要自己写换行符
f.write('1111\n222\n'.encode('utf-8')) #针对b模式的写,需要自己写换行符
f.writelines(['333\n','444\n']) #文件模式
f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式 #了解
f.readable() #文件是否可读
f.writable() #文件是否可读
f.closed #文件是否关闭
f.encoding #如果文件打开模式为b,则没有该属性
f.flush() #立刻将文件内容从内存刷到硬盘
f.name
#read
#以文本的模式读文件,n代表的是字符的个数
# with open('a.txt','r',encoding='utf-8') as f:
# data=f.read(3)
# print(data)
#以b的模式读文件,n代表的是字节的个数
# with open('a.txt','rb') as f:
# data=f.read(3)
# print(f.tell())
# print(data.decode('utf-8'))
#tell:告诉当前光标的位置
# with open('a.txt','r',encoding='utf-8') as f:
# data=f.read(3)
# print(f.tell())
# print(data)
#seek:移动光标
# with open('a.txt','r',encoding='utf-8') as f:
# data1=f.read()
# print('first: ',data1)
# print(f.tell())
# f.seek(0)
#
# data2 = f.read()
# print('second: ',data2)
#0:文件开头
# 1:当前位置
#2:文件末尾
# with open('a.txt','r',encoding='utf-8') as f:
# f.seek(3,0)
# print(f.read())
# with open('a.txt', 'rb',) as f:
# f.read(3)
# f.seek(3,1)
# # print(f.read())
# print(f.read().decode('utf-8'))
# with open('a.txt', 'rb',) as f:
# f.seek(-3,2)
# print(f.read())
#tail -f access.log
with open('access.log','a',encoding='utf-8') as f:
f.write('11111\n')
#truncate
# with open('a.txt','a',encoding='utf-8') as f:
# f.truncate(2)
练习,利用b模式,编写一个cp工具,要求如下:
1. 既可以拷贝文本又可以拷贝视频,图片等文件
2. 用户一旦参数错误,打印命令的正确使用方法,如usage: cp source_file target_file
提示:可以用import sys,然后用sys.argv获取脚本后面跟的参数
import sys
if len(sys.argv) != 3:
print('usage: cp source_file target_file')
sys.exit() source_file,target_file=sys.argv[1],sys.argv[2]
with open(source_file,'rb') as read_f,open(target_file,'wb') as write_f:
for line in read_f:
write_f.write(line)
四 文件内光标移动
一: read(3):
1. 文件打开方式为文本模式时,代表读取3个字符
2. 文件打开方式为b模式时,代表读取3个字节
二: 其余的文件内光标移动都是以字节为单位如seek,tell,truncate
注意:
1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的
2. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果
import time
with open('test.txt','rb') as f:
f.seek(0,2)
while True:
line=f.readline()
if line:
print(line.decode('utf-8'))
else:
time.sleep(0.2)
五 文件的修改
文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都是模拟出来的效果,具体的说有两种实现方式:
方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存覆盖到硬盘(word,vim,nodpad++等编辑器)
import os with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
data=read_f.read() #全部读入内存,如果文件很大,会很卡
data=data.replace('alex','SB') #在内存中完成修改 write_f.write(data) #一次性写入新文件 os.remove('a.txt')
os.rename('.a.txt.swap','a.txt')
方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件
import os with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
for line in read_f:
line=line.replace('alex','SB')
write_f.write(line) os.remove('a.txt')
os.rename('.a.txt.swap','a.txt')
练习题:
1. 文件a.txt内容:每一行内容分别为商品名字,价钱,个数,求出本次购物花费的总钱数
apple 10 3
tesla 100000 1
mac 3000 2
lenovo 30000 3
chicken 10 3 2. 修改文件内容,把文件中的alex都替换成SB
示例: 用文件当做数据库
with open('db.txt','r',encoding='utf-8') as f:
for line in f:
user_l=line.split(',')
print(user_l[1],int(user_l[2]))
函数
一、函数
1.1、函数的用处
1. 组织结构混乱,可读性差
2. 代码冗余
3. 无法统一管理,维护难度极大
具备某一功能的工具即函数
函数的使用的必须遵循:先定义,后调用
1.2、函数的分类
内置函数:python解释器自带的函数,python解释器启动就会定义好这些函数
len()
max()
min()
sum()
2 自定义函数:
语法:
def 函数名(参数1,参数2,...):
"""注释"""
函数体
return 返回值
3 定义阶段
def tell_tag():
print('===========') def tell_msg(msg):
print(msg)
4、调用阶段
tell_tag()
tell_tag()
tell_msg('hello world')
tell_tag()
tell_tag()
# func()
打印函数
print(tell_msg)
'''
===========
===========
hello world
===========
=========== '''
函数咋定义阶段
只检测语法,不执行代码
def func():
print('aaaaaa')
xxxxx
yyyy
zzssaa
asdfasdfasdfasdf
# if
# print('asdfasdfasfd' func()
定义函数阶段的规则
1、函数的使用必须遵循:先定义,后调用
2、函数的定义,就相当于在定义一个变量,如果没有定义而直接调用,就相当于在引用一个不存在的变量名
#定义阶段
def foo():
print('from foo')
bar() #调用阶段
foo() #定义阶段
def bar():
print('from bar')
def foo():
print('from foo')
bar() #调用阶段
foo() #定义阶段
def foo():
print('from foo')
bar() def bar():
print('from bar')
#调用阶段
foo()
函数的三种形式
#无参
def main():
while True:
user=input('>>: ').strip()
# if len(user) == 0:continue
if not user:continue
password=input('>>: ')
res=auth(user,password)
if res:
print('login successful')
else:
print('logon err') 有参:函数体的代码,需要外部传入的值
def auth(user,pwd):
if user == 'egon' and pwd == '':
return True
else:
return False main() def my_max(x,y):
if x > y:
return x
else:
return y res=my_max(1,3)
print(res) #空函数
def select(sql):
'''
查询功能
:param sql: 格式后的sql
:return: xxxx
'''
pass def update():
pass def insert():
pass def delete():
pass
调用函数
1 调用函数:函数名(),
#需要注意:先通过名字找到函数的内存地址,然后加括号调用 2 函数的返回值return
注意的第一点:
在调用函数的过程中,一旦执行到return,就会立刻终止函数,并且把return后的结果当做本次调用的返回值返回
函数体内可以有多个return,但是只能执行一次
def foo():
print('')
return 1
print('')
return 2
print('')
return 3 res=foo()
print('函数调用完毕',res) 注意的第二点:
返回的值,可以是任意类型 注意的第三点: 没有return:默认返回None
可以返回一个值===>值
可以用逗号分隔,返回多个值===>tuple
def foo():
return None res=foo()
print('函数调用完毕',res,type(res)) 3:调用函数的三种形式 def foo():
print('from foo')
return 123 foo() res=foo()
print(res) res=foo()*10
print(res)
四 函数的参数
一 形参与实参
#形参即变量名,实参即变量值,函数调用时,将值绑定到变量名上,函数调用结束,解除绑定
二、具体应用
#1、位置参数:按照从左到右的顺序定义的参数
位置形参:必选参数
位置实参:按照位置给形参传值 #2、关键字参数:按照key=value的形式定义的实参
无需按照位置为形参传值
注意的问题:
1. 关键字实参必须在位置实参右面
2. 对同一个形参不能重复传值 #3、默认参数:形参在定义时就已经为其赋值
可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参)
注意的问题:
1. 只在定义时赋值一次
2. 默认参数的定义应该在位置形参右面
3. 默认参数通常应该定义成不可变类型 #4、可变长参数:
可变长指的是实参值的个数不固定
而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs ===========*args===========
def foo(x,y,*args):
print(x,y)
print(args)
foo(1,2,3,4,5) def foo(x,y,*args):
print(x,y)
print(args)
foo(1,2,*[3,4,5]) def foo(x,y,z):
print(x,y,z)
foo(*[1,2,3]) ===========**kwargs===========
def foo(x,y,**kwargs):
print(x,y)
print(kwargs)
foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs):
print(x,y)
print(kwargs)
foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z):
print(x,y,z)
foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y):
print(x,y) def wrapper(*args,**kwargs):
print('====>')
foo(*args,**kwargs) #5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
可以保证,传入的参数中一定包含某些关键字
def foo(x,y,*args,a=1,b,**kwargs):
print(x,y)
print(args)
print(a)
print(b)
print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5)
结果:
1
2
(3, 4, 5)
1
3
{'c': 4, 'd': 5}
五 练习题
1、写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完成批了修改操作
"""
先定义一个函数,然后打开要修改的文件,再打开一个临时文件,用for循环,把文件写入到临时文件中,
在用os.remove 移除老文件,最后,os.rename 把临时文件移动为新文件。
"""
import os
def file(filename,old,new):
with open('test.txt','r',encoding='utf-8') as read_f,\
open('.bak.swap','w',encoding='utf-8') as wr_f:
for line in read_f:
if old in line:
line=line.replace(old,new)
wr_f.write(line)
os.remove(filename)
os.rename('.bak.swap',filename)
file('test.txt','sun','mayun')
2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数
3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。
4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
5、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
6、写函数,检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
PS:字典中的value只能是字符串或列表
#题目二
def check_str(msg):
res={
'num':0,
'string':0,
'space':0,
'other':0,
}
for s in msg:
if s.isdigit():
res['num']+=1
elif s.isalpha():
res['string']+=1
elif s.isspace():
res['space']+=1
else:
res['other']+=1
return res res=check_str('hello name:aSB passowrd:alex3714')
print(res) #题目三:略 #题目四
def func1(seq):
if len(seq) > 2:
seq=seq[0:2]
return seq
print(func1([1,2,3,4])) #题目五
def func2(seq):
return seq[::2]
print(func2([1,2,3,4,5,6,7])) #题目六
def func3(dic):
d={}
for k,v in dic.items():
if len(v) > 2:
d[k]=v[0:2]
return d
print(func3({'k1':'abcdef','k2':[1,2,3,4],'k3':('a','b','c')}))
函数在运维中脚本的使用案例
#!/bin/python
# -*- coding:UTF-8 -*-
#author sunkedong Mail: 512378102@qq.com QQ: 512378103
import redis
import sys """
各个参数的解释:
keyspace_misses //表示未命中数
keyspace_hits //表示命中数
keyspace_hits_rate = keyspace_hits /(keyspace_hits + keyspace_misses)
connected_clients //客户端连接数
blocked_clients //客户端阻塞数
connected_slaves //从库数
instantaneous_ops_per_sec //客户端每秒执行命令频率
used_memory_rss //操作系统分配给redis的内存
used_memory //redis分配器分配的内存
mem_fragmentation_ratio //内存碎片比例
"""
#把参数定义为列表
keyindex = ['used_memory', 'used_memory_rss', 'mem_fragmentation_ratio', 'blocked_clients', 'connected_clients',
'connected_slaves',
'instantaneous_ops_per_sec', 'keyspace_hits', 'keyspace_misses', 'keypace_query_total_count',
'keyspace_hits_rate', 'status'] returnval = None
def zabbix_faild():
print "ZBX_NOTSUPPORTED"
sys.exit(2)
if len(sys.argv) != 2: #需要有一个参数,加上程序本身是两个参数。所以判断如果没有参数,就直接提示
print len(sys.argv)
zabbix_faild() try:
conn=redis.Redis(host='172.16.17.40',port='',password='')
except Exception,e:
print e
zabbix_faild() #下面是根据参数来判断并且取值,最终返回状态,加入到zabbix中
if sys.argv[1] in keyindex:
if sys.argv[1] == 'status':#如果参数为status ,执行ping,为true返回值为1,zabbix中返回1 则表示正常。
try:
conn.ping()
returnval = 1
except Exception,e:
returnval = 0
elif sys.argv[1] == 'keyspace_hits_rate':
merit = conn.info()
keyspace_hits_count = float(merit['keyspace_hits'])
keyspace_misses_count = float(merit['keyspace_misses'])
keyspace_hits_rate = keyspace_hits_count / (keyspace_hits_count + keyspace_misses_count) * 100
returnval = keyspace_hits_rate
elif sys.argv[1] == 'keypace_query_total_count':
merit = conn.info()
keyspace_hits_count = merit['keyspace_hits']
keyspace_misses_count = merit['keyspace_misses']
keypace_query_total_count = keyspace_hits_count + keyspace_misses_count
returnval = keypace_query_total_count
else:
merit = conn.info()
try:
returnval = merit[unicode(sys.argv[1])]
except Exception,e:
pass #判断返回值状态的函数
def ret_status ():
if returnval == None:
zabbix_faild()
else:
print returnval ret_status()
一 函数对象
一 函数是第一类对象,即函数可以当作数据传递
#1 可以被引用
#2 可以当作参数传递
#3 返回值可以是函数
#3 可以当作容器类型的元素
二 利用该特性,优雅的取代多分支的if
def foo():
print('foo') def bar():
print('bar') dic={
'foo':foo,
'bar':bar,
}
while True:
choice=input('>>: ').strip()
if choice in dic:
dic[choice]()
二 函数嵌套
一 函数的嵌套调用
def max(x,y):
return x if x > y else y def max4(a,b,c,d):
res1=max(a,b)
res2=max(res1,c)
res3=max(res2,d)
return res3
print(max4(1,2,3,4))
二 函数的嵌套定义
def f1():
def f2():
def f3():
print('from f3')
f3()
f2() f1()
f3() #报错,为何?请看下一小节
三 名称空间与作用域
一 什么是名称空间?
名称空间:存放名字的地方,三种名称空间,(之前遗留的问题x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)
二 名称空间的加载顺序
python test.py
#1、python解释器先启动,因而首先加载的是:内置名称空间
#2、执行test.py文件,然后以文件为基础,加载全局名称空间
#3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
三 名字的查找顺序
局部名称空间--->全局名称空间--->内置名称空间 #需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例 # max=1
def f1():
# max=2
def f2():
# max=3
print(max)
f2()
f1()
print(max)
四 作用域
#1、作用域即范围
- 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
- 局部范围(局部名称空间属于该范围):临时存活,局部有效
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1
def f1():
def f2():
print(x)
return f2
x=100
def f3(func):
x=2
func()
x=10000
f3(f1()) #3、查看作用域:globals(),locals() LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间
作业
import sys
import os
def main():
while True:
print('欢迎使用员工管理数据库')
sql = input("input sql cmmand>>").strip("")
if not sql.strip():continue
if sql =="q" or sql=='quit':
exit('正在退出系统')
elif sql=="help":
print("select name,age from staff_table where age > 22")
print("select * from staff_table where dept = 'IT'")
print("select * from staff_table where enroll_date like '2017'")
elif len(sql) == 0:
print('输入的参数空')
else:
sql_fun(sql) def sql_fun(sql): dic = {
'select': select_fun,
'delete': delete_fun,
'insert': insert_fun,
'update': update_fun
}
sql_list=(sql.split(' '))
func=sql_list[0]
res=''
if func in dic:
res=dic[func](sql_list)
return res def sql_parse(sql):
sql_dict={
'form':[],
'where':[],
'limit':[]
} def select_fun(sql):
print('select功能',sql) def delete_fun(sql):
print('delete功能',sql) def insert_fun(sql):
print('insert功能',sql) def update_fun(sql):
print('update功能',sql) main()