第一阶段——CentOS6_Python3.6.1笔记(尚学堂-Python基础快速入门)+ 【补充】麦子-Python程序入门与进阶
虚拟机环境:
设置网络 1、修改网络地址 1.设置网卡为nat模式 2.确保物理机启动dhcp、net服务 3.编辑文件:vim /etc/sysconfig/network-scripts/ifcfg-eth0 4.ONBOOT=yes 5.设置IP: static: IPADDR= NETMASK= GATEWAY= 6.重启网络:service network restart 7.检查:ping 网关 8.ping baidu.com 9.设置DNS服务 vi /etc/resolv.conf nameserver 114.114.114.114
Centos修改DNS重启或重启network服务后丢失问题解决方法: vim /etc/sysconfig/network-scripts/ifcfg-eth0 插入以下2条 DNS1=114.114.114.114 DNS2=8.8.8.8 :wq! #保存退出
查看网卡UUID [root@huis ~]# nmcli con | sed -n '1,2p'
桌面与终端切换 su root more /etc/init init3 #终端 init5 #桌面
目录名称 应放置文件的内容 /boot 开机所需文件——内核,开机菜单及所需配置文件等 /dev 任何设备与接口都以文件形式存放在此目录 /etc 配置文件 /home 用户主目录 /bin 单用户维护模式下还能够被操作的命令 /lib 开机时用到的函数库及/bin与/sbin下面命令要调用的函数 /sbin 开机过程中需要的 /media 一般挂载或删除的设备 /opt 放置第三方的软件 /root 系统管理员的主文件夹 /srv 一些网络服务的数据目录 /tmp 任何人均可使用的“共享”临时目录 /proc 虚拟文件系统,例如系统内核,进程,外部设备及网络状态等 /usr/local 用户自行安装的软件 /usr/sbin 非系统开机时需要的软件/命令/脚本 /usr/share 帮助与说明文件,也可放置共享文件。 /var 主要存放经常变化的文件,如日志。 /lost+found 当文件系统发生错误时,将一些丢失的文件片段存放在这里
网络拷贝 scp -r x/ root@192.168.1.99:/home
scp root@192.168.1.44:/home/x/1.txt
cut命令 tail -1 /etc/passwd | cut -d':' -f1 #tail -1 /etc/passwd 打开文件最后一行; | cut -d':' 通过管道根据分界符‘:’进行分割; -f1 分割的第一段
文本排序sort -n 数值排序 -r 降序 -t 字符分隔符 -k 以哪个字段为关键字进行排序 -u 排序后相同的行只显示一次 -f 排序时忽略字符大小写
linux shell 转义符
一些转义字符的表示的特殊意思
和echo,sed连用时:
\n
表示新行
\r
表示回车
\t
表示水平的制表符
\v
表示垂直的制表符
\b
表示后退符
\a
表示“警告”(蜂鸣或是闪动)
\0xx
翻译成ASCII码为八进制0xx所表示的字符
查看文本 cat、tac、more、less、head、tail、find、grep 管道:| 表示:管道左边的命令执行的结果传给右边的命令。 分屏显示: more less head 查看前-n行 tail 查看后-n行 tail -f 查看文件尾部,不退出,等待显示后续追加至此文件的新内容
引号 $ a=10 1、反引号:`` 命令替换 :echo `whoami` 2、单引号:'' 字符串 :echo "$a" 10 3、双引号:"" 变量替换 :echo '$a' $a
权限: 用户管理:useradd, userdel, usermod, passwd, chsh, chfn, finger, id, chage 组管理:groupadd, groupdel, groupmod, gpasswd, 权限管理:chown, chgrp, chmod, umask P.S.1: useradd Leslie P.S.2: userdel Leslie P.S.3: passwd Leslie; echo "123123" | passwd --stdin Lesslie #只输入一次密码 P.S.5: id Leslie P.S.4: chsh:修改用户的默认shell; shfn:修改注释信息; P.S.6: chmod 777 000.txt #设置为rwx读写执行权限 r=4 w=2 x=1 查看用户:more /etc/passwd 用户名:x:用户ID:组ID:python_CentOS6:用户home根目录:/bin/bash
创建磁盘 先在虚拟机添加一块磁盘 》cd /dev 》fdisk sdb 》m 》n #创建一个分区 》e / p #e扩展分区;p主分区 》1/2/3/4 #1-4分区号 》1 #扇区起始位置 》786 #扇区结束位置 》m 》w #写入分区表并退出 》fdisk -l #查看分区 》mkfs.ext4/mkfs.ext3 /dev/sdb1 #格式化分区 》mkdir /opt/my_disk 》mount /dev/sdb1 /opt/my_disk/ #挂载
系统管理命令 查看进程详细情况: ps -aux #-a显示终端所以进程; -u显示进程详细状态; -x显示没有控制终端的进程; -w显示加宽,以便显示更多信息; -r只显示正在运行的进程 动态显示运行中的进程: top #执行top命令后,按下对应的按键,对结果进行排序。 M根据内存使用量来排序 P根据CPU占有率排序 T根据进程运行时间排序 U根据后面输入的用户名来筛选进程 K可以根据后面输入的PID来杀死进程 q退出 h获得帮助 终止进程: kill/killall #》kill -9 PID 杀死指定PID的进程; 》killall tail 杀死所有进程。 关机重启: reboot、shutdown、init 0 检测磁盘空间: df #-a显示所有文件系统的磁盘使用情况; -m以1024字节为单位显示; -t显示各指定文件系统的磁盘空间使用情况; -T显示文件系统 检测目录所占磁盘空间:du 查看或配置网卡信息: ifconfig #ipconfig eth0 up/down 启动/停止指定网卡; service network restart 重启所有网卡 查看网络情况: netstat -ntpl
压缩: tar -zcvf x.tar.gz x/ #z压缩格式 c压缩 v压缩的详细情况 x.tar.gz压缩后的文件 x/要压缩的那个文件或文件夹 解压: tar -zxvf x.tar.gz
sed命令下批量替换文件内容 格式: sed -i "s/查找字段/替换字段/g" `grep 查找字段 -rl 路径` 文件名 -i 表示inplace edit,就地修改文件 -r 表示搜索子目录 -l 表示输出匹配的文件名 s表示替换,d表示删除 示例:sed -i "s/test1/lgsp_Harold/g" test00.txt 把当前目录下test00.txt里的test1都替换为lgsp_Harold
软件安装管理 安装软件步骤: 1、检查是否已经安装: rpm -qa | grep jdk #-qa查询已经安装的所有包 -qi查询已经安装的指定软件包的详细信息 -qc查询已安装指定的软件包的配置文件路径 2、下载软件包 3、安装依赖 #rpm -ivh 软件包 P.S.1: 使用rpm命令安装软件不需要指定安装路径,rpm文件在制作时已经确定安装路径。
yum install tree/man/ 本地yum源配置:管理rpm软件包 修改配置本地yum源 yum配置文件: cd /etc/yum.repos.d/ 访问 https://opsx.alibaba.com/mirror
centOS帮助》
1、备份
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
2、下载新的CentOS-Base.repo 到/etc/yum.repos.d/
CentOS 5
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo
或者
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo
CentOS 6
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
或者
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
CentOS 7
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
或者
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
3、之后运行yum makecache生成缓存
yum repolist #返回yum列表
搭建本地yum源
放入CentOS-6.9-x86_64-bin-DVD.iso
cd /mnt/
mkdir repos
mount /dev/cdrom /mnt/repos/
cd repos/Packages/
cd /etc/yum.repos.d/
cp Centos-6.repo Centos-6.repo.backup
mv Centos-6.repo my.repo
vim my.repo
[base]
name=CentOS-my #随便填
baseurl=file:///mnt/repos/
gpgcheck=0 #0不检测文件是否安全或完整,1检测。
gpgkey=http://**** #如果gpgcheck=0,此行删去
#其他配置根据需求自行配置,默认后面多余配置不需要,全删。
#保存退出
yum clean all
yum makecache
yum repolist
vi /etc/fstab #自动挂载
需要下载阿里云yum源里面的所有rpm文件: reposync
mv my.repo my.repo.backup
mv Centos-6.repo my.repo.backup Centos-6.repo my.repo
yum clean all
yum makecache
mkdir /opt/repos/
yum reposync -r base -p /opt/repos/ #下载yum源
linux-nfs 服务端配置 1.安装nfs-utils和rpcbind yum install -y nfs-utils rpcbind 2.设置开机启动服务 chkconfig nfs on chkconfig rpcbind on 3.启动相关服务 service rpcbind start service nfs start 4.创建共享目录 mkdir /share 5.编辑/etc/exports文件,添加如下内容 vim /etc/exports /share client_ip(rw,no_root_squash,no_subtree_check) 客户端的指定方式 指定ip地址的主机:192.168.44.44 指定子网中的所有主机:192.168.44.0/24 或 192.168.0,0/255.255.255.0 指定域名的主机:nfs.test.com 指定域中的所有主机:*.test.com 所有主机:.* 选项说明: ro:共享目录只读 rw:共享目录可读写 all_squash:所有访问者都映射为匿名用户或用户组 no_all_squash(默认):访问用户先与本机用户匹配,匹配失败后,再映射为匿名用户或用户组 root_squash:将来访的root用户映射成匿名用户或用户组 no_root_squash:将来访的root用户保持root账号权限 anonuid=<UID>:指定匿名访问用户的本地用户UID,默认为nfsnobody(65534) anongid=<GID>:指定匿名访问用户的本地用户组GID,默认为nfsnobody(65534) secure(默认):限制客户端只能从小于1024的tcp/ip端口连接服务器 insecure:运行客户端从大于1024的tcp/ip端口连接服务器 sync;将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据一致性 async:将数据先保存在内核缓冲区中,必要时才写入磁盘 wdelay(默认):检查是否有相关的写操作,如果有,则将这些写操作一起执行,这样可以提高效率 no_wdelay:若有写操作,则立即执行,应与sync配合使用 subtree_check(默认):若输出目录是一个子目录,则nfs服务器将检查其父目录的权限 no_subtree_check:即使输出的目录是一个子目录,nfs服务器也不检查其父目录的权限,这样可以提高效率 6.刷新配置立即生效 exportfs -a 客户端配置 1.安装nfs-utils和rpcbind yum install nfs-utils repcbind 2.设置开机启动服务 chkconfig nfs on chkconfig rpcbind on 3.启动服务 service rpcbind start service nfs start mkdir -p /mnt/share 5.挂在目录 mount -t nfs server_ip:/share /mnt/share 6.查看挂载目录 df -h 7.卸载挂载目录 umount /mnt/share 8.编辑/etc/fstab,开机自动挂载 vim /etc/fstab #在结尾添加一行 server_ip:/share /mnt/share nfs rw,tcp,intr 0 1
源码安装步骤:
1、下载 2、查看源码(c环境) 3、准备编译环境(gcc) 4、检查(依赖,兼容),预编译 5、编译 6、安装 7、配置环境变量 》rpm -qa | grep gcc gcc-4.4.7-18.el6.x86_64 》tar -zxvf Python-3.6.1.tgz
》cd Python-3.6.1 》./configure --prefix=/usr/python-3.6.1 #预编译 》》please run ./configure --enable-optimizations 开启优化 》yum install zlib* openssl* 》y
》#yum install libffi-devel -y #Python3.7需要一个新的包libffi-devel,安装此包之后,再次进行编译安装即可。 不然会报ModuleNotFoundError: No module named '_ctypes' 》#./configure --prefix=/usr/python-3.7.0 --enable-optimizations #Python3.7.0后续通过pip3 安装ipython会报找不到SSL,暂未找到解决方法,网上3.6.*的解决方法无效
》./configure --prefix=/usr/python-3.6.1 --enable-optimizations
》make #编译
》make install #安装
配置python全局环境变量 》printenv 》vi ~/.bash_profile 或 vi ~/.bashrc #修改配置文件 》PATH=$PATH:/usr/python-3.7.0/bin #最后一行加这个 或 ( PYTHON_HOME=/usr/python-3.7.0 PATH=$PATH:$PYTHON_HOME/bin ) 》wq! #保存退出 》source ~/.bashrc 》python3 P.S.:环境变量除了配置在~/.bashrc里面,也可以配置在/etc/profile。二者区别:.bashrc为当前用户的环境变量配置文件;profile为整个系统的环境变量配置文件。
安装一个python小工具 cd /usr/python-3.6.1/bin pip3 install ipython
解释器: 》mkdir python 》cd python 》vi Test1.py #!/usr/python-3.6.1/bin/python print("hello world") def main(): print("结束") :wq! 》chmod u+x Test1.py 》./Test1.py
修改vim制表符空格 》vi ~/.vimrc set nu set ts=4 set sw=4
python注释 #fdff ''' sssss ssss ddd dd '''
python编码
包含中文时
#encoding=UTF-8
Python关键字(保留字): python3 >>> import keyword >>> keyword.kwlist ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
money=22 a=44 b="lgsp" #输出四位数 print("%04d"%money) print("%s数字%s字符%s字符"%(money,a,b)) >>>>输出: 0022 22数字44字符lgsp字符 P.S.1:%s通用,%d数字。如果要填充数字,必须使用%d。每一个%表示一个占位符 #输出数字包含两位小数,没有用0填充 w=3.1415926 print("%.2f"%w) print("%a%%"%a) #%%第一个表示转义符 c=2 c *=1+2-3+4 #c === 8
w=float(input("输入体重:")) h=float(input("输入身高:")) BMI=w/(h**2) if BMI<18.5: print("瘦") elif 18.5<=BMI<=23.9: print("正常") elif BMI>23.9: print("胖") print("你的BMI指数:%s"%BMI)
i = 1 sum = 0 while i <= 100: sum = sum + i i += 1 print("%d"%sum)
i = 1 sum = 0 while i <= 100: if i%2 == 0: sum = sum + i else: pass i += 1 print("%d"%sum)
y = 1 while y<=10: x = 1 while x<=10: print("*",end="") x+=1 print("") y+=1 print("结束")
x = 1 while x <= 9: y = 1 while y <= x: print(x,"*",y,"=",x*y,end="\t") y += 1 print("") x += 1 print("结束")
age = int(input("输入:")) i=1 while True: if i==age: print("输出:%d"%i) break else: print("错了") i+=1
i=0 while i<10: i+=1 if i%2==0: print("%d是偶数"%i) continue print("当前的i是%dxx"%i) print("ok") else: print("else,i为%d"%i) print("结束")
i=int(input("请输入行数:")) a=0 while a<i: b=0 while b<a: print(" ",end="") b+=1 c=i-a while c>0: print("*",end=" ") c-=1 print("") a+=1
for i in "abcefg": print(i) else: print("没有") for r in range(1,10): print(r)
a="abcdefg" i=len(a) while i>0: i-=1 print(a[i]) my_str="Hello World, My Name Is HuaShuai. My Name Is HuaShuai." my_str.find("HuaShuai") my_str.rfind("HuaShuai") my_str.find("huashuai") my_str.index("HuaShuai) my_str.rindex("HuaShuai") my_str.index("huashuai") #find找不到返回-1,index找不到报错 #find,index从坐找;rfind,rindex从右找 my_str.count("HuaShuai") my_str.replace("HuaShuai","huaShuai") #全部替换 my_str.replace("HuaShuai","huaShuai",1) #替换一次 my_str.split(" ") my_str.split(" ",2) #下标2,隔成三个段 my_str.partition("World") #以World作为切割符,并保留
my_str1="hsad \tasdsadfxc zf dass \tasdas \nasdas das" my_str1.split()
my_str1.split()[-2] title:每个单词首字母大写 capital:字符串第一个字符大写 startswith:检查字符串是否以obj开头,是返回True endswith:检查字符串是否以obj结束,是返回True file_name="xxxx.txt" if file_name.endswith(".txt"): print("文本文档") lower:转换所有大写字母为小写 upper:转换所有小写字母为大写 ljust:左对齐 rjust:右对齐 center:居中 a.ljust(50) a.rjust(50) a.center(50) lstrip:去掉左边空格 rstrip:去掉右边空格 strip:去掉空格 test_str=" avx " test_str.lstrip() splitlines #按照换行符分割,返回一个包含各行作为元素的列表 lines="sada\nsndgfgdfg\ndfgfdg\nasdaolzxv" lines.splitlines() isalpha:判断字符串是否只包含字母,是返回True isdigit:判断字符串是否只包含数字,是返回True isalnum:判断字符串是否包含字母或数字,是返回True isspace:判断是否只包含空格,是返回True join:在每个元素后面追加一个元素 as=["500","60","990"] "-".join(as)
append、extend、insert num1=[100,200,300,400,"500"] num1.append("600") num2=[1,2] num1.extend(num2) num1.insert(0,"000") num1[2]="005" num1.count("000") 000 in num1 000 not in num1 del、pop、remove del num1[1] num1.pop() #默认删除最后一个 num1.remove(400) reverse、sort num1.reverse() num1[-1::-1] num3=[1,3,4,9,50,45,44] num3.sort() num3.sort(reverse=True)
if 条件表达式: pass elif 条件表达式: pass else: pass while 条件表达式: pass break else: pass for 变量(该变量不需要事先声明) in 字符串或集合 pass else: list names=[] names[0]=值 tuple names=(任何类型) dist names={key:value} #key不能重复,value可以为任何类型 names[key]=值
def 函数名():
pass
def 函数名(参赛1,参赛2,....):
pass
可变类型:【字典、列表】值可以修改(内存地址不变,所保存的值变化了)、引用可以修改(变量的内存地址改变了)
不可变类型:【数字、字符串、元组】值不可以修改,引用可以修改(=赋值号)
在函数里面修改全局变量:
1、当全局变量是可变类型,所以在函数里面任意修改(值、引用)
2、如果全局变量是不可变类型,在函数里面不能修改值,也不能修改引用,除非加上global才能修改引用。
def test1(x,y): x.replace("c","C") y.append(10) print("x变量指向的内存地址:%s"%id(x)) print("y变量指向的内存地址:%s"%id(y)) a="abcdefg" b=[1,2,3] print("a变量指向的内存地址:%s"%id(a)) print("b变量指向的内存地址:%s"%id(b)) test1(a,b)
def test1(x,y,*args): print(x,y) print(args) sum=x+y for i in args: sum+=i print(sum) test1(2,3,4,5,6,7) print("="*50) def test2(x,*args,**kwargs): print(x) print(args) print(kwargs) sum=x for i in args: sum+=i for i in kwargs.values(): sum+=i print(sum) test2(2,3,4,num1=5,num2=6,num3=7) print("="*60) nums=[3,4] nums2={"num1":5,"num2":6} test2(2,*nums,**nums2)
#递归函数
#P.S.1:在函数的内部调用自己本身
#P.S.2:递归函数本质是一个方法的循环调用,主要有可能出现死循环
#P.S.3:一定要定义递归的边界(什么时候退出循环) n=4 result=1 i=1 while i<=4: result=result*i i+=1 print(result) def test1(n):#定义函数就是计算数字n的阶乘 if n==1: return 1 return n*test1(n-1) print(test1(4))
#斐波拉契数列 def get_num(n):#获取斐波拉契数列中第n个数字的值 if n==1 or n==2: return 1 return get_num(n-1) + get_num(n-2) nums=[] for i in range(1,20): nums.append(get_num(i)) print(nums)
#匿名函数 def test(a,b): return a+b print(test(22,33)) func=lambda a,b:a+b print(func(22,33)) print("——"*30) def test1(a,b,func): result=func(a,b) return result print(test1(22,33,lambda x,y:x*y)) print("——"*30) stus=[{"name":"zs","age":"22"},{"name":"lw","age":"44"},{"name":"sv","age":"34"}] stus.sort(key=lambda x:x["name"])#匿名函数的功能:返回列表中每个元素(字典)的“name”对应的值 print(stus) print("——"*30) def test2(a,b,func): result=func(a,b) return result func_new=input("请输入你的操作:") func_new=eval(func_new)#把字符串转换成可以执行的表达式 print(test2(22,33,func_new)) #控制台输入表达式,如:lambda a,b:a+b
#数字交换 a=22 b=33 #借用元组 a,b=b,a #使用中间变量 c=0 c=a a=b b=c #a+=a和a=a+a的区别 def test(num): num+=num print(num) a=10 b=[10] test(a) test(b) print(a) print("——"*30) print(b)
可变类型不能作为字典的key
#打开和关闭文件 # f=open("test1.txt","w") # f.write("hello\tworld") # f.close # f=open("test1.txt","r") # content=f.read() # print(content) # f.close() #复制文件 source_file="/home/lgsp/Desktop/PythonWorkspace/test1.txt" #目标文件在当前目录下,找到原始文件名并到末尾,文件名在原始文件名的前面加上copy- dest_file="copy-"+source_file[source_file.rfind("/")+1:] print("目标文件名字:%s"%dest_file) #打开文件 source_file=open(source_file) #创建目标文件 dest_file=open(dest_file,"w") #读取原始文件 content=source_file.read() #把读取的内容写到目标文件中 dest_file.write(content) #关闭文件 source_file.close() dest_file.close()
1、获取当前读写的位置tell #打开一个已经存在的文件 f = open("test.txt","r") #读取三个字符 str = f.read(3) print("读取的数据是:", str) #查找当前位置 position = f.tell() print("当前的文件位置:", position) #再读取一个字符 str = f.read(1) print("当前数据是:", str) #查找当前位置 position = f.tell() print("当前的文件位置:", position) 2、定位到某位置 如果在读写文件的过程中,需要从另外一个位置进行操作的话,可以使用seek(),seek(offset, from)有两个参数 offset:偏移量 from:方向 0:表示文件开头 1:表示当前位置 2:表示文件末尾 f.seek(0,0) f.close() os模块 import os #重命名 os.rename("test1.txt","test2.txt") #获取文件绝对路径 os.path.abspath("test2.txt") #获取文件大小 os.path.getsize("test2.txt") #批量重命名目录下所有的文件 import os file_list=os.listdir("test/") for f in file_list: #print(f) #重新命名之后目标文件名 dest_file="re-"+f #f为原始文件名的名字,它不在工作目录,所以不能使用文件作为相对路径 #f文件的相对路径为“test/f。”或者干脆写绝对路径 #os.rename(f,dest_file) #os.rename("test/"+f,"test/"+dest_file) #采用绝对路径的形式写代码 #获得父目录的绝对路径 动态获取绝对路径 parent_dir=os.path.abspath("test") #文件的绝对路径=父目录的绝对路径+/+文件名 source_file=os.path.join(parent_dir, f) dest_file=os.path.join(parent_dir, dest_file) os.rename("test/"+f,"test/"+dest_file)
import os #从/home/python目录下找包含有test的py文件是哪些? #包含有test的所有py文件列表 file_list=[] #递归函数,该函数中所有的文件路径全部采用绝对路径,parent_dir:文件所在的父目录的绝对路径,file_name:表示当前你要处理的文件或者目录 def find_test(parent_dir,file_name): file_abspath=os.path.join(parent_dir,file_name)#当前正在处理的文件或者目录的绝对路径 if os.path.isdir(file_abspath):#判断当前的文件是一个目录 for f in os.listdir(file_abspath):#进入目录,列出该目录下所有文件列表 find_test(file_abspath,f)#递归调用自己本身的函数 else: if file_abspath.endswith(".py"):#如果传入的文件就是一个文件,判断文件名是否以.py结尾 if read_and_find_test(file_abspath):#读取该py结尾的文件,并且看看文件内容中是否包含有test file_list.append(file_abspath) #该函数主要功能:读取py结尾的文件,并且看看文件内容中是否包含test,如果有就返回True,否则返回False def read_and_find_test(py_file): flag=False#定义一个是否包含test的标记变量,默认文件中不包含test为False f=open(py_file)#打开文件 while True:#由于是一行一行的读取文件,所以需要死循环 line=f.readline()#读取其中一行 if line=='':#文件读到最后一行,终止循环 break elif "test" in line: flag=True break f.close() return flag # print(read_and_find_test("/home/lgsp/Desktop/PythonWorkspace/test.py")) find_test("/home/lgsp/Desktop","PythonWorkspace") print(file_list)
import os def read_stus(): ''' zs\t33\t4545423 ls\t22\t5553344 ww\t22\t5563320 ''' if os.path.exists(file_name): f=open(file_name) while True: student_str=f.readline() if student_str=='': break else: student_info_list=student_str.split() # print("student_info_list:"+student_info_list) student={"name":student_info_list[0],"age":student_info_list[1],"qq":student_info_list[2]} # print("student"+student) stus.append(student) def write_student_to_file(): if os.path.exists(file_name): if os.path.exists(backup_file): os.remove(backup_file) os.rename(file_name,"backup-"+file_name) f=open(file_name,"w") for student in stus: student_str="%s\t%s\t%s"%(student['name'],student['age'],student['qq']) f.write(student_str) f.write("\n") f.close() def print_menu(): print("="*30) print("学生管理系统".center(30)) print("输入1:添加学生") print("输入2:查找学生") print("输入3:修改学生") print("输入4:删除学生") print("输入5:查看所有学生") print("输入6:退出") def add_student(): name=input("请输入学生姓名:") age=int(input("请输入学生年龄:")) qq=input("请输入学生的QQ号:") stu={}#声明一个字典变量 stu["name"]=name stu["age"]=age stu["qq"]=qq stus.append(stu) print("添加成功") def search_student(name): for item in stus: if item["name"]==name.strip(): print("%s学生存在"%name) print_student(item) return item else: print("学生%s没有找到"%name) def print_student(item): print("%s\t%s\t%s"%(item["name"],item["age"],item["qq"])) def print_all_students(): print("序号\t姓名\t年龄\tQQ号") for i,item in enumerate(stus,1): print("%s\t"%i,end="") print_student(item) def del_student(name): student=search_student(name) stus.remove(student) print("删除成功") def main(): print_menu() read_stus() while True: operate=input("请输入你想要的操作:") if operate=="1": add_student() write_student_to_file() if operate=="2": name=input("请输入要查找的学生姓名:") search_student(name) if operate=="3": pass if operate=="4": name=input("请输入要删除的学生姓名:") del_student(name) print("删除学生%s成功"%name) write_student_to_file() if operate=="5": print_all_students() if operate=="6": break file_name="stus.json"#存放学生数据的文件 backup_file="backup-stus.json"#存放学生数据的文件 #一个学生一个字典,字典用列表来存储 stus=[] main()
#定义一个类 class Car: def start(self): print("汽车启动") def print_car_inf(self): print("车的名字是:%s,颜色是:%s"%(self.name,self.color)) c=Car()#构建一个对象 c.name="BMW" c.color="金色" c2=Car() c.print_car_inf() c.start()
保留方法:__****__()
class Person: def __init__(self): self.name="zs" self.age="33" print("对象初始化") def work(self): pass p1 = Person() print(p1.name)
class Person(): #初始化对象的方法,不是构建对象的方法 def __init__(self,name,age,height): self.name=name self.age=age self.height=height def introduce(self): print("姓名:%s,年龄:%s"%(self.name,self.age)) p1=Person("zs",15,1.5) p1.introduce() ''' __init__()方法,在创建一个对象时默认被调用,不需要手动调用 __init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y) __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去 '''
class Person(): #初始化对象的方法,不是构建对象的方法 def __init__(self,name): self.name=name #toString() def __str__(self): return "姓名:%s"%(self.name) def introduce(self): print("姓名:%s"%(self.name)) p1=Person("zs") print(p1) ''' 在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法 当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印从这个方法中return的数据 '''
''' 骰子游戏 每次游戏有2人,每人3颗骰子(dice),估计点数的个数。 ''' import random class Game: def __init__(self,player1,player2): self.player1=player1 self.player2=player2 print("游戏初始化成功,可以开始") #开始游戏 def start_game(self): self.player1.cast() self.player2.cast() # play1_dice_count_list=[self.player1.dices[0].count,self.player1.dices[1].count,self.player1.dices[2].count] # play2_dice_count_list=[self.player2.dices[0].count,self.player2.dices[1].count,self.player2.dices[2].count] # print("玩家1抛骰子之后的点数为:%s"%str(play1_dice_count_list)) # print("玩家2抛骰子之后的点数为:%s"%str(play2_dice_count_list)) print(self.player1) print(self.player2) def get_win(): self.player1.guess_dice() self.player2.guess_dice() #判断谁赢了 class Player: def __init__(self,name,sex,*dice): self.name=name self.sex=sex self.dices=dice#表示该玩家拥有的骰子列表(元组) #玩家抛骰子 def cast(self): for dice in self.dices: dice.move() #猜点数 def guess_dice(self): #4个数,2点数 return (4,2) def __str__(self): play_dice_count_list=[self.dices[0].count,self.dices[1].count,self.dices[2].count] return "姓名为:%s,投掷的骰子点数信息为:%s"%(self.name,str(play_dice_count_list)) class Dice: def __init__(self): self.count=0 #骰子滚动的方法,滚动之后该骰子的点数确定了 def move(self): self.count=random.randint(1,6) #游戏开始之前准备6颗骰子 d1=Dice() d2=Dice() d3=Dice() d4=Dice() d5=Dice() d6=Dice() #准备两个玩家,并分配骰子 p1=Player("player1","男",d1,d2,d3) p2=Player("player2","男",d4,d5,d6) #一共要玩5次游戏 for i in range(1,6): print("第%d次游戏的情况-------------"%i) game=Game(p1,p2) game.start_game() #根据业务需求抽象出类 #根据业务需求来分析每个对象的职责,并在类中定义职责所对应的函数或者方法
class User: def __str__(self): return "用户名为:%s,密码为:%s"%(self.username,self.password) def set_password(self,password): if len(password)>=6: self.password=password else: print("密码:%s,长度不符合规定"%password) u1=User() u1.username="华帅" # u1.password="123456789" u1.set_password("1236663") print(u1)
私有属性、私有方法
# 隐藏(私有)属性 __**** # 如果要输出隐藏属性的值,必须在类里面操作 # 隐藏(私有)方法 __****() class User: def __init__(self,password): if len(password)>=6: self.__password=password # 赋值给隐藏属性 else: print("密码:%s,密码不符合规定"%password) def __str__(self): #调用隐藏方法 self.__say_hello() #返回隐藏属性的值 return "__str__输出隐藏属性的值:%s"%self.__password # 输出隐藏属性的值 def get_password(self): return "get_password输出隐藏属性的值:%s"%self.__password # 隐藏方法 def __say_hello(self): print("隐藏方法的值:" + self.__password) u1=User("1000233") # print("直接打印隐藏属性:"+self.__password) # 通过方法输出隐藏属性的值 print(u1.get_password()) #直接调用隐藏方法 # u1.__say() print("隐藏方法在只能在类里面调用;在方法__str__里面调用") print(u1)
class User: def __init__(self): print("对象初始化") def __del__(self): print("对象即将被销毁,内存回收") #new一个对象就调用__init__方法 u1=User() u2=u1 #del内置的函数,删除内存中的一个对象 del u1 print("--"*30) del u2 print("++"*30) #当有1个变量保存了对象的引用时,此对象的引用计数就会加1 #当使用del删除变量指向的对象时,如果对象的引用计数不是1,比如3,那么此时只会让这个引用技术减1,即变成2,当再次调用del时,变成1,如果再调用1次del,此时会真的把对象进行删除。 #当整个程序执行完了,也会把对象进行删除。
继承
class Animal: def __init__(self): print("动物的初始化") self.name="动物" def eat(self): print("吃饭") def sleep(self): print("睡觉") class Dog(Animal): def __init__(self,name): print("狗初始化") self.name=name def shout(self): print("旺旺叫") class Cat(Animal): # def __init__(self): # print("猫初始化") def catch(self): print("抓老鼠") dog = Dog("小白") print(dog.name) dog.eat() cat = Cat() print(cat.name)
私有方法和私有属性不可以被继承
class Animal: def __init__(self): print("动物的初始化") # self.name="动物" self.__name="动物" def eat(self): print("吃饭") def sleep(self): print("动物的名字:%s"%self.__name) print("睡觉") class Dog(Animal): def __init__(self): print("狗初始化") # self.name=name def shout(self): print("旺旺叫") dog = Dog() # print(dog.__name) dog.eat() dog.sleep()
多继承
class Animal: def __init__(self): print("动物的初始化") # self.name="动物" self.__name="动物" def eat(self): # print("动物的名字:%s"%self.__name) print("吃饭") def sleep(self): print("睡觉") class Dog(Animal): def __init__(self,name): print("狗初始化") self.name=name def shout(self): print("旺旺叫") class Cat(Animal): # def __init__(self): # print("猫初始化") def catch(self): print("抓老鼠") class ZangAo(Dog): def fight(self): print("战斗") dog = Dog("小白") # print(dog.name) dog.eat() cat = Cat() # print(cat.name) # cat.eat() za = ZangAo("藏獒") za.eat()
#多继承 class A: def test(self): print("A----test()") class B: def test(self): print("B----test()") class C(A,B): def test1(self): print("C----test1()") c=C() print("多继承时,方法调用优先顺序以继承顺序有关,除去自身的方法,优先调用第一个类的继承") print(C.__mro__)#打印继承调用的优先顺序 c.test() #调用A的
方法重写
#重写父类方法与调用父类方法 #重写父类方法 #所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。 class Animal: #父类定义init方法 def __init__(self): print("动物的初始化") # self.name="动物" self.color="黄色" def eat(self): # print("动物的名字:%s"%self.__name) print("吃饭") def sleep(self): print("睡觉") class Dog(Animal): #__init__方法和父类的__init__名字一样,所以叫方法的重写。 def __init__(self,name): super().__init__() #主动调用父类的init方法 print("狗初始化") self.name=name def shout(self): print("旺旺叫") def eat(self): super().eat() #调用父类的eat方法 print("狗自己的get方法") class Cat(Animal): #重写父类__init__方法 def __init__(self): print("猫初始化") def catch(self): print("抓老鼠") class ZangAo(Dog): def fight(self): print("战斗") dog = Dog("小白") #如果子类中对某个方法重写了,优先调用子类自己本身的方法 #虽然init方法重写了,可是还想自动调用父类的init方法 print(dog.name) print(dog.color) #如果子类没有super().__init__(),父类的对象属性不能继承 dog.eat()
多态
#多态 class F1(): def show(self): print("F1.show") class S1(F1): def show(self): print("S1.show") class S2(F1): def show(self): print("S2.show") def Func(obj): obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj)
实例对象和类对象之间的区别
1、类属性属于类
2、类属性的访问,可以通过类名,也可以通过对象
3、类属性的修改和删除,只能通过类名
#类属性:所属类,这个类下所有的对象都可以共享这个类属性。 class User(object): name="zs" #公共的类属性 __assword="123456" #私有的类属性 def __init__(self,sex,username): self.sex=sex self.username=username class QQ_User(User): pass u1=User("男","Leslie") print("对象直接调用类属性") print(u1.name) print("name从父类继承过来的,由于name属于类属性,可以直接通过子类来访问,也可以通过类的对象来访问。") print(QQ_User.name) print("如果只能通过对象修改,仅仅给该对象定义了一个对象属性name,并赋值为“ww”") u.name="ww" #本质上没有修改类属性,仅仅给该对象定义了一个对象属性name,并赋值为“ww” print(u.name) print("类属性修改,只能通过类来修改") User.name='zl' print(u.name ) del u.name # 本质上删除了对象的name属性,并没有删除类的属性 print(User.name)
类方法、静态方法
class A(object): name="zs" def test1(self): print("-------------A 的test1方法") #类方法一定要在方法的上面加上一个修饰器(java注解),类方法的参数cls,代表当前的类 @classmethod def test2(cls): cls.name='ww' print("-------------A 的test2方法") #静态方法,属于类。没有默认传递的参数(self\cls),可以通过类对象来调用,也可以通过类名来调用 @staticmethod def test3(): A.name="ls" print("-------------A 的test3方法") a = A() a.test2() A.test2() print("类方法打印") print(A.name) A.test3() print("静态方法打印") print(A.name)
设计模式:(__new__())方法
# __new__ class User(object): def __init__(self,username,password): self.username=username self.password=password print("对象已经构建好,由解释器自动回调的init方法,对象初始化") #new方法是当对象构建的时候由解释器自动回调的方法。该方法必须返回当前类的对象。 def __new__(cls,username,password): print("User类的对象开始构建") return object.__new__(cls) def __str__(self): return "用户名:%s,密码:%s"%(self.username, self.password) u=User("zs","1234") print(u) #__new__()必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。 #__init__()有一个参数self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。 #若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。
特殊方法名 默认的参数 功能描述
__init__() self 初始化当前对象
__str__() self 打印从这个方法中return的数据(打印当前对象)
__del__() self 删除当前对象
__new__() cls 实例化对象
单例模式:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式
单例模式一
# 单例模式 class User(object): def __init__(self,name): self.name=name u1 = User("zs") u2 = User("ls") print(u1==u2)# ==判断表达式如果返回True,这两个对象是一个对象,并内存地址相同 print("u1对象的内存地址:%s,u2对象的内存地址:%s"%(id(u1),id(u2)))
# 单例模式 class User(object): __instance=None # 私有类属性 def __init__(self,name): self.name=name @classmethod def get_instance(cls,name): if not cls.__instance: # 如果__instance为None cls.__instance=User(name) return cls.__instance # u1 = User("zs") # u2 = User("ls") u1 = User.get_instance("zs") u2 = User.get_instance("ls") print(u1==u2)# ==判断表达式如果返回True,这两个对象是一个对象,并内存地址相同 print("u1对象的内存地址:%s,u2对象的内存地址:%s"%(id(u1),id(u2)))
单例模式二
# 单例模式 class User(object): __instance=None # 私有类属性 def __init__(self,name): self.name=name def __new__(cls,name): if not cls.__instance: # 保证object.__new__()方法只会调用一次 cls.__instance=object.__new__(cls) #创建当前类的对象并赋值给__instance return cls.__instance u1 = User("zs") u2 = User("ls") print(u1==u2)# ==判断表达式如果返回True,这两个对象是一个对象,并内存地址相同 print("u1对象的内存地址:%s,u2对象的内存地址:%s"%(id(u1),id(u2)))
工厂模式
#工厂模式 class Person(object): def __init__(self,name): self.name=name def work(self): print(self.name + "开始工作了") #person完成work,需要使用一把斧头 #在原始社会,人需要一把石斧 # aux = StoneAxe("花岗岩斧头") # aux.cut_tree() #使用钢铁的斧头 axe = SteelAxe("加爵斧头") axe.cut_tree() class Axe(object): def __init__(self,name): self.name=name def cut_tree(self): print("%s斧头开始砍树"%self.name) class StoneAxe(Axe): def cut_tree(self): print("使用石头做的斧头砍树") class SteelAxe(Axe): def cut_tree(self): print("使用钢铁做的斧头砍树") # p = Person("原始人") # p.work() p = Person("现代人") p.work()
#简单工厂模式:Simple Factory模式不是独立存在的设计模式,他是Factory Method模式的一种简单的、特殊的实现。他也被称为静态工厂模式,通常创建者的创建方法被设计为static方便调用。 class Person(object): def __init__(self,name): self.name=name def work(self,axe_type): print(self.name + "开始工作了") #person完成work,需要使用一把斧头 #在原始社会,人需要一把石斧 # aux = StoneAxe("花岗岩斧头") # aux.cut_tree() #使用钢铁的斧头 # axe = SteelAxe("加爵斧头") #已经有工厂 axe = Factory.create_axe(axe_type) axe.cut_tree() class Axe(object): def __init__(self,name): self.name=name def cut_tree(self): print("%s斧头开始砍树"%self.name) class StoneAxe(Axe): def cut_tree(self): print("使用石头做的斧头砍树") class SteelAxe(Axe): def cut_tree(self): print("使用钢铁做的斧头砍树") #工厂类 class Factory(object): # 生产斧头,根据用户指定的类型来生产 @staticmethod def create_axe(type): if type=="stone": return StoneAxe("花岗岩斧头") elif type=="steel": return SteelAxe("加爵斧头") else: print("传入的类型不对") # p = Person("原始人") # p.work() p = Person("现代人") p.work("stone")
全局函数模式:
#全局函数 class Person(object): def __init__(self,name): self.name=name def work(self,axe_type): print(self.name + "开始工作了") #person完成work,需要使用一把斧头 #在原始社会,人需要一把石斧 # aux = StoneAxe("花岗岩斧头") # aux.cut_tree() #使用钢铁的斧头 # axe = SteelAxe("加爵斧头") axe=create_axe(axe_type) axe.cut_tree() class Axe(object): def __init__(self,name): self.name=name def cut_tree(self): print("%s斧头开始砍树"%self.name) class StoneAxe(Axe): def cut_tree(self): print("使用石头做的斧头砍树") class SteelAxe(Axe): def cut_tree(self): print("使用钢铁做的斧头砍树") #全局函数-替代了之前的工厂类 # 生产斧头,根据用户指定的类型来生产 def create_axe(type): if type=="stone": return StoneAxe("花岗岩斧头") elif type=="steel": return SteelAxe("加爵斧头") else: print("传入的类型不对") # p = Person("原始人") # p.work() p = Person("现代人") p.work("stone")
工厂方法模式
#简单工厂模式:Simple Factory模式不是独立存在的设计模式,他是Factory Method模式的一种简单的、特殊的实现。他也被称为静态工厂模式,通常创建者的创建方法被设计为static方便调用。 class Person(object): def __init__(self,name): self.name=name def work(self): print(self.name + "开始工作了") #person完成work,需要使用一把斧头 #在原始社会,人需要一把石斧 # aux = StoneAxe("花岗岩斧头") # aux.cut_tree() #使用钢铁的斧头 # axe = SteelAxe("加爵斧头") #已经有工厂,person去找工厂生产一把斧头 factory=Stone_Axe_Factory() axe = factory.create_axe() axe.cut_tree() class Axe(object): def __init__(self,name): self.name=name def cut_tree(self): print("%s斧头开始砍树"%self.name) class StoneAxe(Axe): def cut_tree(self): print("使用石头做的斧头砍树") class SteelAxe(Axe): def cut_tree(self): print("使用钢铁做的斧头砍树") #工厂类 class Factory(object): # 生产斧头,根据用户指定的类型来生产 def create_axe(self): pass class Stone_Axe_Factory(Factory): def create_axe(self): return StoneAxe("花岗岩斧头") class Steel_Axe_Factory(Factory): def create_axe(self): return SteelAxe("钢铁斧头") # p = Person("原始人") # p.work() p = Person("现代人") p.work() ''' 工厂方法模式去掉了简单工厂模式中工厂方法的静态方法,使得它可以被子类继承。对于python来说,就是工厂类被具体工厂继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。 抽象的工厂类提供了一个创建对象的方法,也叫做工厂方法。 1、抽象的工厂角色(Factory):这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。 2、具体工厂角色(Stone_Axe_Factory,Steel_Axe_Factory):它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。 3、抽象产品角色(Axe):它是具体产品继承的父类或者是实现的接口。在python中抽象产品一般为父类。 4、具体产品角色(Stone_Axe,Steel_Axe):具体工厂角色所创建的对象就是此角色的实例。由一个具体类实现。 '''
工厂方法模式去掉了简单工厂模式中工厂方法的静态方法,使得它可以被子类继承。对于python来说,就是工厂类被具体工厂继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
异常捕获
# 异常捕获 try: print(a) i=1/0 #第4行没有执行 except (NameError,ZeroDivisionError) as ex: # 要捕获的异常并赋值给ex,ex代表刚刚捕获的异常对象 print("出现异常了") #捕获到异常之后不会回头继续执行之前的代码 print(ex)
# 异常基本写法 a="123" f=open("text.txt","w") try: f.write("hellio\n") # f.write("world %d"%a) except Exception as ex: #Exception 捕获所有异常。 print(ex) else: # 没有异常的情况会自动执行的代码 print("else") finally: # 最终要执行的代码,不管前面是否出现exception print("finally") f.close()
# 嵌套异常捕获 a="123" f=None try: f=open("text2.txt") try: content=f.read() content.index("hadoop") # except ValueError as ex: except Exception as ex: print(ex) finally: print("最里层的finally") except FileNotFoundError as ex: print(ex) else: # 没有异常的情况会自动执行的代码 print("else") finally: # 最终要执行的代码,不管前面是否出现exception print("finally") if f: f.close()
# 异常传递 a="123" f=None try: f=open("text.txt") try: content=f.read() content.index("hello") i = 1/0 except ValueError as ex: # except Exception as ex: #捕获所有异常类型 print(ex) finally: print("最里层的finally") except (FileNotFoundError,ZeroDivisionError) as ex: print(ex) print("外面的try捕获异常") else: # 没有异常的情况会自动执行的代码 print("else") finally: # 最终要执行的代码,不管前面是否出现exception print("finally") if f: f.close()
# 自定义异常 class PasswordException(Exception): def __init__(self,pw,min_length): self.password=pw self.min_length=min_length def __str__(self): return "%s密码错误,密码的最小长度为%s"%(self.password,self.min_length) def reg(username,password): if len(password)<6: raise PasswordException(password,6) #抛出你指定的异常 else: print("用户名为:%s,密码为:%s"%(username,password)) try: reg("zs","1234") except Exception as ex: #两个except 会按照顺序先执行第一,如果第一个满足异常类型条件,进入第一excep。不会进入后面的except。 print("第一个except") print(ex) except PasswordException as ex: print("第二个except") print(ex)
# 模块 #写一个工具方法,判断字符是否为null,当字符串为None、''、' '为null def isnull(str): if not str: return True elif str.strip()=='': return True else: return False def test1(): print("-----------test1-------------")
#使用自己制作的模块 # import Test38_2_1 as t38_2_1 from Test38_2_1 import isnull,test1 ''' from module1 import * from module2 import * #如果两个模块中方法名字相同,则在后面引入的覆盖前面引入的 ''' a='22' # print(t38_2_1.isnull(a)) print(isnull(a)) test1()
包引入
# P.S.1:在python2下,包里面必须有一个__init__.py的文件,否则无法正常引入,这个文件可以没有内容。 # import my_package.Test38_2_1 from my_package import Test38_2_1 # my_package.Test38_2_1.test1() Test38_2_1.test1() # 有__init__.py文件在python3中没有错,为了兼容python2、python3,包的目录下必须新建一个init文件
模块的发布和安装
# 模块的发布和安装 # setup.py from distutils.core import setup setup(name="压缩包的名字",version="版本",description="描述",author="作者",author="邮箱",py_modules=['包名1.模块名1','包名1.模块名2','包名2.模块名1']) # 进到这个包里执行 python3 setup.py build 构建模块 # 生成发布压缩包 python3 setup.py sdist # 安装 # 1、找到模块的压缩包(拷贝到其他地方) # 2、解压 # 3、进入文件夹 # 4、执行命令:python3 setup.py install(如果在install的时候,执行目录安装,可以使用python3 setup.py install --prefix=安装路径)
给程序传参
# 给程序传参数 import sys print(sys.argv) print(sys.argv[1])
列表推导式&&set集合
# 列表推导式 #所谓的列表推导式,就是指的轻量级循环创建列表 a=[ i for i in range(1,10)] a=[ (x,y) for x in range(1,4) for y in range(1,5) ] #生成一个[[1,2,3],[4,5,6],[7,8,9]....]的列表,最大值在100以内 a=[1,2,3] b=[ [a[0]+i,a[1]+i,a[2]+i] for i in range(0,98) if i%3==0 ] b=[ [1+i,2+i,3+i] for i in range(0,98) if i%3==0 ] #set:集合类型 ''' 列表(list) a=[] 按照先后顺序,有下标位[index],元素可以重复,可变类型(增删改查) 元组(tuple) a=() 按照先后顺序,有下标位[index],元素可以重复,不可变类型(只能查) 字典(dict) a={key:value} 没有先后顺序,没有下标,key不可重复,value可以重复,可变类型 集合(set) a=set() \\a={} 没有先后顺序,没有下标,元素不可重复,可变类型 ''' # 集合 a={1,2,3,4,5} a.add(6) #a={1,2,3,4,5,6} a.add(6) #a={1,2,3,4,5,6} 不可重复 a={1,2,2,1,2,2,1,1,1,2,2,2,1,1,} #a={1,2} # 相互转换 l=list(a) s=set(l) t=tuple(s)
#去重 a=[1,2,3,4,5,6,1,2,3,4] #可以使用set去重 def change_list(li): temp=[] for i in li: if i not in temp: temp.append(i) return temp if __name__=="__main__": print(change_list(a))
#写出一个函数,给定参赛n,生成n个元素值为1~n的列表,元素顺序随机,但值不重复 import random def create_list(n): temp=[] while True: if len(temp)==n: break i=random.randint(1,n) if i not in temp: temp.append(i) return temp if __name__=="__main__": print(create_list(8))
#在不使用第三个变量情况下交换两个变量值 a=10 b=5 a = a+b #取两个数的和 b = a-b #然后a-b等于a然后赋值给b a = a-b #然后a-b等于b然后赋值给a,完成值的交换 #或者 a,b = b,a
def extendList(val,list=[]): list.append(val) return list list1 = extendList(10) list2 = extendList(123,['a','b','c']) list3 = extendList('a') print("list1=%s"%list1) #[10,a] print("list2=%s"%list2) #['a','b','c',123] print("list3=%s"%list3) #[10,a] #第一次调用extendList方法和第三次调用的时候,两次调用list形参指向的内存地址是一样的
坦克大战
# coding=utf-8 # Version:python3.6.1 __date__ = '2018/9/20 18:51' __author__ = 'Lgsp_Harold' import pygame, sys, time from random import randint from pygame.locals import * # 坦克大战主窗口 class TankMain(object): width = 600 height = 500 my_tank_missile_list = [] # 我方子弹列表 my_tank = None # enemy_list = [] # 敌方坦克列表 wall=None enemy_list = pygame.sprite.Group() # 敌方坦克的组 explode_list = [] enemy_missile_list=pygame.sprite.Group() # 开始游戏的方法 def startGame(self): pygame.init() # pygame模块初始化,加载系统的资源 # 创建一个窗口,窗口大小(宽,高)、窗口的特性(0,RESIZEBLE,RULLscreem) screem = pygame.display.set_mode((TankMain.width, TankMain.height), 0, 32) pygame.display.set_caption("坦克大战") # 创建一堵墙 TankMain.wall = Wall(screem,65,160,30,120) # my_tank=My_Tank(screem) # 创建一个我方坦克,坦克显示在屏幕的中下部位置 TankMain.my_tank = My_Tank(screem) # 创建一个我方坦克,坦克显示在屏幕的中下部位置 if len(TankMain.enemy_list)==0: # enemy_list=[] for i in range(1, 6): # 游戏开始时初始化5个敌方坦克 # TankMain.enemy_list.append(Enemy_Tank(screem)) TankMain.enemy_list.add(Enemy_Tank(screem)) # 把敌方坦克放到组里面 while True: if len(TankMain.enemy_list) < 5: TankMain.enemy_list.add(Enemy_Tank(screem)) # 把敌方坦克放到组里面 # color RGB(0,0,0) # 设置窗口屏幕背景颜色 screem.fill((0, 0, 0)) # 面向过程的写法 # pygame.draw.rect(screem,(0,255,0),Rect(400,30,100,30),5) # 显示左上角的文字 for i, text in enumerate(self.write_text(), 0): screem.blit(text, (0, 5 + (15 * i))) # 显示游戏中的墙,并且对墙和其他对象进行碰撞检测 TankMain.wall.display() TankMain.wall.hit_tank() self.get_event(TankMain.my_tank,screem) # 获取事件,根据获取事件进行处理 if TankMain.my_tank: TankMain.my_tank.hit_enemy_missile() # 我方坦克和敌方的炮弹碰撞检测 if TankMain.my_tank and TankMain.my_tank.live: TankMain.my_tank.display() # 在屏幕上显示我方坦克 TankMain.my_tank.move() # 在屏幕上移动的我方坦克 else: # del(TankMain.my_tank) # TankMain.my_tank=None # print("Game Over") # sys.exit() TankMain.my_tank=None # 显示和随机移动所有的敌方坦克 for enemy in TankMain.enemy_list: enemy.display() enemy.random_move() enemy.random_fire() # 显示所有的我方炮弹 for m in TankMain.my_tank_missile_list: if m.live: m.display() m.hit_tank() # 炮弹打中敌方坦克 m.move() else: TankMain.my_tank_missile_list.remove(m) # 显示所有的敌方炮弹 for m in TankMain.enemy_missile_list: if m.live: m.display() m.move() else: TankMain.enemy_missile_list.remove(m) for explode in TankMain.explode_list: explode.display() # 显示重置 time.sleep(0.05) # 每次休眠0.05秒跳到下一帧 pygame.display.update() # 获取所有的事件(敲击键盘,鼠标点击事件) def get_event(self, my_tank,screem): for event in pygame.event.get(): if event.type == QUIT: # 程序退出 self.stopGame() if event.type == KEYDOWN and (not my_tank) and event.key == K_n: TankMain.my_tank = My_Tank(screem) if event.type == KEYDOWN and my_tank: if event.key == K_LEFT or event.key == K_a: my_tank.direction = "L" my_tank.stop = False # my_tank.move() if event.key == K_RIGHT or event.key == K_d: my_tank.direction = "R" my_tank.stop = False # my_tank.move() if event.key == K_UP or event.key == K_w: my_tank.direction = "U" my_tank.stop = False # my_tank.move() if event.key == K_DOWN or event.key == K_s: my_tank.direction = "D" my_tank.stop = False # my_tank.move() if event.key == K_ESCAPE: self.stopGame() if event.key == K_SPACE: m = my_tank.fire() m.good = True # 我方坦克发射的炮弹,好炮弹 TankMain.my_tank_missile_list.append(m) if event.type == KEYUP and my_tank: if event.key == K_LEFT or K_RIGHT or K_UP or K_DOWN: my_tank.stop = True if event.type == MOUSEBUTTONUP: pass # 关闭游戏 def stopGame(self): sys.exit() # 在游戏窗口内左上角显示文字内容 def write_text(self): font = pygame.font.SysFont("方正兰亭超细黑简体", 16) # 定义一个字体 # 文字,抗锯齿,字体颜色 text_sf1 = font.render("敌方坦克数量为:%d" % len(TankMain.enemy_list), True, (255, 0, 0)) # 根据字体创建一个文件的图像 text_sf2 = font.render("我方坦克炮弹数量为:%d" % len(TankMain.my_tank_missile_list), True, (255, 0, 0)) # 根据字体创建一个文件的图像 return text_sf1, text_sf2 # 坦克大战游戏中所有对象的父类 class BaseItem(pygame.sprite.Sprite): def __init__(self, screem): pygame.sprite.Sprite.__init__(self) # 所有对象共享的属性 self.screem = screem # 把坦克对应图片显示在游戏窗口上 # 在游戏屏幕中显示当前游戏的对象 def display(self): if self.live: self.image = self.images[self.direction] self.screem.blit(self.image, self.rect) # 把图片画在屏幕上 # 坦克公共父类 class Tank(BaseItem): # 定义类属性,所有坦克对象高和宽都是一样 width = 50 height = 50 def __init__(self, screem, left, top): super().__init__(screem) # self.screem=screem # 坦克在移动或者显示过程中需要用到当前游戏的屏幕 self.direction = 'D' # 坦克的方向,默认方向向下(上下左右) self.speed = 5 # 坦克移动的速度 self.stop = False self.images = {} # 坦克的所有图片,key:方向,value:图片路径(surface) self.images['L'] = pygame.image.load("../img/p1tankL.gif") self.images['R'] = pygame.image.load("../img/p1tankR.gif") self.images['U'] = pygame.image.load("../img/p1tankU.gif") self.images['D'] = pygame.image.load("../img/p1tankD.gif") self.image = self.images[self.direction] # 坦克的图片由方向决定 self.rect = self.image.get_rect() self.rect.left = left self.rect.top = top self.live = True # 决定坦克是否消灭了 self.oldTop=self.rect.top self.oldLeft=self.rect.left def stay(self): self.rect.left=self.oldLeft self.rect.top=self.oldTop # 坦克移动方法 def move(self): if not self.stop: # 如果坦克不是停止状态 self.oldLeft=self.rect.left self.oldTop=self.rect.top if self.direction == "L": # 如果坦克的方向向左,那么只需要改坦克的left就可以了,left在减少 if self.rect.left > 0: # 判断坦克是否在左边的边界上 self.rect.left -= self.speed else: self.rect.left = 0 elif self.direction == "R": if self.rect.right < TankMain.width: self.rect.right += self.speed else: self.rect.right = TankMain.width elif self.direction == "U": if self.rect.top > 0: self.rect.top -= self.speed else: self.rect.top = 0 elif self.direction == "D": if self.rect.bottom < TankMain.height: self.rect.bottom += self.speed else: self.rect.bottom = TankMain.height def fire(self): m = Missile(self.screem, self) return m # 我方坦克类 class My_Tank(Tank): def __init__(self, screem): super().__init__(screem, 275, 400) # 创建一个我方坦克,坦克显示在屏幕的中下部位置 self.stop = True self.live = True def hit_enemy_missile(self): hit_list=pygame.sprite.spritecollide(self,TankMain.enemy_missile_list,False) for m in hit_list: # 我方坦克中弹 m.live=False TankMain.enemy_missile_list.remove(m) self.live=False explode = Explode(self.screem,self.rect) TankMain.explode_list.append(explode) # 敌方坦克类 class Enemy_Tank(Tank): def __init__(self, screem): super().__init__(screem, randint(1, 5) * 100, 200) self.speed = 3 self.step = 12 # 坦克按照某个方向移动的步数 self.get_random_direction() def get_random_direction(self): r = randint(0, 4) # 得到一个坦克移动方向和停止的随机数 if r == 4: self.stop = True elif r == 2: self.direction = "L" self.stop = False elif r == 0: self.direction = "R" self.stop = False elif r == 1: self.direction = "U" self.stop = False elif r == 3: self.direction = "D" self.stop = False # 敌方坦克按照一个确定的随机方向,连续移动12步,然后再次改变方向 def random_move(self): if self.live: if self.step == 0: self.get_random_direction() self.step = 12 else: self.move() self.step -= 1 #随机开火 def random_fire(self): r=randint(0,50) if r==10: m=self.fire() TankMain.enemy_missile_list.add(m) else: return # 炮弹类 class Missile(BaseItem): width = 5 height = 5 def __init__(self, screem, tank): super().__init__(screem) self.tank = tank self.direction = tank.direction # 炮弹的方向由所发射的坦克方向决定 self.speed = 12 # 炮弹移动的速度 enemymissileImg = pygame.image.load("../img/enemymissile.gif") self.images = {} # 炮弹的所有图片,key:方向,value:图片路径(surface) self.images['L'] = enemymissileImg self.images['R'] = enemymissileImg self.images['U'] = enemymissileImg self.images['D'] = enemymissileImg self.image = self.images[self.direction] # 坦克的图片由方向决定 self.rect = self.image.get_rect() # 炮弹的边界 # 炮弹坐标 self.rect.left = tank.rect.left + (tank.width - self.width) / 2 self.rect.top = tank.rect.top + (tank.height - self.height) / 2 self.live = True # 决定炮弹是否消灭了 self.good = False def move(self): if self.live: # 如果炮弹还存在 if self.direction == "L": # 如果坦克的方向向左,那么只需要改坦克的left就可以了,left在减少 if self.rect.left > 0: # 判断坦克是否在左边的边界上 self.rect.left -= self.speed else: self.live = False elif self.direction == "R": if self.rect.right < TankMain.width: self.rect.right += self.speed else: self.live = False elif self.direction == "U": if self.rect.top > 0: self.rect.top -= self.speed else: self.live = False elif self.direction == "D": if self.rect.bottom < TankMain.height: self.rect.bottom += self.speed else: self.live = False # 炮弹击中坦克,第一种我方炮弹击中敌方坦克;第二种敌方炮弹击中我方坦克 def hit_tank(self): if self.good: # 如果炮弹是我方的炮弹 hit_list = pygame.sprite.spritecollide(self,TankMain.enemy_list,False) for e in hit_list: e.live=False TankMain.enemy_list.remove(e) # 如果敌方坦克被击中,从列表中删除敌方坦克 self.live=False expload = Explode(self.screem,e.rect) # 产生一个爆炸对象 TankMain.explode_list.append(expload) # 爆炸类 class Explode(BaseItem): def __init__(self, screem, rect): super().__init__(screem) self.live = True self.images = [pygame.image.load("../img/blast1.gif"),\ pygame.image.load("../img/blast2.gif"),\ pygame.image.load("../img/blast3.gif"),\ pygame.image.load("../img/blast4.gif"),\ pygame.image.load("../img/blast5.gif"),\ pygame.image.load("../img/blast6.gif"),\ pygame.image.load("../img/blast7.gif"),\ pygame.image.load("../img/blast8.gif")] self.step = 0 self.rect = rect # 爆炸的位置和发生爆炸前,炮弹碰到的坦克位置一样。在构建爆炸的时候把坦克的rect传递进来。 # display方法在整个游戏运行过程中,循环调用,每隔0.1秒调用一次 def display(self): if self.live: if self.step == len(self.images): # 最后一张爆炸图片已经显示 self.live = False else: self.image = self.images[self.step] self.screem.blit(self.image, self.rect) self.step+=1 else: pass # 删除该对象 # 游戏中的墙 class Wall(BaseItem): def __init__(self,screem,left,top,width,height): super().__init__(screem) self.rect=Rect(left,top,width,height) # self.left=left # self.top=top # self.width=width # self.height=height self.color=(255,0,0) def display(self): self.screem.fill(self.color,self.rect) #针对墙和其他坦克或者炮弹的碰撞检测 def hit_tank(self): if TankMain.my_tank: is_hit=pygame.sprite.collide_rect(self,TankMain.my_tank) if is_hit: TankMain.my_tank.stop=True TankMain.my_tank.stay() if len(TankMain.enemy_list)!=0: hit_list=pygame.sprite.spritecollide(self,TankMain.enemy_list,False) for e in hit_list: e.stop=True e.stay() # 敌方炮弹击中墙,子弹消失 if TankMain.enemy_missile_list: enemy_missile_hit_list=pygame.sprite.spritecollide(self,TankMain.enemy_missile_list,False) for e in enemy_missile_hit_list: e.stop=True e.live=False # 我方炮弹击中墙,子弹消失 if TankMain.my_tank_missile_list: my_tank_missile_hit_list=pygame.sprite.spritecollide(self,TankMain.my_tank_missile_list,False) for e in my_tank_missile_hit_list: e.stop=True e.live=False tankMain = TankMain() tankMain.startGame() if __name__ == '__main__': pass
查看模块搜索路径 import sys sys.path 添加搜索路径 sys.path.append('/home/sxt/xxx') sys.path.insert(0,'/home/sxt/xxx') # 可以确保先搜索这个路径 重新导入模块 from imp import * reload(模块名) 查看python所有的modules:help("modules") help(sys) dir(sys) 循环导入 #把import语句放置在函数中 例子:一个系统由两部分组成,包括A主模块和B模块。 其中A模块包含a1()和a2(),B模块包含b1(),并且在b1()中会用到a1()。 # A.py from b1 import B def a1(): print("enter a1") def a2(): print("-------Enter a2-------") b1() print("++++++++Exit a2+++++++") control() # B.py import A def b1(): print("enter B calling a1") A.a1() 执行A.py 从Traceback看,两个模块互相导入,造成导入循环错误。 方案:把B.py中的导入模块语句放在函数中 # B.py def b1(): import A print("enter B calling ctlCommon") A.a1() is和== is:是比较两个引用是否指向了同一个对象(引用比较) ==:是比较两个对象是否相等(值比较) 例子: a=[1,2,3] b=a a==b => True a is b => True c=a[:] c => [1,2,3] a==c => True a is c => False a=100 b=100 a==b => True a is b => True c=1000 d=1000 c==d => True c is d => False ''' Integer a=100实际在内部做了 Integer a = Integer.valueOf(100)的操作。 看Integer.class源码 这个方法的首先断言了IntegerCache.high的值大于等于127(关于这里assert 大于等于127解释请看补充),否则退出方法。 接着if条件内i需要在low值和high值之间。 可以看到low为-128,即if条件需要i在-128和127之间,那么返回i+128作为整型数组 cache的下标,用来放在缓存中。这样也就是说任意一个相同数值的Integer的数,如果在-128和127之间,那么它们之间的内存地址是相同的。 这也就解释了为什么Integer a=100,b=100时候a==b返回true。 而如果if条件不满足则返回new Integer(i)。 此时不放入缓存,也就是说不在-128和127范围内的数它们的内存地址是不相同的,这也就解释了为什么Integer c=1000,d=1000时候c==d返回false。 补充: 补充说明assert断言IntegerCache.high的值大于等于127才执行该方法。 看源码可以知道high本身没有值,是h=127赋给它的,而h的值在这之前可能会改变。也就是说缓存上限的127其实是可以改变,可以从VM这个java.lang.Integer.IntegerCache.high中读取为上限,但是需要满足这个上限的值确实在-128和127之间。也就是说缓存的容量其实是可以改变的。基于这个特点,如果缓存的容量在-128到127之间,那么我们为了使用内存数据的高效性,可以将范围内大小的Integer放在同一缓存数组中,而不在范围内的则不放在缓存数组中,这也就是为什么下面的ValueOf方法中需要断言assert high值>=127的原因。 ''' 浅拷贝:是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容 深拷贝:是对于一个对象所有层次的拷贝(递归) import copy ——copy.deepcopy()#深拷贝 ——copy.copy()#浅拷贝 浅拷贝对不可变类型和可变类型的copy不同(例如=列表和元组) 分片表达式可以赋值一个序列 字典的copy方法可以拷贝一个字典 有些内置函数可以生成拷贝(list) a=[1,2,3] b=[4,5,6] c=[a,b] c => [[1,2,3],[4,5,6]] d=c d => [[1,2,3],[4,5,6]] a.append(7) c => [[1,2,3,7],[4,5,6]] d => [[1,2,3,7],[4,5,6]] import copy e=copy.deepcopy(c) e => [[1,2,3,7],[4,5,6]] b.append(8) c => [[1,2,3,7],[4,5,6,8]] e => [[1,2,3,7],[4,5,6]] f=copy.copy(c) f => [[1,2,3,7],[4,5,6,8]] a.append(9) c => [[1,2,3,7,9],[4,5,6,8]] f => [[1,2,3,7,9],[4,5,6,8]] 类的数据隐藏(数据私有化) XX:公有变量 _X:单前置下划线:私有化属性或方法 __XX:双前置下划线:避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到) __XX__:双前后下划线:用户名字空间的魔法对象或属性。例如:__init__,__不要自己发明这样的名字。 XX__:单后置下划线:用于避免与Python关键词的冲突 通过name mangling(名字重整)目的就是以防子类意外重写基类的方法或者属性。如:_Class__object机制就可以访问private了 class Test(object): def __init__(self): self.__num=100 t = Test() print(t.__num) 为私有属性添加getter和setter方法 class Test(object): def __init__(self): self.__num=100 def getNum(self): return self.__num def setNum(self,num): if num<100: self.__num=num t = Test() t.setNum(20) print(t.getNum()) class Test(object): def __init__(self): self.__num=100 def getNum(self): return self.__num def setNum(self,num): if num<100: self.__num=num num=property(getNum,setNum) t = Test() t.num(20) print(t.num()) property用法 使用property升级getter和setter方法 num=property(getNum,setNum) 使用property取代getter和setter方法 @property @num.setter class Test(object): def __init__(self): self.__num=100 @property def num(self): return self.__num @num.setter def num(self,num): if num<100: self.__num=num t = Test() t.num(20) print(t.num()) 进制、位运算 有符号数和无符号数的概念 原码、反码、补码 ——规则: 正数:原码=反码=补码 负数:反码=符号位不变,其他位取反 补码=反码+1 1的原码:0000 0000 0000 0001 -1的原码:1000 0000 0000 0001 -1的反码:1111 1111 1111 1110 -1的补码:1111 1111 1111 1111 -负数的补码转换原码的规则: 原码=补码的符号位不变 —> 数据位取反 —> 尾+1 -1的补码:1111 1111 1111 1111 取反:1000 0000 0000 0000 -1的原码:1000 0000 0000 0001 口诀:除2取余,按位取反 十六进制hex() 十进制int() 八进制oct() 二进制bin() 字节byte=8位bit 1024byte=1kb 1024kb=1mb 1024mb=1gb 1024gb=1tb 有符号的数:最高位代表符号位 1负数 0正数
位运算
位运算 &按位与 |按位或 ^按位异或 ~按位取反 <<按位左移 >>按位右移 用途:直接操作二进制,省内存,效率高 例: 5 0000 0101 左移: 000 01010 10 00 010100 20 num << n ==> num*2n 右移: 00000 010 2 000000 01 1 num >> n ==> num/2n (整除) num=5 num<<1 => 10 num<<2 => 20 num>>1 => 2 num>>2 => 1 位与 5 0000 0101 3 0000 0011 1 0000 0001 位或 5 0000 0101 3 0000 0011 7 0000 0111 异或 5 0000 0101 3 0000 0011 6 0000 0110 取反 5 0000 0101 -5 1111 1010
生成器 第一种方式 在Python中,一边循环一边计算的机制,称为生成器:generator 创建生成器:G=(x for x in range(5)) 可以通过next()函数获得生成器的下一个返回值 没有更多的元素时,抛出Stoplteration的异常 生成器也可以使用for循环,因为生成器也是可迭代对象 第二种方式 def fib(times): #斐波拉契数列 n=0 a,b=0,1 while n<times: yield b #后面的语句先不执行 a,b=b,a+b #交换 n+=1 return 'done' 使用__next__()方法 使用send()方法 ——next()等价于send(None) def gen(): i=0 while i<5: temp=yield i print(temp) i+=1 生成器的特点: 1、节约内存 2、迭代到下一次的调用时,所使用的参数都是第一次所保留下来的。
def fib(times): n=0 a,b=0,1 while n<times: print(b) a,b=b,a+b n=n+1 return "done" fib(5)
def fib(times): n=0 a,b=0,1 while n<times: yield b a,b=b,a+b n=n+1 return "done" g = fib(5) next(g) for x in g: print(x) ''' 0,1 1,1 1,2 2,3 3,5 '''
def fib(times): n=0 a,b=0,1 while n<times: yield b a,b=b,a+b n=n+1 return "done" i=0 m=5 z=10 g1 = fib(m) #next(g1) while i < m: print(g1.__next__()) i+=1 g2 = fib(z) g2.send(None) g2.send("lgsp_Harold")
def gen(times): n=0 while n < times: temp = yield n*2 #temp用于接收传过来的参数 print(temp) n=n+1 return "done"
g = fib(5)
g.send(None)
g.send("lgsp_Harold")
迭代器 迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器只能往前不会后退。 可选迭代对象(Iterable) ——集合数据类型,如list、tuple、dict、set、str等 ——生成器带yield的generator function 如何判断对象可迭代? ——from collections import Iterable ——isinstance([],Iterator) 迭代器(Iterator):可以被next()函数调用并不断返回下一个值的对象称为迭代器 ——from collections import Iterable ——isinstance((x for x in range(10)),Iterator) ——iter()函数:将可迭代对象转换成迭代器
命名空间(namespace) ——防止命名冲突 全局变量和局部变量 ——函数内的变量叫局部变量 ——函数外的变量叫全局变量 ——globals()和locals() ——局部变量和全局变量的冲突(LEGB原则) locals -> enclosing function(闭包) -> globals -> builtins(内建) 查看内建模块dir(__builtin__)
闭包 内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包 #定义一个函数 def test(number): #在函数内部再定义一个函数,并且这个函数用到了外边函数的变量 def test_in(number_in): print("in test_in 函数,number_in is %d" %number_in) return number+number_in #其实这里返回的就是闭包的结果 return test_in 闭包的应用 def line_conf(a,b): def line(x): return a*x+b return line line1=line_conf(1,1) line2=line_conf(4,5) print(line1(5)) print(line2(5))
def fun3(): def fun4(): print("fun4") return fun4 ret=fun3() ret()
def outter(num): def inner(num_in): print("num_in is %d"%num_in) return num+num_in return inner #outter(10)(20) fun=outter(10) fun(20)
装饰器 装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数 装饰器有2个特性: 1、可以把被装饰的函数替换成其他函数 2、可以在加载模块的时候立即执行 def w1(func): def inner(): #验证1 #验证2 #验证3 func() return inner @w1 def f1(): print("f1") 装饰器(decorator)功能 1、引入日志 2、函数执行时间统计 3、执行函数前预备处理 4、执行函数后清理功能 5、权限校验等场景 6、缓存
def doca(func): def wrapper(): print(func.__name__) func() return wrapper def fun1(): print("----1----") ret=doca(fun1) ret() @doca def fun2(): print("----2----") fun2()
#装饰器例子: #定义函数:完成包裹数据 def makeBold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped #定义函数:完成包裹数据 def makeItalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makeBold def test1(): return "hello world-1" @makeItalic def test2(): return "hello world-2" @makeBold @makeItalic def test3(): return "hello world-3" print(test1()) print(test2()) print(test3())
#装饰器对有参函数进行装饰 from time import ctime,sleep def timefun(func): def wrappedfunc(a,b): print("%s called at %s"%(func.__name__,ctime())) print(a,b) func(a,b) return wrappedfunc @timefun def foo(a,b): print(a+b) foo(3,5) sleep(2) foo(2,4) #装饰器对无参函数进行装饰 from time import ctime,sleep def timefun(func) def wrappedfunc(): print("%s called at %s"%(func.__name__,ctime())) func() return wrappedfunc @timefun def foo(): print("I am foo") foo() sleep(2) foo() #装饰器对不定长参数函数进行装饰 from time import ctime,sleep def timefun(func): def wrappedfunc(*args, **kwargs): print("%s call at %s"%(func.__name__,ctime())) func(*args, **kwargs) return wrappedfunc @timefun def foo(a,b,c): print(a+b+c) foo(3,5,7) sleep(2) foo(2,4,9) #装饰器对带有返回值的函数进行装饰 from time import ctime,sleep def timefun(func): def wrappedfunc(): print("%s called at %s"%(func.__name__, ctime)) ret=func() return ret return wrappedfunc @timefun def foo(): print("I am foo") @timefun def getInfo(): return "----lgsp_Harold----" #通用装饰器 # 不定长参数、带有返回值
class Person(): def __init__(self,name,age): self.name=name self.age=age p1 = Person("Harold","44") p2 = Person("Leslie","19") Person.addr="北京" # 为对象动态添加属性 p1.addr p2.addr # 实例方法 def run(self,speed): print("%s在移动,速度%d KM/H"%(self.name,self.speed)) import types # 为对象添加实例方法 p1.run = types.MethodType(run,p1) p1.run(180) # 类方法 @classmethod def fun1(cls): print("ClassMethod") Person.fun1=fun1 # 为类添加类方法 p1.fun1() # 静态方法 @staticmethod def fun2(a,b): return a+b Person.fun2=fun2 print(p1.fun2(2,3)) print(p2.fun2(2,3)) # 限制修改对象的属性 class Student(object): __slots__=("name","age") stu = Student() stu.name="Harold" stu.age=22 stu.score=222 【 Traceback(most recent call last): AttributeError:Student object has no attribute 'score' p.s.:__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的 】 # 类装饰器 装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。 一般callable对象都是函数,但也有例外。只要某个对象重写了__call__()方法,那么这个对象就是callable的 class Test(): def __call__(self): print("call me") t = Test() t() # call me class Test(object): def __init__(self,func): print("---初始化---") print("func name is %s"%func.__name__) self.__func = func def __call__(self): print("装饰器中的功能") self.__func() @Test # 生成Test的一个对象,所以会调用__init__方法,并且把下面装饰的函数作为参数传进去 def test(): print("test") test() # 如果把这句注释,重新运行程序,依然会看到“---初始化---”;调用Test的一个对象的__call__方法 对象池 python为了优化速度,使用了小整数[-5,257)对象池,避免为整数频繁申请和销毁内存空间 同理,单个字符也提供对象池,常驻内存 每一个大整数,均创建一个新的对象 对于字符串,单个单词,不可修改,默认开启intern机制,采用引用计数机制共用对象,引用计数为0则销毁 垃圾回收GC Garbage collection(垃圾回收) ——为新生成的对象分配内存 ——识别那些垃圾对象 ——从垃圾对象那回收内存 Python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略 Python里每一个东西都是对象,它们的核心就是一个结构体:PyObject C语言 typedef struct_object{ int ob_refcnt; struct_typeobject*ob_type; }PyObject; #define Py_INCREF(op)((op)->ob_refcnt++) //增加计数 #define Py_DECREF(op) \ //减少计数 if(--(op)->ob_refcnt!=0) \ ; \ else \ __Py_Dealloc((PyObject *)(op)) 引用计数 引用计数机制的优点 ——简单 ——实时性:一旦没有引用,内存就直接释放。不像其他机制等到特定时间。实时性还带来一个好处,处理回收内存的时间分摊到了平时。 引用计数机制的缺点 ——维护引用计数消耗资源 ——循环引用 导致引用计数+1的情况 ——对象被创建,例如a=23 ——对象被引用,例如b=a ——对象被作为参数,传入到一个函数中,例如func(a) ——对象作为一个元素,存储在容器中,例如list1=[a,a] 导致引用技术-1的情况 ——对象的别名被显式销毁,例如del a ——对象的别名被赋予新的对象,例如a=24 ——一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会) ——对象所在的容器被销毁,或从容器中删除对象 查看一个对象引用次数 a=1000 import sys sys.getrefcount(a) 引用计数的缺陷是循环引用的问题 import gc class ClassA(): def __init__(self): print("object born,id:%s"%str(hex(id(self)))) def f2(): while True: c1=ClassA() c2=ClassA() c1.t=c2 c2.t=c1 del c1 del c2 #把python的gc关闭 gc.disable() f2() 执行f2(),进程占用的内存会不断增大 有三种情况会触发垃圾回收: 1、调用gc.collect() 2、当gc模块的计数器达到阀值的时候 3、程序退出的时候 垃圾回收后的对象会放在gc.garbage列表里面 gc.get_threshold()获取的gc模块中自动执行垃圾回收的频率 gc.set_threshold(threshold0[,threshold1[,threshold2]])设置自动执行垃圾回收频率 gc.get_count()获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表 gc.collect([generation])显示进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一、二代对象,2代表检查一、二、三代的对象,如果不传参数,执行一个full collection,也就是等于传2。返回不可达(unreachable objects)对象的数目 gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法 内建属性 __init__ 构造初始化函数 创建实例后,赋值时使用,在__new__后 __new__ 生成实例所需属性 创建实例时 __class__ 实例所在类 实例.__class__ __str__ 实例字符串表示,可读性 print(类实例),如没实现,使用repr结果 __repr__ 实例字符串表示,准确性 类实例 回车或者print(repr(类实例)) __del__ 析构 del删除实例 __dict__ 实例自定义属性 vars(实例__dict__) __doc__ 类文档,子类不继承 help(类或实例) __getattribute__ 属性访问拦截器 访问实例属性时 __bases__ 类的所有父类构成元素 类名.__bases__ 属性访问拦截器 class School(object): def __init__(self,subject1): self.subject1=subject1 self.subject2="cpp" # 重写属性访问时拦截器,打log def __getattribute__(self,obj): if obj=="subject1": print("log subject1") return "redirect python" else: # 测试注释掉这2行试试(注意,如果注释后面的两行,其他属性会不能正常访问) return object.__getattribute__(self,obj) s=School("python") print(s.subject1) print(s.subject2) 使用属性访问拦截器的坑 class Person(object): def __getattribute__(self,obj): print("--test--") if obj.startswith("a"): retu "haha" else: return self.test def test(self): print("heihei") t.Person() t.a # 返回haha t.b # 会让程序死掉 dir(__builtins__) range(start,stop[,step])计数从start开始,默认从0开始,到stop结束,但不包括stop;每次跳跃的间距默认为1 map(function,sequence[,sequence,...])根据提供的函数对指定序列做映射 filter(function or None,sequence)对指定序列执行过滤操作 reduce(function, sequence[,initial])对参数序列中元素进行累积 # 函数需要一个参数 map(lambda x: x*x,[1,2,3]) # 结果为:[1,4,9] # 函数需要两个参数 map(lambda x,y: x+y,[1,2,3],[4,5,6]) # 结果为[5,7,9] def f1(x,y): return(x,y) l1=[0,1,2,3,4,5,6] l2=["sun","M","T","W","T","F","S"] l3=map(f1,l1,l2) print(list(l3)) # 结果为:[(0,"sum"),(1,"M"),(2,"T"),(3,"W"),(4,"T"),(5,"F"),(6,"S")] filter 返回一个filter对象,是一个迭代器 f=filter(lambda x: x%2,[1,2,3,4]) for item in f: print(item) f=filter(None, "hello") for item in f: print(item) 在python3里,reduce()函数已经被从全局名字空间里移除了,它现在被放置在functools模块里 ——from functools import reduce reduce(lambda x,y: x+y,[1,2,3,4]) 10 reduce(lambda x,y:x+y[1,2,3,4],5) 15 reduce(lambda x,y:x+y,['aa','bb','cc'],'dd') 'ddaabbcc' 偏函数 functools functools是python2.5被引入的,一些工具函数放在此包里 -import functools dir(functools) partial函数(偏函数) ——把一个函数的某些参数设置默认值,返回一个新的函数,调用这个新的函数会更简单 wraps函数 ——使用装饰器时,被装饰后的函数其实已经是另外一个函数了,(函数名等函数属性会发生改变) ——wraps的装饰器可以消除这样的副作用 import functools def showarg(*args,**kwargs): print(args) print(kwargs) p1=functools.partial(showarg,1,2,3) p1() => (1,2,3) {} p1(4,5,6) => (1,2,3,4,5,6) {} p1(a='python',b='Harold') => (1,2,3) {'a':'python','b':'Harold'} p2=functools.partial(showarg,a=3,b='linux') p2() => () {'a'=3,'b'='linux'} p2(1,2) => (1,2) {'a'=3,'b'='linux'} p2(a='python',b='Harold') => () {'a':'python','b':'Harold'} 消除装饰器的副作用 import functools def note(func): "note function" @functools.wraps(func) # 消除装饰器的副作用 def wrapper(): "wrapper function" print('note something') return func() return wrapper @note def test(): "test function" print("I am test") test() print(test.__doc__) # "test function" 常用标准模块 标准库 说明 builtins 内建函数默认加载 os 操作系统接口 sys Python自身运行环境 functools 常用的工具 json 编码和解码json对象 logging 记录日志,调试 multiprocessing 多进程 threading 多线程 copy 拷贝 time 时间 datetime 日期和时间 calendar 日历 hashlib 加密算法 random 生成随机数 re 字符串正则匹配 socket 标准的BSD Sockets API 常用第三方模块 扩展库 说明 requests 使用的是urllib3,继承urllib2的所有特性 urllib 基于http的高层库 scrapy 爬虫 beautifulsoup4 HTML/XML的解析器 celery 分布式任务调度模块 redis 缓存 Pillow(PIL) 图像处理 xlsxwriter 仅写excle功能,支持xlsx xlwt 仅写excle功能,支持xls,2013或更早版office xlrd 仅读excle功能 elasticsearch 全文搜索引擎 pymysql 数据库连接库 mongoengine/pymongo mongodbpython接口 matplotlib 画图 numpy/scipy 科学计算 django/tornado/flask web框架 xmltodict xml转dict SimpleHTTPServer 简单地HTTP Server,不使用Web框架 gevent 基于协程的Python网络库 fabric 系统管理 pdb调试 执行时调试 python -m pdb some.py 交互调试 import pdb pdb.run("testfun(args)") 程序里埋点 import pdb pdb.set_trace() pdb调试 命令 简写命令 作用 break b 设置断点 continue c 继续执行程序 list l 查看当前行的代码段 step s 进入函数 return r 执行代码直到从当前函数返回 quit q 中止并退出 next n 执行下一行 print p 打印变量值 help h 帮助 args a 查看传入参数 回车 重复上一条命令 break b 显示所有断点 break lineno b lineno 在指定行设置断点 break file:lineno b file:lineno 在指定文件的行设置断点 clear num 删除断点 bt 查看函数调用栈帧
def note(func): def inner(): "inner" # 文档注释 print("note----inner") return func() return inner @note def fun(): "fun" print("I am fun") fun() print(fun.__doc__) #inner # 消除装饰器副作用 import functools def note(func): @functools.wraps(func) # 消除装饰器副作用 def inner(): "inner" # 文档注释 print("note----inner") return func() return inner @note def fun(): "fun" print("I am fun") fun() print(fun.__doc__) #fun
多进程 在Unix/Linux操作系统中,提供一个fork()系统函数,它非常特殊 ——普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后分别在父进程和子进程内返回 ——子进程永远返回0,而父进程返回子进程ID ——一个父进程可以fork出很多子进程,所以父进程要记下每一个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程ID ——父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法 import os import random import time #coding=utf-8 from multiprocessing import Pool, Process pid = os.fork() if pid<0: print("fork()调用失败。") elif pid==0: print("我是子进程(%s),我的父进程是(%s)"%(os.getpid(),os.getppid())) x+=1 else: print("我是父进程(%s),我的子进程是(%s)"%(os.getpid(),pid)) print("父子进程都可以执行这里的代码") 进程不能共享全局变量 num=100 pid = os.fork() if pid<0: print("fork()调用失败。") elif pid==0: time.sleep(2) num+=1 print("我是子进程(%s),我的父进程是(%s),num:%s"%(os.getpid(),os.getppid(),num)) x+=1 else: time.sleep(3) print("我是父进程(%s),我的子进程是(%s),num:%s"%(os.getpid(),pid,num)) print("父子进程都可以执行这里的代码") 多个fork()的问题 pid = os.fork() if pid = 0: print("子进程11") else: print("父进程11") pid = os.fork() if pid = 0: print("子进程22") else: print("父进程22") 通过process创建子进程 由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。 multiprocessing模块就是跨平台版本的多进程模块 multiprocessing模块提供了一个Process类来代表一个进程对象 # 子进程要执行的代码 def run_proc(name): print("子进程运行中,name=%s,pid=%d..."%(name,os.getpid())) if __name__=="__main__": print("父进程%s."%os.getpid()) p=Process(target=run_proc,args=("test",)) print("子进程将要执行") p.start() p.join() print("子进程已结束") 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。 Process([group[,target[,name[,args[,kwargs]]]]]) target:表示这个进程实例所调用对象 args:表示调用对象的位置参数元组 kwargs:表示调用对象的关键字参数字典 name:为当前进程实例的别名 group:大多数情况下用不到 join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步 Process类常用方法: is_alive():判断进程实例是否还在执行 join([timeout]):是否等待进程实例执行结束,或等待多少秒 start():启动进程实例(创建子进程) run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法 terminate():不管任务是否完成,立即终止 Process类常用属性: name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数 pid:当前进程实例的PID值 进程池 当需要创建的子进程数量不够时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,手动去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法 初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行 def worker(msg): t_start=time.time() print("%s开始执行,进程号为%s"%(msg.os,getpid())) # random.random()随机生成0~1之间的浮点数 time.sleep(random.random()*2) t_stop=time.time() print(msg,"执行完毕,耗时%0.2f"%(t_stop-t_start)) pool = Pool(3) # 定义一个进程池,最大进程数3 for i in range(0,10): # pool.apply_async(需调用的目标,(传递给目标的参数元组,)) # 每次循环将会用空闲出来的子进程去调用目标 pool.apply_async(worker,(i,)) print("_________start_________") pool.close() # 关闭进程池,关闭后po不在接收新的请求 pool.join() # 等待po中所有子进程执行完成,必须放在close语句之后, print("__________end____________") 进程池使用细节 multiprocessing.Pool常用函数解析: ——apply_async(func[,args[,kwds]]):使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表; ——apply(func[,args[,kwds]]):使用阻塞方式调用func ——close():关闭Pool,使其不再接受新的任务; ——terminate():不管任务是否完成,立即终止 ——join():主进程阻塞,等待子进程的退出,必须在close()或terminate()之后使用 进程池中的消息队列 下面的实例代码吧。
# 通过Process创建子进程 from multiprocessing import Process import os import time def fun(name): time.sleep(3) print("子进程id:%d,父进程id:%d,name:%s"%(os.getpid(),os.getppid(),name)) # 子进程打印 print("父进程") # 创建子进程 p = Process(target=fun,args=("test",)) # 开始执行子进程 p.start() # 父进程等待子进程结束 p.join() print("子进程已结束") # 父进程打印
# Process的常用方法和属性 from multiprocessing import Process import time def fun(name,num,**kwargs): time.sleep(2) print("子进程:name:%s,num:%s"%(name,num)) for k,v in kwargs.items(): print("%s:%s"%(k,v)) print("父进程") p=Process(target=fun,name="p1",args=("test",10),kwargs={"a":10,"b":20}) p.start() p.join() print("子进程的名字:%s,id:%s"%(p.name,p.pid)) p.terminate() print("子进程已结束") # _______________________________________________________________________________ from multiprocessing import Process import time def fun(name,num,**kwargs): time.sleep(4) print("子进程:name:%s,num:%s"%(name,num)) for k,v in kwargs.items(): print("%s:%s"%(k,v)) print("父进程") p=Process(target=fun,name="p1",args=("test",10),kwargs={"a":10,"b":20}) p.start() p.join(2) print("子进程的名字:%s,id:%s"%(p.name,p.pid)) p.terminate() # 终止进程 print("子进程已结束")
# 子类化Process from multiprocessing import Process import os import time # 定义自己的继承类 class MyProcess(Process): def __init__(self,interval): super().__init__() self.interval=interval def run(self): print("子进程") startTime=time.time() time.sleep(self.interval) stopTime=time.time() print("子进程id:%d,父进程id:%d,执行了%ds"%(os.getpid(),os.getppid(),stopTime-startTime)) print("主进程") startTime=time.time() p=MyProcess(2) p.start() p.join() stopTime=time.time() print("子进程已结束,花费了%ds"%(stopTime-startTime))
# 同时创建多个进程 from multiprocessing import Process import os import time # 定义自己的继承类 class MyProcess(Process): def __init__(self,interval): super().__init__() self.interval=interval def run(self): print("子进程") startTime=time.time() time.sleep(self.interval) stopTime=time.time() print("子进程id:%d,父进程id:%d,执行了%ds"%(os.getpid(),os.getppid(),stopTime-startTime)) print("主进程") startTime=time.time() childs=[] for x in range(5): p=MyProcess(x+1) p.start() childs.append(p) # p.join() for item in childs: item.join() stopTime=time.time() print("子进程已结束,花费了%ds"%(stopTime-startTime))
# 异步 from multiprocessing import Pool import time,os def worker(msg): print("子进程pid:%d"%os.getpid()) startTime=time.time() time.sleep(2) stopTime=time.time() print("子进程msg:%s,花费的时间:%d"%(msg,stopTime-startTime)) # 创建进程池 pool = Pool(3) for i in range(10): pool.apply_async(worker,(i,)) # 异步请求 # 关闭进程池 pool.close() # 父进程等待进程池的结束 pool.join() print("进程池已结束") # —————————————————————————————————————————————————————————————————————————————————————— # 同步 from multiprocessing import Pool import time,os def worker(msg): print("子进程pid:%d"%os.getpid()) startTime=time.time() time.sleep(2) stopTime=time.time() print("子进程msg:%s,花费的时间:%d"%(msg,stopTime-startTime)) # 创建进程池 pool = Pool(3) for i in range(10): pool.apply(worker,(i,)) # 同步请求 # 关闭进程池 pool.close() # 父进程等待进程池的结束 pool.join() print("进程池已结束")
from multiprocessing import Process,Queue import os,time,random def write(q): for item in "ABC": print("正在往消息队列写入%s"%item) q.put(item) time.sleep(random.random()) def reader(q): while True: if not q.empty(): item = q.get() print("从消息队列读出%s"%item) time.sleep(random.random()) else: break # 创建消息队列 q=Queue() # 创建写入进程 pw=Process(target=write,args=(q,)) pw.start() pw.join() # 创建读进程 pr=Process(target=reader,args=(q,)) pr.start() pr.join() print("所有数据已读完") from multiprocessing import Pool,Manager import os,time,random def write(q): for item in "ABC": print("正在往消息队列写入%s"%item) q.put(item) time.sleep(random.random()) def reader(q): while True: if not q.empty(): item = q.get() print("从消息队列读出%s"%item) time.sleep(random.random()) else: break # 创建消息队列 q=Manager.Queue() #创建进程池 pool=Pool(3) # 创建写入进程 pool.apply(write,(q,)) # 创建读进程 pool.apply(reader,(q,)) pool.close() pool.join() print("所有数据已读完")
多线程 线程:是进程中一个“单一的连续控制流程”(a single sThread,equential flow of control)/执行路径 ——线程又被称为轻量级进程。 ——Threads run at the same time, independently of one another ——一个进程可拥有多个并行的(concurrent)线程 ——一个进程中的线程共享相同的内存单元/内存地址空间->可以访问相同的变量和对象,而且它们从同一堆中分配对象->通信、数据交换、同步操作 ——由于线程间的通信是在同一个地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度更快 线程和进程的区别 ——进程是系统进行资源分配和调度的一个独立单位 ——进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的运行效率 ——一个程序至少有一个进程,一个进程至少有一个线程 ——线程是进程的一个实体,是cpu调度和分派的基本单位,它是比进程更小的,能独立运行的基本单位 ——线程自己基本上不拥有系统资源,又拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源 ——线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高 ——线程不能独立执行,必须依存在进程中 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理与保护;而进程正相反。 线程和进程的根本区别: 进程:作为资源分配单位 线程:调度和执行的单位 多线程 Python的thread模块是比较底层的模块 Python的threading模块是对thread做了一些包装的,可以更加方便的被使用 #coding=utf-8 import threading import time def saySorry(): print("亲爱的,我错了,我能吃饭了吗?") time.sleep(2) if __name__=="__main__": for i in range(5): t=threading.Thread(target=saySorry) t.start() # 启动线程,让线程开始执行 查看线程的数量 #coding=utf-8 import threading,time def sing(): for i in range(3): print("正在唱歌……%s"%i) time.sleep(2) def dance(): for i in range(3): print("正在跳舞……%s"%i) time.sleep(2) if __name__=="__main__": print("开始:%s"%ctime()) t1=threading.Thread(target=sing) t2=threading.Thread(target=dance) t1.start() t2.start() while True: length=len(threading.enumerate()) print("当前运行的线程数为:%s"%length) if length<=1: break time.sleep(1) 线程的子类化 使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法 #coding=utf-8 import threading,time class MyThread(threading.Thread) def run(self) for i in range(3): time.sleep(1) msg="I'm"+self.name+"@"+str(i) # name属性中保存的是当前线程的 print(msg) if __name__=='__main__': t=MyThread() t.start() 线程的几种状态 多线程程序的执行顺序是不确定的。当执行到sleep语句,线程将被阻塞(Blocked),到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。代码中只能保证每个线程都运行完整个run函数,但是线程的启动顺序、run函数中每次循环的执行顺序都不能确定。 线程共享全局变量 在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享,(这点要比多进程要好) 缺点就是,线程对全局变量随意修改可能造成多线程之间对全局变量的混乱(即线程非安全) from threading import Thread import time g_num=100 def work1(): global g_num for i in range(3): g_num+=1 print("____in work1,g_num is %d____"%(g_num)) def work2(): global g_num print("____in work2,g_num is %d____"%g_num) print("线程创建之前,g_num is %d----"%g_num) t1=Thread(target=work1) t1.start() # 延时一会,保证t1线程中的事情做完 time.sleep(1) t2=Thread(target=work2) t2.start() 线程同步问题 当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源 threading模块中定义了Lock类,可以方便的处理锁定 g_num+=1 g_num=g_num+1 g_num原来存放在内存 操作分成三步 1、cpu得先从内存将g_num的值读到寄存器 2、在寄存器中将读出的数加1 3、将寄存器中的值写回到内存 线程同步,给线程加锁 创建锁 mutex=threading.Lock() 锁定 mutex.acquire([blocking]) ——如果设定blocking为True,则当前线程会堵塞,直接获取到这个锁为止(如果没有指定,那么默认为True) ——如果设定blocking为False,则当前线程不会堵塞 释放 mutex.release() from threading import Thread,Lock import time g_num=0 def test1(): global g_num for i in range(1000000): mutexFlag=mutex.acquire(True) if mutexFlag: g_num+=1 mutex.release() print("----test1----g_num=%d"%g_num) def test2(): global g_num for i in range(1000000): mutexFlag=mutex.acquire(True) # True表示堵塞 if mutexFlag: g_num+=1 mutex.release() print("test2-----g_num=%d"%g_num) # 创建一个互斥锁 # 默认是未上锁的状态 mutex=Lock() p1=Thread(target=test1) p1.start() p2=Thread(target=test2) p2.start() print("g_num---%d---"%g_num) 死锁 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁 进入到了死锁状态,可以使用ctrl+z退出 import threading,time class MyThread1(threading.Thread): def run(self): if mutexA.acquire(): print(self.name+"---------do1-------up----------") time.sleep(1) if mutexB.acquire(): print(self.name+"-------do1------down------") mutexB.release() mutexA.release() class MyThread2(threading.Thread): def run(self): if mutexB.acquire(): print(self.name+"-----do2--------up------") time.sleep(1) if mutexA.acquire(): print(self.name+"---do2--down-----") mutexA.release() mutexB.release() if __name__=="__main__": mutexA=threading.Lock() mutexB=threading.Lock() t1=MyThread1() t2=MyThread2() t1.start() t2.start() # 通过锁控制线程的执行顺序 from threading import Thread,Lock from time import sleep class Task1(Thread): def run(self): while True: if lock1.acquire(): print("Task1") sleep(1) lock2.release() class Task2(Thread): def run(self): while True: if lock2.acquire(): print("Task2") sleep(1) lock3.release() class Task3(Thread): def run(self): while True: if lock3.acquire(): print("Task3") sleep(1) lock1.release() # 使用Lock创建出的锁默认没有锁上 lock1=Lock() # 创建另一把锁,并且锁上 lock2=Lock() lock2.acquire() # 创建另外一把锁,并且锁上 lock3=Lock() lock3.acquire() t1=Task1() t2=Task2() t3=Task3() t1.start() t2.start() t3.start() 使用queue在多线程中传递数据 线程同步 Python的Queue模块中提供了同步、线程安全的队列类,包括: FIFO(先入先出)队列Queue LIFO(后入先出)队列LifoQueue 这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么就做完),能够在多线程中直接使用。 import threading,time # Python2中 # from Queue import Queue # Python3中 from queue import Queue class Producer(threading.Thread): def run(self): global queue count=0 while True: if queue.qsize()<1000: for i in range(100): count = count+1 msg="生成产品"+str(count) queue.put(msg) print(msg) time.sleep(0.5) class Consumer(threading.Thread): def run(self): global queue while True: if queue.qsize()>100: for i in range(3): msg=self.name+"消费了"+queue.get() print(msg) time.sleep(1) if __name__=="__main__": queue=Queue() for i in range(500): queue.put("初始产品"+str(i)) for i in range(2): p=Producer() p.start() for i in range(5): c=Consumer() c.start() ThreadLocal变量 一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。 可以理解为全局变量local_school是一个dict,可以绑定其他变量。 ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。 import threading # 创建全局ThreadLocal对象: local_school=threading.local() def process_student(): # 获取当前线程关联的student std=local_school.student print("Hello,%s (in %s)"%(std,threading.current_thread().name)) def process_thread(name): # 绑定ThreadLocal的student: local_school.student=name process_student() t1=threading.Thread(target=process_thread,args=("zhangsan",),name="t1") t1=threading.Thread(target=process_thread,args=("老王",),name="t2") t1.start() t2.start() t1.join() t2.join()
# 多线程 import threading,time def fun(num): print("线程执行%d"%num) time.sleep(2) for i in range(5): t=threading.Thread(target=fun,args=(i+1,)) t.start() print("主线程结束")
# 查看线程的数量 import threading,time def sing(): for i in range(3): print("唱歌") time.sleep(1) def dance(): for i in range(3): print("跳舞") time.sleep(2) st=threading.Thread(target=sing) dt=threading.Thread(target=dance) st.start() dt.start() while True: length=len(threading.enumerate()) print("当前线程数:%s"%length) if length<=1: break time.sleep(0.5)
#线程的子类化 import threading,time class MyThread(threading.Thread): def __init__(self,num,str1): super().__init__() self.num = num self.str1 = str1 def run(self): for i in range(4): time.sleep(1) msg="i am "+self.name+"@"+str(i)+"num:"+str(self.num)+"str1:"+self.str1 print(msg) if __name__=="__main__": for i in range(5): t=MyThread(10,"abc") t.start()
# 线程共享全局变量 import threading,time # 线程共享全局变量 g_num=100 def work1(): global g_num for i in range(3): g_num+=1 print("in work1,g_num=%d"%g_num) def work2(): print("in work2,g_num=%d"%g_num) print("主线程,g_num=%d"%g_num) w1=threading.Thread(target=work1) w1.start() time.sleep(1) w2=threading.Thread(target=work2) w2.start() print("主线程,g_num=%d"%g_num)
# 线程采用传参的方式使用全局变量 import threading,time # 线程共享全局变量 g_num=100 def work1(num): for i in range(3): num+=1 print("in work1,num=%d"%num) def work2(num): print("in work2,num=%d"%num) print("主线程,g_num=%d"%g_num) w1=threading.Thread(target=work1,args=(g_num,)) w1.start() time.sleep(1) w2=threading.Thread(target=work2,args=(g_num,)) w2.start() print("主线程,g_num=%d"%g_num) # ____________________________________________________ import threading,time # 线程共享全局变量 g_num=[11,22,33] def work1(num): for i in range(3): num.append(44) print("in work1,num=%s"%num) def work2(num): print("in work2,num=%s"%num) print("主线程,g_num=%s"%g_num) w1=threading.Thread(target=work1,args=(g_num,)) w1.start() time.sleep(1) w2=threading.Thread(target=work2,args=(g_num,)) w2.start() print("主线程,g_num=%s"%g_num)
import threading,time g_num=0 def worker(): global g_num for i in range(20000000): g_num+=1 for i in range(2): t=threading.Thread(target=worker) t.start() print("main thread: g_num=%s"%g_num)
import threading,time g_num=0 # 创建互斥锁 mutex=threading.Lock() def worker(): global g_num for i in range(20000000): #加锁 flag=mutex.acquire(False) if flag: g_num+=1 # 解锁 mutex.release() ths=[] for i in range(2): t=threading.Thread(target=worker) t.start() ths.append(t) for t in ths: t.join() print("main thread: g_num=%s"%g_num)
import threading,time class MyThread1(threading.Thread): def run(self): if mutexA.acquire(): print("thread1 do something...") time.sleep(2) if mutexB.acquire(): print("thread1 get mutexB") mutexB.release() mutexA.release() class MyThread2(threading.Thread): def run(self): if mutexB.acquire(): print("thread2 do something...") time.sleep(2) if mutexA.acquire(): print("thread2 get mutexA") mutexA.release() mutexB.release() mutexA=threading.Lock() mutexB=threading.Lock() t1=MyThread1() t1.start() t2=MyThread2() t2.start()
import threading,time class Task1(threading.Thread): def run(self): while True: if lock1.acquire(): print("Task1") time.sleep(1) lock2.release() class Task2(threading.Thread): def run(self): while True: if lock2.acquire(): print("Task2") time.sleep(1) lock3.release() class Task3(threading.Thread): def run(self): while True: if lock3.acquire(): print("Task3") time.sleep(1) lock1.release() lock1=threading.Lock() lock2=threading.Lock() lock2.acquire() lock3=threading.Lock() lock3.acquire() t1=Task1() t2=Task2() t3=Task3() t1.start() t2.start() t3.start()
import threading,time from queue import Queue class Producter(threading.Thread): def run(self): while True: if queue.qsize()<1000: for x in range(100): msg="产品"+str(x) print("%s创建了%s"%(self.name,msg)) queue.put(msg) time.sleep(1) class Consumer(threading.Thread): def run(self): while True: if queue.qsize()>100: for x in range(3): msg=queue.get() print("%s消费了%s"%(self.name,msg)) time.sleep(0.5) queue=Queue() for i in range(500): msg="产品"+str(i) queue.put(msg) for i in range(2): t=Producter() t.start() for i in range(5): c=Consumer() c.start()
import threading localSchool=threading.local() def processStudent(): name=localSchool.student print("Hello %s in %s"%(name,threading.current_thread().name) def processThread(name): localSchool.student=name processStudent() t1=threading.Thread(target=processThread,args=("zhangsan",),name="t1") t2=threading.Thread(target=processThread,args=("lisi",),name="t2") t1.start() t2.start()
socket-udp socket-tcp 创建Socket ——import socket ——socket.socket(AddressFamily,Type) ——说明:函数socket.socket创建一个socket,返回该socket的描述符 ——该函数带有两个参数 AddressFamily:可以选择AF_INET(用于Internet进程间通信)或者AF_UNIX(用于同一台机器进程间通信),实际工作常用AF_INET Type:套接字类型,可以是SOCK_STREAM(流式套接字,主要用于TCP协议)或者SOCK_DGRAM(数据报套接字,主要用于UDP协议) TCP与UDP的区别 1、基于连接与无连接 2、对系统资源的要求(TCP较多,UDP少) 3、UDP程序结构较简单 4、流模式与数据报模式 5、TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证 UDP编程 发送数据 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、准备接收方地址 sendAddr=('192.168.1.111',8080) # 3、从键盘获取数据 sendData=raw_input("请输入要发送的数据:") # 4、发送数据到指定的电脑上 udpSocket.sendto(sendData,sendAddr) # udpSocket.sendto(b"this is a test.",sendAddr) # 5、关闭套接字 udpSocket.close() 接收数据 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、准备接收方的地址 sendAddr=('192.168.1.103',8080) # 3、从键盘获取数据 sendData=input("请输入要发送的数据:") # 4、发送数据到指定的电脑上 udpSocket.sendto(byte(sendData,encoding="utf-8"),sendAddr) # udpSocket.sendto(b"this is a test.",sendAddr) # 5、等待接收对方发送的数据 recvData=udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数 # 6、显示对方发送的数据 print(recvData) # 7、关闭套接字 udpSocket.close() 绑定信息 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配 bindAddr=("",7788) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip udpSocket.bind(bindAddr) # 3、准备接收方的地址 sendAddr=('192.168.1.103',8080) # 4、从键盘获取数据 sendData=raw_input("请输入要发送的数据:") # 5、发送数据到指定的电脑上 udpSocket.sendto(sendData,sendAddr) # udpSocket.sendto(b"this is a test.",sendAddr) # 6、等待接收对方发送的数据 recvData=udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数 # 7、显示对方发送的数据 print(recvData) # 8、关闭套接字 udpSocket.close() echo服务器 绑定信息 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配 bindAddr=("",7788) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip udpSocket.bind(bindAddr) num=1 while True: recvData=udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数 #3、将接收到的数据再发送给对方 udpSocket.sendto(recvData[0],recvData[1]) #4、统计信息 print('已经将接收到的第%d个数据返回给对方,内容为:%s'%(num,recvData[1])) num+=1 #5、关闭套接字 udpSocket.close() 聊天室 from socket import * from time import ctime udpSocket=socket(AF_INET,SOCK_DGRAM) bindAddr=("",5533) udpSocket.bind(bindAddr) while True: recvData=udpSocket.recvfrom(1024) print('【%s】%s:%s'%(ctime(),recvData[1][0],recvData[0])) udpSocket.close()
# UDP发送 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、准备接收方地址 sendAddr=('192.168.234.1',8080) # 3、发送数据到指定的电脑上 udpSocket.sendto(b"this is a test.",sendAddr) # 4、关闭套接字 udpSocket.close()
# UDP接收 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、准备接收方地址 sendAddr=('192.168.234.1',8080) # 3、发送数据到指定的电脑上 udpSocket.sendto(b"this is a test.",sendAddr) # 4、等待接收对方发送的数据 recvData=udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数 # 5、显示对方发送的数据 print(recvData) # 6、关闭套接字 udpSocket.close()
# echo服务器 from socket import * # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配 bindAddr=('',9998) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip udpSocket.bind(bindAddr) num=1 while True: recvData=udpSocket.recvfrom(1024) #3、将接收到的数据再发送给对方 udpSocket.sendto(recvData[0],recvData[1]) #4、统计信息 print('已经将接收到的第%d个数据返回给对方,内容为:%s'%(num,recvData[0])) num+=1 #5、关闭套接字 udpSocket.close()
# 聊天室 from socket import * import time # 1、创建套接字 udpSocket=socket(AF_INET,SOCK_DGRAM) # 2、绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配 bindAddr=('',9884) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip udpSocket.bind(bindAddr) while True: recvData=udpSocket.recvfrom(1024) print('%s--%s:%s,'%(time.ctime(),recvData[1][0],recvData[0])) #5、关闭套接字 udpSocket.close()
TFTP下载过程:udp 69端口 1、客户端发送请求:下载还是上传(操作类型)、文件名。 2、服务器收到请求确认:如果下载文件没有,会发出错信息;如果下载文件有,发确认信息,或直接发文件的一部分给你。 3、客户端收到服务器数据后:如果收到文件数据,需要发确认的消息给服务器,并且要将数据写入本地文件;如果收到出错信息,直接中止下载过程。 4、循环过程:服务器发文件的一部分,客户端收到确认,直到文件发完。 5、如果文件已经结束,应该有一个文件结束的标志。 操作码 1——读请求,即下载 2——写请求,即上传 3——表示数据包,即DATA 4——确认码,即ACK 5——错误 读写请求 操作码|文件名|0|模式|0 数据包 操作码|块编码|数据 ACK 操作码|块编码 ERROR 操作码|差错码|差错信息|0 Struct模块使用 p.s.1: struct.pack("打包的格式",1,"文件名",0,"模式",0) struct.pack("!H8sb5sb",1,"test.jpg",0,"octet",0) struct.pack("!HH",4,p_num) cmdTuple=struct.unpack("!HH",recvData[:4])
# 给服务器发送请求 from socket import * import struct udpSocket=socket(AF_INET,SOCK_DGRAM) tftpAddr=("192.168.44.1",69) msg=struct.pack("!H8sb5sb",1,b"test.gif",0,b"octet",0) udpSocket.sendto(msg,tftpAddr) udpSocket.close()
# 接收服务器的返回信息 from socket import * import struct udpSocket=socket(AF_INET,SOCK_DGRAM) tftpAddr=("192.168.44.1",69) msg=struct.pack("!H8sb5sb",1,b"test.gif",0,b"octet",0) udpSocket.sendto(msg,tftpAddr) recvData=udpSocket.recvfrom(1024) print(recvData) udpSocket.close() # 接收服务器的返回信息并解包 from socket import * import struct udpSocket=socket(AF_INET,SOCK_DGRAM) tftpAddr=("192.168.44.1",69) msg=struct.pack("!H9sb5sb",1,b"test1.gif",0,b"octet",0) udpSocket.sendto(msg,tftpAddr) recvData=udpSocket.recvfrom(1024) data=struct.unpack("!HH",recvData[0][:4]) print(recvData) if data[0]==5: print("%s"%recvData[0][4:].decode()) udpSocket.close()
# 接收服务器的返回信息并解包 from socket import * import struct udpSocket=socket(AF_INET,SOCK_DGRAM) tftpAddr=("192.168.44.1",69) fileName="test.gif" fmt=str.format("!H%dsb5sb"%len(fileName)) # 发请求 msg=struct.pack(fmt,1,fileName.encode(),0,b"octet",0) udpSocket.sendto(msg,tftpAddr) # 解包 recvData=udpSocket.recvfrom(1024) data=struct.unpack("!HH",recvData[0][:4]) print(recvData) if data[0]==5: # 出错信息 print("%s"%recvData[0][4:].decode()) elif data[0]==3: # 正常的数据 f=open(fileName,"wb") f.write(recvData[0][4:]) udpSocket.close()
# 接收服务器的返回信息并解包 from socket import * import struct udpSocket=socket(AF_INET,SOCK_DGRAM) tftpAddr=("192.168.44.1",69) fileName=input("请输入文件名:") fmt=str.format("!H%dsb5sb"%len(fileName)) # 发请求 msg=struct.pack(fmt,1,fileName.encode(),0,b"octet",0) udpSocket.sendto(msg,tftpAddr) f=None while True: # 解包 recvData=udpSocket.recvfrom(1024) data=struct.unpack("!HH",recvData[0][:4]) print(recvData) dataType=data[0] dataNo=data[1] if dataType==5: # 出错信息 print("%s"%recvData[0][4:].decode()) break elif dataType==3: # 正常的数据 dataLen=len(recvData[0][4:]) if dataNo==1: f=open(fileName,"wb") f.write(recvData[0][4:]) else: f.write(recvData[0][4:]) # 给服务器发确认包 msg=struct.pack("!HH",4,dataNo) udpSocket.sendto(msg,recvData[1]) # 收到数据包中的数据的长度如果小于512,就认为文件已经接收完毕 if dataLen<512: f.close() break else: break udpSocket.close()
TCP服务器 TCP服务器流程如下: 1、socket创建一个套接字 2、bind绑定ip和port 3、listen使套接字变为可以被动链接 4、accept等待客户端的链接 5、recv/send接收发送数据 from socket import * # 创建socket tcpSerSocket = socket(AF_INET,SOCK_STREAM) # 绑定本地信息 address = ('',7788) tcpSerSocket.bind(address) # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收 tcpSerSocket.listen(5) # 如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务器 # newSocket用来为这个客户端服务 # tcpSerket就可以省下来专门 newSocket,clientAddr = tcpSerSocket.accept() # 接收对方发送过来的数据,最大接收1024个字节 recvData=newSocket.recv(1024) print '接收到的数据为:',recvData # 发送一些数据到客户端 newSocket.send("thank you!") # 关闭为这个客户端服务的套接字,只要关闭了,就意味着不能再为这个客户端服务了 newSocket.close() # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接 tcpSerSocket.close() select 多路复用 设备操作认为是一种文件操作 三个标准的输入输出设备 stdin stdout stderr socket的收发也可以看做文件的读写 单进程服务器-select版 import select,socket,sys server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) serverAddr=('',9898) server.bind(serverAddr) server.listen(5) inputs=[server,sys.stdin] running=True while True: #调用select函数,阻塞等待 readable,writeable,exceptional=select.select(inputs,[],[]) # 数据抵达,循环 for sock in readable: #监听到有新的连接 if sock==server: conn,addr=server.accept() #select监听的socket inputs.append(conn) #监听到键盘有输入 elif sock == sys.stdin: cmd=sys.stdin.readline() running=False break #有数据到达 else: #读取客户端链接发送的数据 data = sock.recv(1024) if data: sock.send(data) else: #移除select监听的socket inputs.remove(sock) sock.close() #如果检测到用户输入敲击键盘,那么就退出 if not running: break server.close() 单进程服务器epoll版 import socket,select #创建套接字 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #设置可以重复使用绑定的信息 s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #绑定本机信息 s.bind('',8899) #变为被动 s.listen(10) #创建一个epoll对象 epoll=select.epoll() #测试,用来打印套接字对应的文件描述符 # print s.fileno() # print select.EPOLLIN|select.EPOLLET #注册事件到epoll中 # epoll.register(fd[,eventmask]) #注意,如果fd已经注册过,则会发生异常 #将创建的套接字添加到epoll的事件监听中 epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET) connections={} addresses={} #循环等待客户端的到来或者对方发送数据 while True: #epoll 进行fd扫描的地方——未指定超时时间则为阻塞等待 epoll_list=epoll.poll() #对事件进行判断 for fd,events in epoll_list: #prind fd #print events #如果是socket创建的套接字被激活 if fd==s.fileno(): conn,addr=s.accept() print('有新的客户端到来%s'%str(addr)) #将conn和addr信息分别保存起来 connections[conn.fileno()]=conn addresses[conn.fileno()]=addr #向epoll中注册连接socket的可读事件 epoll.register(conn.fileno(),select.EPOLLIN|select.EPOLLET) elif events=select.EPOLLIN: #从激活fd上接收 recvData=connections[fd].recv(1024) if len(recvData)>0: print("recv:%s"%recvData) else: #从epoll中移除该连接fd epoll.unregister(fd) #server侧主动关闭该连接fd connections[fd].close() print("%s--offline--"%str(addresses[fd]))
# TCP服务器 from socket import * serverSocket=socket(AF_INET,SOCK_STREAM) #绑定地址 serverAddr=('',9999) serverSocket.bind(serverAddr) #监听listening serverSocket.listen(5) #接收客户端连接 clientSocket,clientAddr = serverSocket.accept() #与客户端通信 recvData = clientSocket.recv(1024) print(recvData) clientSocket.send(b"Hello") #关闭socket clientSocket.close() serverSocket.close()
# TCP客户端 from socket import * clicketSocket = socket(AF_INET,SOCK_STREAM) #连接服务器 serverAddr=('',9999) clicketSocket.connect(serverAddr) #发送数据 clicketSocket.send(b"hello") #收数据 recvData=clicketSocket.recv(1024) print(recvData) clicketSocket.close()
# TCP服务器多线程版 from socket import * serverSocket=socket(AF_INET,SOCK_STREAM) #绑定地址 serverAddr=('',9999) serverSocket.bind(serverAddr) #监听listening serverSocket.listen(5) while True: #接收客户端连接 clientSocket,clientAddr = serverSocket.accept() print("接收到客户端:%s"%clientAddr[0]) #与客户端通信 # recvData = clientSocket.recv(1024) # print(recvData) # clientSocket.send(b"Hello") #关闭socket clientSocket.close() serverSocket.close()
#epoll版服务器 from socket import * import select,sys serverSocket=socket(AF_INET,SOCK_STREAM) serverSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) serverAddr=('',8899) serverSocket.bind(serverAddr) serverSocket.listen(5) #创建epoll对象 epoll = select.epoll() #向epoll注册设备 epoll.register(serverSocket.fileno(),select.EPOLLIN|select.EPOLLET) epoll.register(sys.stdin.fileno(),select.EPOLLIN|select.EPOLLET) connections={} clientAddrs={} while True: running=True # 使用epoll查看每个设备的状态,将有事件发生的设备放入列表 pollList=epoll.poll() for fd,event in pollList: if fd == serverSocket.fileno(): #处理服务器(接收客户端连接) clientSocket,clientAddr = serverSocket.accept() print("接收到新的链接:%s"%clientAddr[0]) connections[clientSocket.fileno()]=clientSocket clientAddrs[clientSocket.fileno()]=clientAddr epoll.register(clientSocket.fileno(),select.EPOLLIN|select.EPOLLET) elif fd==sys.stdin.fileno(): #处理用户键盘输入 cmd=sys.stdin.readline() running=False break else: #处理客户端socket recvData=connections[fd].recv(1024) if recvData: print("接收到数据:%s"%recvData) connections[fd].send(recvData) else: #客户端已经断开 connections[fd].close() print("%s客户端关闭"%clientAddrs[fd][1]) epoll.unregister(fd) if running==False: break serverSocket.close()
# TCP客户端 from socket import * clicketSocket = socket(AF_INET,SOCK_STREAM) #连接服务器 serverAddr=('',9999) clicketSocket.connect(serverAddr) while True: msg=input(">>") #发送数据 clicketSocket.send(msg.encode()) #收数据 recvData=clicketSocket.recv(1024) print("<<%s"%recvData) if msg=="byb": break clicketSocket.close()
# TCP服务器多线程版 from socket import * import threading def clientProcess(clientSocket): while True: #与客户端通信 recvData = clientSocket.recv(1024) print(recvData) clientSocket.send(recvData) if recvData.decode()=="byb": break #关闭socket clientSocket.close() serverSocket=socket(AF_INET,SOCK_STREAM) #绑定地址 serverAddr=('',9999) serverSocket.bind(serverAddr) #监听listening serverSocket.listen(5) while True: #接收客户端连接 clientSocket,clientAddr = serverSocket.accept() print("接收到客户端:%s,port:%s"%(clientAddr[0],clientAddr[1])) clientThread=threading.Thread(target=clientProcess,args=(clientSocket,)) clientThread.start() serverSocket.close()
# TCP服务器多进程版 from socket import * import multiprocessing def clientProcess(clientSocket): while True: #与客户端通信 recvData = clientSocket.recv(1024) print(recvData) clientSocket.send(recvData) if recvData.decode()=="byb": break #关闭socket clientSocket.close() serverSocket=socket(AF_INET,SOCK_STREAM) #绑定地址 serverAddr=('',9999) serverSocket.bind(serverAddr) #监听listening serverSocket.listen(5) while True: #接收客户端连接 clientSocket,clientAddr = serverSocket.accept() print("接收到客户端:%s,port:%s"%(clientAddr[0],clientAddr[1])) client_Process=multiprocessing.Process(target=clientProcess,args=(clientSocket,)) client_Process.start() serverSocket.close()
#select版服务器 from socket import * import select,sys serverSocket=socket(AF_INET,SOCK_STREAM) serverSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) serverAddr=('',8899) serverSocket.bind(serverAddr) serverSocket.listen(5) inputList=[serverSocket,sys.stdin] while True: running=True # 使用select查看每个设备的状态,并根据状态将设备放入不同的列表 readable,writeable,exceptionable=select.select(inputList,[],[]) for sock in readable: if sock == serverSocket: clientSocket,clientAddr = sock.accept() print("接收到新的链接:%s"%clientAddr[0]) inputList.append(clientSocket) elif sock==sys.stdin: cmd=sys.stdin.readline() running=False break else: recvData=sock.recv(1024) if recvData: print("接收到数据:%s"%recvData) sock.send(recvData) else: sock.close() inputList.remove(sock) if running==False: break serverSocket.close()
#epoll版服务器 from socket import * import select,sys serverSocket=socket(AF_INET,SOCK_STREAM) serverSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) serverAddr=('',8899) serverSocket.bind(serverAddr) serverSocket.listen(5) #创建epoll对象 epoll = select.epoll() #向epoll注册设备 epoll.register(serverSocket.fileno(),select.EPOLLIN|select.EPOLLET) epoll.register(sys.stdin.fileno(),select.EPOLLIN|select.EPOLLET) connections={} clientAddrs={} while True: running=True # 使用epoll查看每个设备的状态,将有事件发生的设备放入列表 pollList=epoll.poll() for fd,event in pollList: if fd == serverSocket.fileno(): #处理服务器(接收客户端连接) clientSocket,clientAddr = serverSocket.accept() print("接收到新的链接:%s"%clientAddr[0]) connections[clientSocket.fileno()]=clientSocket clientAddrs[clientSocket.fileno()]=clientAddr epoll.register(clientSocket.fileno(),select.EPOLLIN|select.EPOLLET) elif fd==sys.stdin.fileno(): #处理用户键盘输入 cmd=sys.stdin.readline() running=False break else: #处理客户端socket recvData=connections[fd].recv(1024) if recvData: print("接收到数据:%s"%recvData) connections[fd].send(recvData) else: #客户端已经断开 connections[fd].close() print("%s客户端关闭"%clientAddrs[fd][1]) epoll.unregister(fd) if running==False: break serverSocket.close()
协程 比线程更小的执行单元 某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行 协程自己主动让出CPU 协程的切换只是单纯的操作CPU的上下文,比线程的切换更快速 1:N模式。所谓1:就是一个线程作为一个容器里面放置多个协程 IO密集型的程序和CPU密集型程序 import time def A(): while True: print("-------A") yield time.sleep(3) def B(): while True: print("------B") c.next() time.sleep(4) if __name__=="__main__": a=A() B(a) 协程-greenlet python中的greenlet模块对协程进行了封装 安装模块:pip3 install greenlet from greenlet import greenlet import time def test1(): while True: print "A" gr2.switch() time.sleep(2) def test2(): while True: print "B" gr1.switch(2) # 协程切换器 gr1=greenlet(test1) gr2=greenlet(test2) #切换到gr1中运行 gr1.switch() 协程-gevent 安装模块:pip3 install gevent python中还有一个比greenlet更强大的并且能够自动切换任务的模块,gevent 原理是当一个greenlet遇到IO(指的是input output输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他greenlet,等IO操作完成,再在适当的时候切换回来继续执行 gevent切换执行 # coding=utf-8 import gevent def f(n): for i in range(n) print gevent.getcurrent(),i g1=gevent.spawn(f,5) g2=gevent.spawn(f,5) g3=gevent.spawn(f,5) g1.join() g2.join() g3.join() 协程-并发下载器 from gevent import monkey import gevent,urllib2 # 有IO要做时需要这一句 monkey.patch_all() def myDownLoad(url): print('GET:%s'%url) resp=urllib2.urlopen(url) data=resp.read() print("%d bytes received from %s."%(len(data),url)) gevent.joinall([ gevent.spawn(myDownLoad,'http://www.baidu.com/'), gevent.spawn(myDownLoad,'http://www.sina.com/'), gevent.spawn(myDownLoad,'http://www.yahoo.com/'), ]) 协程-tcp版服务器 import sys,time,gevent from gevent import socket,monkey monkey.patch_all() def handle_request(conn): while True: data=conn.recv(1024) if not data: conn.close() break print("recv:",data) conn.send(data) def server(port): s=socket.socket() s.bind('',port) s.listen(5) while True: cli,addr=s.accept() #接收客户端连接 gevent.spawn(handle_request,cli) #生成一个协程 if __name__=='__main__': server(7788) 正则表达式:Python中需要通过正则表达式对字符串进行匹配的时候,可以使用re模块 import re # 使用match方法进行匹配操作 result=re.match(正则表达式,要匹配的字符串) #如果上一步匹配到数据的话,可以使用group方法提取数据 result=re.group() re.match是用来进行正则匹配检查的方法,若字符串匹配正则表达式,则match方法返回匹配对象(Match Object),否则返回None(注意不是空字符串"") 匹配对象Match Object具有group方法,用来返回字符串的匹配部分 字符 功能 表示字符 . 匹配任意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,} 匹配前一个字符至少出现m次 {m,n} 匹配前一个字符出现从m到n次 表示边界 ^ 匹配字符串开头 $ 匹配字符串结尾 \b 匹配一个单词的边界 \B 匹配非单词边界 匹配分组 | 匹配左右任意一个表达式 (ab) 将括号中字符作为一个分组 \num 引用分组num匹配到的字符串 (?P<name>) 分组起别名 (?P=name) 引用别名为name分组匹配到的字符串 P.S.1: # 如果hello的首字符小写,那么正则表达式需要小写的h ret=re.match("h","hello Python") ret.group() # 如果hello的首字符大写,那么正则表达式需要大写的H ret=re.match("H","Hello Python") ret.group() # 大小写h都可以的情况 ret = re.match("[hH]","hello Python") ret.group() ret = re.match("[hH]","Hello Python") ret.group() # 匹配0到9第一种写法 ret = re.match("[0123456789]","7Hello Python") ret.group() # 匹配0到9第二种写法 ret = re.match("[0-9]","7Hello Python") ret.group() 原始字符串 Python 中字符串前面加上r表示原声字符串 正则表达式里使用“\”作为转义字符 -假如你需要匹配文本中的字符“\”,那么使用正则表达式里需要4个反斜杠“\\”:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在反斜杠后再在正则表达式里转义成一个反斜杠 Python里的原声字符串很好地解决了这个问题,有了原始字符串,你再也不用担心是不是漏写了反斜杠 -ret=re.match(r"c:\\a","c:\\a\\b\\c") ret.group() P.S.2: ret = re.match("\d{11}","13256489561") ret.group P.S.3: # 通过引用分组中匹配到的数据即可,但是要注意是元字符串 ret = re.match(r"<([a-zA-Z]*)>\w*</\1>","<html>gg</html>") ret.group() # 如果2对<>中的数据不一样,就无法匹配出来 ret = re.match(r"<([a-zA-Z]*)>\w*</\1>","<html>gg</body>") ret.group() re模块的其他用法 search搜索符合特征的字符串 —— ret = re.search(r"\d+","阅读次数为9999") ret.group() findall找出所有符合特征的字符串 —— ret = re.findall(r"\d+","python=9999,c=7890,c++=12345") sub将匹配到的数据进行替换 —— ret = re.sub(r"\d+",'998',"python=997") split根据匹配进行切割字符串,并返回一个列表 —— ret = re.split(r":| ","info:xiaoZhang 33 shandong") 贪婪和非贪婪 Python里正则表达式数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符 非贪婪则相反,总是尝试匹配尽可能少的字符 在“*”,“?”,“+”,“{m,n}”后面加上?,使贪婪变成非贪婪 —— re.match(r"aa(\d+)","aa2345ss").group(1) >>2345 —— re.match(r"aa(\d+?)","aa2345ss").group(1) >>2 —— re.match(r"aa(\d+)ss","aa2345ss").group(1) >>2345 —— re.match(r"aa(\d+?)ss","aa2345ss").group(1) >>2345
# greenlet实现协程 from greenlet import greenlet import time def test1(): while True: print "A" gr2.switch() time.sleep(2) def test2(): while True: print "B" gr1.switch(2) # 协程切换器 gr1=greenlet(test1) gr2=greenlet(test2) #切换到gr1中运行 gr1.switch()
# gevent实现协程 import gevent def fun(num): for i in range(num): print("%s%s"%(gevent.getcurrent(),str(i))) g1=gevent.spawn(fun,5) g2=gevent.spawn(fun,5) g3=gevent.spawn(fun,5) g1.join() g2.join() g3.join()
# gevent的自动切换 import gevent def fun(num): for i in range(num): print("%s%s"%(gevent.getcurrent(),str(i))) #模拟一个费时的任务,注意必须用gevent中的sleep gevent.sleep(1) g1=gevent.spawn(fun,5) g2=gevent.spawn(fun,5) g3=gevent.spawn(fun,5) g1.join() g2.join() g3.join()
# gevent下载器 from gevent import monkey import gevent,urllib # 需要让gevent能够自动处理io事件,必须先打补丁 monkey.patch_all() def myDownLoad(url): print('GET:%s'%url) resp=request.urlopen(url) data=resp.read() print("%d bytes received from %s."%(len(data),url)) gevent.joinall([ gevent.spawn(myDownLoad,'http://www.baidu.com/'), gevent.spawn(myDownLoad,'http://www.sina.com/'), gevent.spawn(myDownLoad,'http://www.yahoo.com/') ])
# 协程-tcp服务器 from gevent import socket,monkey import gevent #打补丁 monkey.patch_all() #客户端处理 def handleRequest(conn): while True: #接数据 data=conn.recv(1024) if not data: conn.close() break else: print("收到数据:%s"%data) conn.send(data) # 创建服务器 def server(port): s=socket.socket() # s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) s.bind('',port) s.listen(5) while True: cli,addr=s.accept() gevent.spawn(handleRequest,cli) if __name__=="__main__": server(7788)
# 未使用正则的写法
def isPhone(num): # 判断长度是不是11位 if len(num) !=11: return False # 判断是不是都是数字 if not str.isdigit(num): return False # 判断前三位是不是正确的号段 li = ['137','138','187'] if num[:3] not in li: return False return True print(isPhone('1374567')) print(isPhone('137fde43454')) print(isPhone('13788899964')) print(isPhone('15688888996'))
Mysql安装 查询是否安装:rpm -qa | grep mysql 安装服务端和客户端:yum -y install mysql-server mysql 服务启动脚本查询:cd /etc/init.d/ 服务启动:service mysqld start 作为系统项跟随系统启动:chkconfig mysqld on 查询跟随系统启动项:chkconfig --list 显示mysql相关网络信息:netstat -ntpl | grep mysql 关于linux下mysql乱码:安装默认是希腊编码 修改方法:# vi /etc/my.cnf [mysqld]下添加 default-character-set=utf8 然后重启服务:service mysqld restart 【 初始化数据库安全设置: 安装完mysql-server 可以运行mysql_secure_installation。运行mysql_secure_installation会执行几个设置: a)为root用户设置密码 b)删除匿名账号 c)取消root用户远程登录 d)删除test库和对test库的访问权限 e)刷新授权表使修改生效 通过这几项的设置能够提高mysql库的安全。建议生产环境中mysql安装这完成后一定要运行一次mysql_secure_installation,详细步骤请参看下面的命令: [root@localhost ~]# mysql_secure_installation NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY! In order to log into MySQL to secure it, we'll need the current password for the root user. If you've just installed MySQL, and you haven't set the root password yet, the password will be blank, so you should just press enter here. Enter current password for root (enter for none): <–初次运行直接回车 OK, successfully used password, moving on… Setting the root password ensures that nobody can log into the MySQL root user without the proper authorisation. Set root password? [Y/n] <– 是否设置root用户密码,输入y并回车或直接回车 New password: <– 设置root用户的密码 Re-enter new password: <– 再输入一次你设置的密码 Password updated successfully! Reloading privilege tables.. … Success! By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? [Y/n] <– 是否删除匿名用户,生产环境建议删除,所以直接回车 … Success! Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? [Y/n] n <–是否禁止root远程登录,根据自己的需求选择Y/n并回车,建议禁止 … Success! By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? [Y/n] n <– 是否删除test数据库 - Dropping test database… … Success! - Removing privileges on test database… … Success! Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? [Y/n] y <– 是否重新加载权限表 … Success! Cleaning up… All done! If you've completed all of the above steps, your MySQL installation should now be secure. Thanks for using MySQL! [root@localhost ~]#mysql -uroot -p .... mysql>grant all on *.* to root@'%' identifide by '你的数据库密码' <- 设置一个root用户允许远程的其他机器登陆 .... 关闭防火墙:service iptables stop 禁止防火墙开机启动:chkconfig iptables off P.S.2:生产模式下,一般不允许关闭防火墙,正确方法是在防火墙加入开放3306端口规则,添加方法看下面P.S.1 】 P.S.1: Windows下Navicat连接虚拟机CentOS中的MySql 【 首先配置CentOS下防火墙iptables规则: # vim /etc/sysconfig/iptables 向其中加入下列规则: -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT 说明:防火墙开放http用的80端口和连接MySql的3306端口。 P.S.1.1:新加的规则写在-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT 之后 # service iptables restart 然后配置MySQL允许远程登陆: 先在CentOS中连接Mysql数据库 # mysql -u root -p 执行 > GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '你的数据库root密码,如果没运行mysql_secure_installation设置,密码为空' WITH GRANT OPTION; 再执行下 > flush privileges; 刷新一下权限,不用重启MySql服务。 最后配置工作完成,打开Windows下的Navicat就可以直接连接到CentOS下的数据库了。 】 cmd客户端连接 mysql的bin目录下:mysql -u root -p -h 192.168.234.44 -u:用户名 -p:密码 -h:host服务端地址 SQL语言分类 ——DQL(数据查询语言):select ——DML(数据操作语言):insert、update、delete ——DDL(数据定义语言):create、alter、drop ——DCL(数据控制语言):grant、revoke ——TCL(事务控制语言):SAVEPOINT、ROLLBACK、SET TRANSACTION、COMMIT DDL: 数据库操作 创建数据库:create database 数据库名 charset=utf8; 删除数据库:drop database 数据库名; 切换数据库:use 数据库名; 查看当前选择的数据库:select database() 表操作 查看当前数据库中所有的表:show tables; 创建表、设置auto_increment自动增长:create table 表名(列及类型); P.S.3: create table infomation( id int auto_increment primary key, name varchar(20) not null ) 修改表:alter table 表名 add|change|drop|modify 列名 类型; P.S.4: alter table infomation add birthday datetime; alter table infomation change 原字段 新字段 varchar(100) not null; alter table infomation modify id int(4) auto_increment; 删除表:drop table 表名; 查看表结构:desc 表名; 更改表名称;rename table 原表名 to 新表名; 查看表的创建语句:show create table '表名'; DML、DQL: 查询:select * from 表名; 增加: 全列插入:insert into 表名 values(....); insert into infomation values(1,'harold','sh',0) 缺省插入:insert into 表名 (列1,....) values (值1,....); insert into infomation (id,name) values (2,'ZH'); 同时插入多条数据:insert into 表名 [列名] values (....),(....),....;或insert into 表名 (列1,列2,列3....) values (值1),(值2),(值3),....; insert into infomation (id,name) values (3,'WW'),(4,'SL'); insert into infomation values (3,'WW','BJ',0),(4,'SL','SH',1); 修改:update 表名 set 列1=值1,.... where 条件 删除:delete from infomation where address='BJ' 备份与恢复 数据备份: 进入mysql库目录:cd /var/lib/mysql 运行mysqldump命令:mysqldump -uroot -p 数据库名 > ~/Desktop/back.sql 数据恢复: 连接MySQL,创建数据库 退出连接,执行命令:mysql -uroot -p 数据库名 < ~/Desktop/back.sql P.S.5: 如果存在一个sql脚本文件,直接执行该文件:source sql文件的路径 去重查询: select distinct deptno from emp; 排序:asc\desc select name,sal from emp order by sal asc 分页查询: select * from emp limit 第几条数据开始,读取多少条数据; P.S.6:select * from emp limit 0,5;
聚合函数和内置函数 聚合: 为了快速得到统计数据,提供了5个聚合函数 count(*)表示计算总行数,括号中写星与列名,结果是相同的 count(列)、max(列)、min(列)、sum(列)、avg(列) 内置函数: 拼接:concat 截取:left(str,len)返回字符串str左端len个字符;right(str,len)返回字符串str右端len个字符;substring(str,pos,len)返回字符串str的位置pos起len个字符【select substring('abc123',2,3);】。 字符串拼接:select concat (ename,'的工资是:',sal) from emp; 去除空格:ltrim(str)返回删除了左空格的字符str;rtrim(str)返回删除了右空格的字符串str;trim([方向 remstr from] str)方向包括:both、leading、trailing,表示两侧、左、右。 日期格式化:data_format(date,format) format参数可用的值如下: 获取年:%Y,返回4位的整数 获取年:%y,返回2位的整数 获取月:%m,返回1-12的整数 获取日:%d,返回整数 获取时:%h,返回1-12的整数 获取分:%i,返回0-59的整数 获取秒:%s,返回0-59的整数 P.S.1: select date_format('2018-11-22','%Y %m %d'); 当前日期:select current_date(); 当前时间:select current_time(); 当前日期时间:select now(); 分组查询和过滤 GROUP BY 字句将表中数据分成若干小组 存在group by分组,select查询列不能写group by以外的字段,除非这些用在聚合函数中。 语法格式: ——select column, group_function(column) ——from table ——[where condition] ——[group by group_by_expression] ——[order by column] 使用举例: ——select deptno,avg(sal) from emp group by deptno; ——select deptno,job,count(*),avg(sal) from emp group by deptno,job; having过滤: 对分组查询结果进行过滤,要使用having从句 having从句过滤分组后的结果,它只能出现在group by从句之后,而where从句要出现在group by从句之前。 where过滤行,having过滤分组,having支持所有where操作符 语法格式: ——select column,group_function(column) ——from table ——[where condition] ——[group by group_by_expression] ——[having group_condition] ——[order by column] 过滤分组之后的结果 使用举例: 统计,每个部门的人数,最高工资,最低工资,平均工资,但是部门的平均工资小于2000的不要统计 ——select deptno, count(1), max(sal), min(sal), avg(sal) from emp group by deptno having avg(sal) > 2000 列出工资最小值小于2000的职位 ——select deptno,job from emp group by job having min(sal) < 2000 统计人数小于4的部门的平均工资 ——select avg(sal) from emp group by deptno having count(1) < 4 统计各部门的最高工资,排除最高工资小于3000的部门 ——select max(sal) from emp group by deptno having max(sal) >= 3000 列出每年入职的人数和最高工资 ——select count(1), year(hiredate), max(sal) from emp group by year(hiredate) 表的约束 主键约束(Primary Key):要求主键列数据唯一,不允许为空。主键可以包含表的一列或多列,如果包含表的多列,则需要在表级定义。 唯一约束(Unique):要求该列唯一,允许为空 检查约束(Check):某列取值范围限制、格式限制等,如年龄的约束 非空约束(not null):某类内容不能为空 外键约束(Foreign Key):用于两表间建立关系,需要指定引用主表的那列。外键通常用来约束两个表之间的数据关系,定义外键的那张表称为子表,另一张表称为主表。在表的创建过程中,应该先创建主表,后创建字表。 主键约束:从功能上看相当于非空且唯一;一个表只允许一个主键;一个表可能是由两个或者两个以上的字段组成主键——联合主键;主键是表中能够唯一确定一个行数据的字段;主键字段可以是单字段或者是多字段的组合。 create table t3( id number, --primary key, constraint t3_pk primary key(id) ); 唯一性约束: create table employees( id number(6), name varchar(50), email varchar(45) CONSTRAINT emp_email_uk UNIQUE(email) ); Check约束: create table emp3( id number(4) primary key, age number(2) check(age > 0 and age < 100), salary number(7,2), constraint salary_check check(salary>0) ); 外键约束: 外键是表中的一个列,其值必须在另一个表的主键或者唯一键中列出; 作为主键的表称为“主表”,作为外键的关系成为“依赖表”; 外键参照的是主表的主键或唯一键; 对于主表的删除和修改主键值的操作,会对依赖关系产生影响,已删除为例;当要删除主表的某个记录(即删除一个主键值,那么对依赖的影响可采取下列3种做法: RESTRICT方式:只有当依赖表中没有一个外键值与要删除的主表中主键值相对应时,才可以执行删除操作。 CASCADE方式:将依赖表中所有外键值与主表中要删除的主键值相对应的记录一起删除。 SET NULL方式:将依赖表中所有与主表中被删除的主键值相对应的外键值设为空值 ——FOREIGN KEY (字段名) REFERENCES 表名(字段名) ——[ON DELETE[CASCADE|SET NULL]]如省略on短句,缺省为第一种处理方式。 ) 添加约束: ——ALTER TABLE 表名 ADD CONSTRAINT 约束名 约束类型 具体的约束说明 删除约束 ——ALTER TABLE 表名 DROP CONSTRAINT 约束名 可以添加或删除约束,但不能修改。 P.S.2: create table 表一( id int(8) primary ket, name varchar(200) not null, emp_max_count int(2) check(emp_max_count>0 and emp_max_count<51), parent_id int(8) foreign key parent_id REFERENCES 表二(id) ) ALTER table 表一 add constraint `FK_DEPTNO` foreign key (`parent_id`) references `表一` (`id`) 索引 自动索引:主键、唯一约束 手动索引: 给员工的名字字段建立索引:create index i_emp_ename on emp(ename) 在一列或者多列上创建索引: create index 索引名 on 表名 (列名1,列名2,....); 删除索引: drop index 索引名; 视图: 定义 视图是从若干基本表和(或)其他视图构造出的虚表。 在创建一个视图时,只是存放的视图的定义,也即是动态检索数据的查询语句,而并不存放视图对应的数据 在用户使用视图时才去求相对应的数据,所以视图被称作“虚表” 作用 可以限制对数据的访问,可以给用户授予表的特定部分的访问权限而不是整个表的访问权限 可以使复杂的查询变的简单,在编写查询后,可以方便的重用它而不必知道它的基本查询细节 提供了对相同数据的不同显示 创建视图: ——create [or replace] view 视图名 ——[(alias[,alias]....)] ——AS subquery ——[with read only]; 查询视图: select * from 视图名; 删除视图: drop view 视图名; P.S.3: ——创建视图之后,隐藏基本表中某些字段,只对外功能部分字段 create view v_emp as select empno, ename, job from emp; select * from v_emp limit 1; insert into v_emp values (3333,'ss','clerk') 视图可以简化查询语句 查询员工表中工资最高的和最低员工信息 create or replace view v_emp2 as select max(sal) as max_sal , min(sal) as min_sal from emp select * from emp where sal in (select max_sal from v_emp2) or sal in (select min_sal from v_emp2) 总结: 视图是一个虚拟表,对应一条select语句,可将它的输出看作一个表 视图不存储数据 改变基本表的数据,也会反应到基于该表的视图上 视图可以基于基本表的若干行,若干列 视图可以基于一个表、多个表,甚至是基于其他的视图 使用视图可以提供数据访问的安全性,只显示指定的行列数据 使用视图可以降低查询的难度,定制数据显示 可以对视图进行CRUD操作,实际上是对基本表的CRUD操作 如果视图对应多个表,一般不允许添加操作,可以通过触发器解决 使用with read only定义只读视图 事务(Transaction):是为了保证数据库的完整性 事务是一个操作序列。这些操作要么都做,要么都不做,的一个不可分割的工作单位,是数据库环境中的逻辑工作单位。 当一个业务逻辑需要多个sql完成时,如果其中某条sql语句出错,则希望整个操作都退回。 使用事务可以完成退回的功能,保证业务逻辑的正确性 事务语句: 开始begin; 提交commit; 回滚rollback; P.S.4: create table t_account( id int primary key, money double ) begin; update t_account set money=money-100 where id=1; update t_account set money=money+100 where id=2; commit|rollback; 表的关系:(PowerDesigner表设计软件) 一对一的关联关系,只需想办法让外键字段同时有唯一约束。外键字段在任意的表中 一对多的关联关系,只需要在多的那张表中增加一个外键字段。 多对多的关联关系,需要找一个中间表,转化成两个多对一的关系。 # 第一种方式 P.S.5:建立身份证表和人表 create table t_person( id int primary key, name varchar(255), age int ) ——身份证表 create table t_idcard( card_number varchar(18) primary key, create_date date, p_id int unique, foreign key (p_id) references t_person (id) ) # 第二种方式 create table t_person( id int primary key, name varchar(200), age int ) create table t_idcard( id int primary key, card_number varchar(18) unique, create_date date, foreign key (id) references t_person (id) )
内连接:返回两表重叠内容 语法规则1 select table1.column,table2.column from table1,table2 where table1.column1 = table2.column2; 语法规则2 select table1.cloumn,table2.column from table1 inner join table2 on table1.column1 = table2.column2 P.S.1: 列出员工姓名,工资和工资顶级编号 select ename, sal, grade from emp,salgrade where emp.SAL> salgrade.losal and emp.SAL < salgrade.HISAL 查询员工的编号,姓名,领导的编号和姓名 select t1.empno,t1.ename,t2.empno,t2.ename from emp as t1 join emp as t2 on t1.MGR=t2.EMPNO 查询所有用户的姓名,部门编号和部门名称 select t1.ename,t2.deptno,t2.dname from emp as t1 join dept as t2 on t1.deptno = t2.deptno 查看10部门员工的姓名,薪水和薪水等级 select t1.ename,t1.sal,t2.grade from emp as t1, salgrade as t2 where t1.deptno=10 and t1.sal between t2.losal and t2.HISAL 外连接 左外连接 两个表在连接过程中除返回满足连接条件的行以外,还返回左表中不满足条件的行,这种连接称为左外连接。 右外连接 两个表在连接过程中除返回满足连接条件的行以外,还返回右表中不满足条件的行,这种连接称为右外连接。 全外连接 两个表在连接过程中除返回满足连接条件的行以外,还返回两个表中不满足条件的行,这种连接称为满外连接。 P.S.2:using(字段)连接的条件,并且两个表之间的连接字段名字相同 左外连接 select t2.dname,t1.ename from dept as t2 left join emp as t1 on t1.deptno=t2.deptno select deptno,dname,empno,ename,job from dept left join emp using(deptno) 右外连接 select deptno,dname,empno,ename,job from dept right join emp using(deptno) 全外连接 select deptno,dname,empno,ename,job from dept full join emp using(deptno) P.S.3: 查询所有员工的编号,姓名,有领导的显示领导的编号和姓名,没有领导的只显示员工信息 select t1.empno, t1.ename, t2.empno, t2.ename from emp as t1 left join emp as t2 on t1.mgr=t2.empno 查询所有部门的详细信息及每个部门的平均工资,包含没有员工的部门 select t2.*, avg(t1.sal) as 每个部门的平均工资 from emp as t1 right join dept as t2 on t1.deptno=t2.deptno group by t2.deptno 子查询 语法:select 字段列表 from 表名 where 表达式 operator (select 字段列表 from 表名) P.S.4: 子查询可以在select,from,where 查询工资高于平均工资的员工 select ename, sal from emp where sal > (select avg(sal) from emp) 查询和scott同一部门且比他工资低的雇员名字和工资 select t1.ename,t1.sal from emp as t1 join (select deptno,sal from emp where ename='scott') as t2 on t1.deptno = t2.deptno where t1.sal < t2.sal 单行子查询:对单行子查询可使用单行记录比较运算符<、>、=>、=、<=、<>、!= 多行子查询:多行子查询只能使用多行记录比较运算符。(ALL和子查询返回的所有值比较、ANY和子查询返回的任意一个值比较、IN等于列表中的任何一个)
Pymysql 安装pymysql模块:pip3 install PyMySQL Connection对象: 1、用于建立与数据库的连接 2、创建对象:调用connect()方法 conn=connect(参数列表) 3、参数host:连接的mysql主机,如果本机是localhost 4、参数port:连接mysql主机的端口,默认是3306 5、参数db:数据库的名称 6、参数user:连接的用户名 7、参数password:连接的密码 8、参数charset:通信采用的编码方式,默认是‘gb2312’,要求与数据库创建时指定的编码一致,否则中午会乱码 对象的方法: close()关闭连接 commit()事务,需要提交才会生效 rollback()事务,放弃之前的操作 cursor()返回cursor对象,用于执行sql语句并获得结果 Cursor对象 1、执行sql语句 2、创建对象:调用Connection对象的cursor()方法 cursor1=conn.cursor() 对象的方法 close()关闭 execute(operation[,parameters])执行语句,返回受影响的行数 fetchone()执行查询语句时,获取查询结果集的第一个行数据,返回一个元组 next()执行查询语句时,获取当前行的下一行 fetchall()执行查询时,获取结果集的所有行,一行构成一个元组,再将这些元组组装入一个元组返回。 P.S.1: #coding:utf-8 import pymysql #创建数据库连接 connection = pymysql.connect("数据库地址","数据库账号","数据库密码","数据库名") #创建cursor对象 cursor = connection.cursor() sql = "select * from emp" #cursor执行sql cursor.execute(sql) #获取cursor执行sql之后第一行结果 emp = cursor.fetchone() print(type(emp)) print(emp) #关闭游标和连接 cursor.close() connection.close() P.S.2: #coding:utf-8 import pymysql,datetime connection = None cursor = None try: #创建数据库连接 connection = pymysql.connect("数据库地址","数据库账号","数据库密码","数据库名") #创建cursor对象 cursor = connection.cursor() sql = "select * from emp" #cursor执行sql try: cursor.execute(sql) #获取cursor执行sql之后第一行结果 emp = cursor.fetchone() print(type(emp)) print(type(emp[4])) print(datetime.datetime.strftime(emp[4],"%Y/%m/%d")) print(emp[4].strftime('%Y{y}%m{m}%d{d}').format(y='年',m='月',d='日')) print(emp[4]) print(emp) except Exception as ex: print(ex) except expression as ex: print(ex) finally: #关闭游标和连接 if cursor: cursor.close() if connection: connection.close() P.S.3: #coding:utf-8 import pymysql,datetime connection = None cursor = None try: #创建数据库连接,connection自动加入事务处理 connection = pymysql.connect("数据库地址","数据库账号","数据库密码","数据库名") #创建cursor对象 cursor = connection.cursor() sql1 = "insert into dept values(60,'leslie','上海')" sql2 = "insert into dept values(%s,'%s','%s')"%(60,'leslie','上海') sql3 = "insert into dept values(%s,%s,%s)" print(sql“1/2/3”) #cursor执行sql try: cursor.execute(sql“1/2”) cursor.execute(sql3,(70,'Jim','北京')) connection.commit() except Exception as ex: connection.rollback() print(ex) except expression as ex: print(ex) finally: #关闭游标和连接 if cursor: cursor.close() if connection: connection.close() 数据库操作代码封装 #coding:utf-8 import pymysql class MysqlHelper(object): config={"host":"192.168.0.4", "user":"root", "password":"root", "db":"test", "charset":"utf8" } def __init__(self): self.connection = None self.cursor = None #从数据库表中查询一行数据 def getOne(self,sql,*args): try: self.connection = pymysql.connect(**MysqlHelper.config) self.cursor = self.connection.cursor() self.cursor.execute(sql,args) return self.cursor.fetchone() except Exception as ex: print(ex,ex) finally: self.close() #从数据库表中查询多行数据 def getList(self,sql,*args): try: self.connection = pymysql.connect(**MysqlHelper.config) self.cursor = self.connection.cursor() self.cursor.execute(sql,args) return self.cursor.fetchall() except Exception as ex: print(ex,ex) finally: self.close() #对数据库进行增,删,改 def executeDML(self,sql,*args): try: self.connection = pymysql.connect(**MysqlHelper.config) self.cursor = self.connection.cursor() num = self.cursor.execute(sql,args) #sql语句执行之后影响的行数 self.connection.commit() return num except Exception as ex: self.connection.rollback() print(ex,ex) finally: self.close() def close(self): if(self.cursor): self.cursor.close() if(self.connection): self.connection.close() if __name__ == '__main__':
# 测试 helper = MysqlHelper() print(helper.executeDML("delete from dept where deptno=%s",80))
知识补缺:
字典创建:phone_book={'Harold':123, 'Jim':456}
字典清空:phone_book.clear()
字典追加:phone_book['Leslie'] =789
字典删除:phone_book['Jim']
字典修改:phone_book['Harold']=444
打印输出:
age=3
name="harold"
print("{0} was {1} years old".format(name, age))
print(name + "was" + str(age) + "years old")
读写文件:
some_sentences = '''\ I love learning python because python is fun and also easy to use ''' f = open('sentences.txt', 'w') f.write(some_sentences) f.close() f = open('sentences.txt') while True: line = f.readline() if len(line) ==0: break print(line) f.close()
装饰器:
def add_candles(cake_func): def insert_candles(): return cake_func() + "and candles" return insert_candles def make_cake(): return "cake" gift_func = add_candles(make_cake) print(make_cake()) print(gife_func()) '''等同于''' def add_candles(cake_func): def insert_candles(): return cake_func() + "and candles" return insert_candles def make_cake(): return "cake" make_cake = add_candles(make_cake) print(make_cake()) '''等同于''' def add_candles(cake_func): def insert_candles(): return cake_func() + "and candles" return insert_candles @add_candles def make_cake(): return "cake"
print(make_cake())
图形界面Tkinter:
# coding=utf-8 # Version:python3.6.1 __date__ = '2019/1/18 11:04' __author__ = 'Lgsp_Harold' from tkinter import * import tkinter.simpledialog as dl import tkinter.messagebox as mb root = Tk() w = Label(root,text = 'Guess Number Game') w.pack() mb.showinfo("Welcome","Welcome to Guess Number Game") number = 44 while True: # guess = int(input("Enter an integer:")) guess = dl.askinteger("Number","Enter an integer:") if guess == number: output = "猜对了" mb.showinfo("Hint:", output) break if guess < number: # print("你输入的数字小了。") output = "你输入的数字小了。" mb.showinfo("Hint", output) continue else: # print("你输入的数字大了。") output = "你输入的数字大了。" mb.showinfo("Hint:", output) continue print("Done") if __name__ == '__main__': pass
创建简单网站:
下载安装python2.7
安装lpthw.web:cmd>C:\ProgramFiles\Development\Python27\Scripts\easy_install lpthw.web
创建目录:D:\ProgramFiles\Development\PycharmProjects\python2_7_Web\bin
创建app.py
import web urls =( '/', 'index' ) app = web.application(urls,globals()) class index: def GET(self): greeting = "Hello World" return greeting if __name__ == "__main__": app.run()
cmd运行:
cd D:\ProgramFiles\Development\PycharmProjects\python2_7_Web\bin
D:\ProgramFiles\Development\Python27\python app.py