python开发之路-day03

一 文件操作
一 介绍

计算机系统分为:计算机硬件,操作系统,应用程序三部分。

我们用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()
上一篇:201521123081《java程序设计》 第11周学习总结


下一篇:Podman中如何运行一个 Linux 虚拟机?