python学习笔记

Python学习笔记

windows 的反斜杠\在路径里的会起到转义的作用,可以在前面加r表示不用转义

第3节:数据和表达式
整数:int
1、Python语言里一切数据都是对象
2、1、2、3
3、Python2:int-----long(长整数)
4、32bit -2(31)—2(31)-1
64bit -2(63)—2(63)-1

 5、Python3中:只有int没有long
 type()表示;显示输入的内容的类型

浮点数float(有长度限制,精度有限):
3.14
3.0
-3.0

加法:2+1 #+号前后空格没有限制,通常是一个空格

减法:5-4

乘法:5*56

查看环境变量命令:echo %path%

除法:
python 2
8/4 #结果是2 整数
9/4 #结果是2 余数与之无关
如果想得到浮点数,则可以将除数或者被除数改为浮点数:
8.0/4 #结果是2.0

Python3:
8/4 #结果是2.0 浮点数
9/4 #结果是2.25 浮点数
8.0/4 #结果是2.0 浮点数
如果想要得到整数,则用//
8//4 #结果是2 整数
9//4 #结果是2 整数

9%4 余数1 #Python2、3都是一样的。

幂运算:
2**2 #2的2次方 结果是4

混合运算:
2 ** 31 -1 #2的31次方减1

运算符优先级:
乘除比加减优先级高,如果需要改变运算优先级,可以使用括号,如有多个优先级,可以多次加括号

常用数据类型-字符串
print(‘hello world’) #可以使用双引号、三引号、双三引号,英文引号,实际的字符串对象是没有引号的,是我们为了定义它但是一个字符串加的,如果字符串本身就含有引号,可以用:
print(’ ‘‘hello world’’ ')

字符串和数字之间是不是能做运算的
字符串拼接,可用+号:
‘abc’ + ‘dfg’ #结果是’abddfg’

除了命令交互行,其余都是需要加print的

第4节:
pycharm
不只是一个代码编辑工具
它是一个IDE:集成开发环境
先了解代码编辑和执行的功能

python中所有的数据类型都是对象,是非常泛义的概念,包括函数,类,模块,方法都是对象

从代码表达直接可以产生Python对象的叫做字面量,比如:
3
3.5
‘sgshfksh’

   变量的命名:
   1、数据变量名是一种identifier(标识符、通俗的说就是name名字)
   2、一般以字母(小写、大写均可)开头
   3、后面可以使用下划线_或者数字
   4、大小写敏感
   5、不能与关键字相同(关键字是Python语言里面的特殊字符串、如import)
   6、不要与内置函数相同 #如果后面再引用到该内置函数,后面,该内置函数就是作为一个变量了
   7、不能用减号
   
  
  变量定义的过程,就是赋值的过程。#所有的数据类型都是对象
  name='jianglaoshi'
  解释器有变量的引用计数表
    a=233,a=344
    1、没有任何变量引用的对象,会被Python解释器清除
    2、被认为是无用的
    3、何时清除、一定的策略
    4、释放内存空间。

注意:1、Python中的任何变量赋值
1、都不会修改变量原来指向对象的值
2、而是将变量指向一个新的对象而已
3、如果原来的对象有其他变量指向它,也不会改变其他变量的指向。
a*=5 a=a*5
a/=2 a=a/2

常见的几种变量的赋值:
a +=1 #即a = a+1
a -=1 #即a = a-1

在datetime模块中有一个timedelta这个方法,它代表两个datetime之间的时间差
import datetime
otherStyleTime = (datetime.datetime.now()+datetime.timedelta(seconds=+5)).strftime("%Y%m%d_%H:%M:%S") #加五秒的时间

(datetime.datetime.now() + datetime.timedelta(days=-1)).strftime(’%Y-%m-%d %H:%M’) #减一天的时间

(datetime.datetime.now()+datetime.timedelta(minutes=+5)).strftime("%Y%m%d_%H:%M:%S") #加五分钟的时间
第5节课:
字符串的特性被称为sequence(序列),所有sequence类型都支持索引和切片
单引号’
双引号"
三引号’’’ ‘’’ #换行的时候只能使用三引号,内容较多时需要换行时使用
字符串里面是’时,外面使用"。反之相反。
字符串里面既有’和"时,外面使用’’’
字符串相乘:
‘abc’*4

空字符串和空格字符串有区别:
‘’ #空字符串
’ ’ #空格字符串

字符串不可以被改变

一个序列,若干个元素组成
sequence相同的操作。

位置索引(index)
标志每个元素的位置,用来获取元素
从左到右,从0开始
a[0],a[1],a[2]
可以用正数表示,也可以用负数表示,
最后一个元素也可以是a[-1]
长度为n的字符串,最后一个元素a[n-1]

a=‘name:jack’
a[4+1] 或a[5]

含义更清楚的表示:
namelen=4
splilen=1
a=[namelen+splilen]

切片:(slice)
a=‘name:jack’
a[5:9] 从start开始到end结束,但不包括end
a[5:5+namelen] 或a[5:5+4]
a[5:]
a[:4]或a[0:4]
a[1:2]=[1,2,3] #切片的内容直接赋值

负数切片:
始终是从前往后数,可以写成’abcd’[-3:-1],不可以写成[-1:-3]

用正数下标,还是负数下标
比如,日志文件
a=11:12:11> 001 enter chatroom,level 2
把ID抠出来
做法:
a=[10:10+3] #10+3 10代表要切的id的第一个位置,3表示它的长度
或者
11:12:11 > leve25 0025 enter chatroom
做法:
a=[-18:-18+4]

第6节课
列表:list
可以存储任何类型的数据,每个元素是任意类型,所有的数据类型都是存储在里面
[]空列表

[1,2233,‘122’,‘223’]4个元素 元素之间有空格或者换行没关系,只是为了好看

元素获取(index)与切片(slice)操作
a=[‘xi’,‘li’,‘liming’]
a=[0]
a=[‘lili’,‘kkek’,[‘lili’,‘minmingd’],‘lei’]
a=[2][0]

a.extend([1,2,3,4]) #列表中添加多个元素

a.insert(index, obj) #index – 对象 obj 需要插入的索引位置,obj – 要插入列表中的对象,会在列表指定位置插入对象

a.index(b) #查找元素的索引值
a.count(‘lili’) #查找元素出现的次数
复制列表:
a.copy() #复制列表 a

反序操作:
a.reverse()

排序操作:
a.sort() #把列表从小到大排序
a.sort(reverse=True) #把列表从大到小排序

改变list的内容:
修改:a[0]=‘apple’
增加:a.append(‘huahua’)
a.remover(‘xiaoxixao’) #括号里只能是具体的值,比如‘lili’
指定位置增加:a.insert(2,‘xiaobai’)
删除列表最后一个:a.pop()
删除列表中指定元素的索引 : a.pop(1)
删除列表中的所有元素 a.clear()
指定删除元素:a.pop(1) #括号里只能是下标数字
容器的概念:
像列表这种元素可以是任何其他类型对象的对象
我们通常会形象的吧它成为"容器"。因为它像一个容器一样用来盛放其他对象。
任何变量的重新赋值都不会改变原来的对象。而是创建了一个新的对象,然后将变量指向该创建的对象
#字符串对象不可改变
元组:
元组也是一种sequence类型
可以存储任何类型的数据,每个元素是任意类型
元组不能改变其中的元素,只能获取
如果只有一个元素,后面加,如a=d,
元组:
元组也可以直接这样写:a=1,2,3,4 也可以这样赋值:a,b = 1,2
元组也支持切片和索引

https://github.com/jcyrss/songqin-testdev/edit/master/python/task/prac03.md
第7节课:布尔表达式与条件判断
布尔类型(bool) id() #查看对象在内存上的地址
真假 对错 只有两种取值
true (真,对)
false(假,错)
数值的比较:
11
1
1
1==2
是否不相等:
1 !=2
大于
<= #不能有空格

=
字符串也可以进行比较—根据内部存储的大小进行比较
字符串大小的比较按ASCII码里对应的数字来比较
例如a对应的97,b对应的98,所以a<b

in 和not in 经常用在sequence类型中
新的布尔表达式in和not in
a=(123,12,1,3,1,3)
123 in a 意思是123在不在a这个元组里面
同样适用于字符串:
‘123’ in ‘122343233444’ 判断字符是不是其中的一部分,且一部分是 连续的

布尔表达式:
1、产生布尔值的表达式
2、== !=、> 都是比较操作符
2、因为表达式可被求值,所以它可写在赋值语句等号的右侧:
a=1
var = a>1
print (var)

条件组合:逻辑运算符
and 比or 优先级高,如果需要改变这种运算方式,可以加括号,多种情况时,可以加多个括号
not 比 or 优先级高,如果需要改变这种运算方式,可以加括号,多种情况时,可以加多个括号
and 且 :如果明年精神好且明天天气好,那么去爬山 true annd false
or 或 :小明在宿舍或小王在宿舍,那么你就回去拿钥匙 true or false
not 不:如果不 明天天气好,那么不去爬山。 not true

and :只当2个都为true,结果为true
or :只当其中的一个为true,结果为true
not:把操作反过来,not true 就是false。反之相反。

第8课:流程控制
1、程序流程控制的基本方式:
条件判断
循环
2、条件语句 if开头,条件是布尔表达式,也可以是布尔值
后面有个冒号
3、仅当条件成立时才运行下面缩进区块的语句。缩进4个空格
4、然后,继续后面的语句
5、否则跳过这个区块中的语句,然后继续运行区块后面的语句。
例如:
a=1
if a>0:
print(a)
else语句
else 表示非if 条件的情况下要做的事情,不可单独使用
代码段缩进规则和if语句相同
后面不缩进的部分,继续执行
if else一起的时候,只会执行里面的一种情况,要么if, 要么else
例如:
a=1
if a>0:
print(‘hello’)
else:
pirnt(‘mngming’)

elif 语句:
更多的分支,分别处理
a=50
if a>=80:
print
elif a>=70:
print
else:
print

判断条件是 复合表达式:
如果一个人的年龄大于等于60岁,并且为男性,我们称之为老先生
age=60
gender = ‘mail’
if age >=60 and gender == ‘mail’:
print(‘old gentleman’)

如果代码过长可以用\分行
例如:
age=60
gender = ‘mail’
if age >=60
and gender == ‘mail’:
print(‘old gentleman’)

条件判断里面还可以有条件语句

\n是换行,专门作为字符串中换行,只能出现在在字符串中
\是代表其实属于是一行,但是实际看起来有换行

https://github.com/jcyrss/songqin-testdev/edit/master/python/task/prac04.md

函数
1.给一段代码取一个名字就是定义一个函数
2.函数名(参数):缩进,缩进后的内容属于该函数下的内容
3.函数执行必须是函数名+括号,表示调用该函数
函数的作用
1.就是一段代码的组合, 一般实现一个具体的功能
2.函数名就是代表一段代码的变量
3.可以实现一个个的函数,最后组合起来
4.容易理解、容易维护,整体清晰,方便调用
5.调用函数,其实就是执行函数定义里面的代码块
6.函数的定义要在函数的调用前面

函数的调用和返回
调用函数,其实就是去执行函数定义里面的代码块
函数的定义要在函数的调用前面

函数的调用参数与返回值
函数的参数
1.定义参数(形参)时的命名规则和变量一样
2.可以是一个,也可以是多个,逗号隔开
3.函数体里面像变量一样的使用参数
4.调用函数时,传入对应个数的参数(实参)
例如:def define(a,b)
调用:define(3,4)
define(a = 3,b = 4)
define(b = 4,a = 3)
define(3,b = 4)
define(a = 3, 4)错误用法

def fool(x): #x是形参
print(x+3)

fool(3) # 3是实参

另一种参数传入的方式:
关键字参数调用:
def fool(a,b):
print(a*b)

fool(a=2,b=3) 或fool(2,3)或fool(b=3,a=2)

函数的返回值
return 关键字
返回对象可以赋值给变量,也可以直接使用
def fool(x):
return x+3
print(fool(6))
可以是一个,也可以是多个
def foo2(a,b)
return a3,b4
a,b=foo2(3,4)
a,b=(1,2)
参数变量有效范围是在函数内部,优先级高于外部的变量
可以返回任意类型的对象
def foo():
return xxxx #xxxx 可以是任意类型、比如()[]{}

Python的内置(built-in)函数
Python中内置了许多函数,我们直接调用即可
print、len、max、min、
数据类型转换:
int(‘33’)

第10节:对象的方法
Python中的一切类型的数据都是对象
对象:数据+方法
对象数据:
1、对象所对应的存储的东西,是静态的部分
2、比如a=‘s2222’
对象方法:
1、可以看做对象的行为,是动态的部分
2、对象方法其实就是属于该对象的函数。
3、例如a.append(‘feefe’)

常用的方法:
查找字符串的位置:
a.find(‘e’) 注:查找’e’的位置
‘ok,good’.find(’,’,3) 注:从第三个位置开始找

count 计算字符串中包含的多少个指定的字符串 # 比如查找班上有几个叫小明
’123 123 789’.count(‘123’) #返回结果为2

endswith 检查字符串是否已指定的字符串结尾(返回的是布尔值)
’123 33789’ .endswith(‘89’)

startswitch 检查字符串是否已指定的字符串开头(返回的是布尔值)
‘185 123 789’.startswitch(‘123’)

isalpha 检查字符串中是否都是字母
‘fsfs1’.isalpha()
false

isdigit 检查字符串是否都是数字
‘12233’.isdigit()
true

str.join将元素(sequence)类型的参数的元素字符串合并(连接)到一个字符串,将
string作为分隔符 #元组和列表都可以

‘;’.join([‘i’,‘kie’,‘play’,‘football’])

split() 与join意思相反,就是切割的意思,返回的是list(列表)
‘122 334 444343’.split(’’) #括号里不填入任何内容,默认为空

splitlines(keepends) 返回一个包含各行作为元素的列表 参数keepends – 在输出结果里是否保留换行符(’\r’, ‘\r\n’, \n’),默认为 False,不包含换行符,如果为 True,则保留换行符。
str1 = ‘ab c\n\nde fg\rkl\r\n’
print str1.splitlines();
[‘ab c’, ‘’, ‘de fg’, ‘kl’]

str2 = ‘ab c\n\nde fg\rkl\r\n’
print str2.splitlines(True)
[‘ab c\n’, ‘\n’, ‘de fg\r’, ‘kl\r\n’]

lower() 将字符串里面如果有大写字母的全部转为小写字母

‘china’.islower() #是否全部为小写字母
‘china’.isupper() #是否全部为大写字母

upper 将字符串里面如果有小写字母的全部转为大写字母
‘china’.upper()
‘CHINA’

replace 替换字符串里面指定的字符串
‘Tom is dog. sonnpy is a dog ‘.replace(‘dog’,‘pig’) 全部替换
Tom is a pig.snoopy is a pig’
去掉所有的空格:’ good ‘.replace(’ ‘,’’)

strip 将字符串前置空格和后置空格删除
’ good '.strip()

lstrip将字符串前置空格删除
rstrip将字符串后置空格删除

\n :换行
\t :制表符
\b :退格符
\r :回车符,当前位置移到本行开头
\ :转义
\a :系统提示音
’ :对符号进行转义
\0 :代表一个空字符
r :对路径转义

元祖的方法只有index和count

list对象的常用方法:
空list有什么用。

a.append() #给列表添加一个元素,是向列表的尾部添加元素,只能添加一个元素
a.insert(0,3) #有两个参数,前面的0是指定的索引位置,后面的3是要插入的元素,插入元素后,原先处于该位置的元素往后移动
a.extend() 函数用于在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
extend()方法语法:
list.extend(seq) #是一个序列
aList = [123, ‘xyz’, ‘zara’, ‘abc’, 123];
bList = [2009, ‘manni’];
aList.extend(bList)
[123, ‘xyz’, ‘zara’, ‘abc’, 123, 2009, ‘manni’]

从列表中删除一个元素:
del a[1] 参数是索引值


a.pop() a.pop(1) 参数是索引值,有返回值,返回的就是删除的值


a=[1,2,4,3,4,5]
a.remove(4) 注意:参数是删除的元素名字
print a
[3,1,2,4]
注意:根据值删除,但是这种方法要注意的是,如果列表里有多个元素都是这个值,那么一次只能删除第一个元素

以上三种删除方式,哪种性能最高,如果知道某个元素的位置最好用del或者pop,remove是从第一个开始找该元素,所以性能稍低

reverse,将列表里面元素倒序排列
a=[1,2,3,4,5]
a.reverse()
print a
[5,4,3,2,1]

删除一整个列表,可以赢a.clear() 或者del[:]
所有的方法都是圆括号
学会查看文档
谷歌、bing
https://github.com/jcyrss/songqin-testdev/blob/master/python/task/prac05.md
https://github.com/jcyrss/songqin-testdev/blob/master/python/task/task01.md

第11课:字符串格式化
字符串的格式化
name=‘lili’
height=170
print(‘我叫%s,身高%scm’ % (name,height)) #%后面可以是一个变量也可以是字符串,多个变量用()
提高可读性

字符串的格式化
format % values
format 是等待格式化的字符串
由包含%号的占位符的字符串组成的

values
values 可以是普通的数值对象、字符串对象
tuple,表示多个对象填充format里面的占位符%

%s :用str()函数进行字符串转换
%d :转成有符号的十进制数
%f :转成浮点数
%x :转成无符号十六进制数

指定宽度:(%)
‘%d’ % 56
‘%10d’ % 56 # 最小宽度,不足空格补齐 #右对齐
‘%-10d’ % 56 #左对齐
‘%010d’ % 56 #补零

十六进制:
“%x" % 108
“%X” %108 #大写
“%#X” % 108
“%#x” % 108 结果:‘0x6c’ 表示十六进制

小数:
‘%f’ % 1234.56789
‘%09.2f’ % 1234.56897 前面补零

字符串的format 方法:
使用format()内置函数格式化每个参数
‘my name is {}’.format(‘lili’)
可以指定位置填充:
‘{1}-{0}|{0}-{1}’.format(‘lili’,16)#要么都填充数字,要么都不填
参数名方法:
‘my name is {name}’.format(name=‘lili’)

指定宽度:(format)
‘{}’.format(56)
‘{:10}’.format(56)
‘{:<10}’.format(56) #左对齐
‘{:>10}’.format(56) #右对齐
‘{:10}’.format(56) #不足补充空格
‘{:<010}’.format(56) #左对齐不足补充0
’{1}-{0}|{}-{0:>010}’.format(‘lili’,16)

指定宽度:十六进制
‘{:x}’.format(56)
‘{:X}’.format(56)
‘{:#x}’.format(55)

指定宽度:小数
‘{}’.format(56.2145)
‘{:06.2f}’.format(15.12145)
‘{:.2f}’.format(15.255) # 如果字符串本身就有花括号,需要再花括号上面再加一个花括号,例如{{}}

Python 3.6以后
name =‘lili’
f"he is name is {name:<10}"

字符串里面的转义符:
print(‘jack sid ‘hello’’)
\n 表示换行
print(r" \fe\fefe\fee\fef")

字符终端的输入:
input #input输入的是一个字符串对象,不是数字对象,所以遇到数字做比较,应使用int进行转义
input(‘请输入你的姓名:’) #可以赋值给一个变量
name = input(‘请输入你的姓名:’)
用户输入后按回车键就表示结束

第12节课:循环
流程控制:循环和条件判断
流程离不开循环
while 循环:
当某个条件成立时,一直执行某个动作
比如:老师对学生说,9:00以前,你们反复朗读 课文
while循环的语法:
while条件表达式
循环代码
条件表达式结果为true的时候,循环代码会一直执行,直到条件表达式结果为false

死循环:
while true
print(’hello,world)
永不退出,直到进程被杀死
注意不要写出错误的死循环
有的死循环是正常的
Ctrl+C终止死循环
在shell 和GUI消息处理循环中是正常的

for 循环代码
student=[‘lili’,‘mingming’,‘xiaoxiao’]
for one in student:
print (one) #one 习惯就好,也可以用其他定义。

for 循环

使用场景:
从list 、tuple 里面获取每个元素进行处理

已知固定的循环次数:range\xrange

python 2 range xrange 二者的区别:
range 函数返回的是一个列表,而xrange像一个生产器,
如果需要遍历一个很大的数字范围,比如1-100w,用xrange,因为节省内存
Python 3 range 类似Python2 的xrange

range 的补偿值:for i in range(1,222,3)# 3是补偿值,也就是间隔值,也可以是负数如(1,2000,-3)

break语句:
执行到break语句时,跳出循环,不再执行。适用while和for循环

break、return、continue的区别
return只能用于函数中,和函数有关系,和循环没有关系。
函数中只可以用return,break可用于函数和循环中
break 只是跳出循环,如果后面有内容,继续执行,return是从整个函数中返回,后面不再执行。
continue语句:#跳出当次循环,后面的代码不执行,但是后面的循环继续执行。(break是打所有地鼠,continue是不打当次地鼠,打其他地鼠)

注释:
Python的注释来帮助理解代码
可以用中文、英文、日文
它对代码的执行不起任何影响
虽然Python的抽象级高,易理解,仍然大段代码需要表明意图
注意点需要标志,(比如一个变量的主要用途等)
团队开发,别人看你的代码
自己看,过了一段时间。

:表示整行注释

跟在代码后面注释 #
整行注释:
##############################################################################################
#Apple
##############################################################################################
当注释内容多的时候
‘’‘sdfsdfdsfsdfsdfdsfsfs
‘’’

本身是字符串,没有赋值给任何变量,可以当作注释用。
作业地址:
练习内容见该链接
https://github.com/jcyrss/songqin-testdev/blob/master/python/task/prac06.md
另外有一道编程题,内容见下面链接
https://github.com/jcyrss/songqin-testdev/blob/master/python/task/task02.md
22

文件对象
不同的类型 会产生不同类型的对象
1.整数类型的对象
2.字符串类型的对象
3.字典类型的对象
4.元祖类型的对象
5.布尔类型的对象
6.range类型的对象

文件的读写通过文件操作对象进行
python2 叫ile
python3 叫TextIOWrapper
内置函数open打开文件,获取文件操作对象

open的参数
file_object = open(filen_name,access_mode=‘r’)

access_mode=‘r’ #表示以只读的方式打开,如果不写该参数,也表示以只读的方式打开

file_name
文件路径:相对路径和绝对路径
access_mode
读(定义时等于号指定的值,缺省参数)

读+写

相对路径
什么时候当前工作目录:每个运行的进程,都有个当前的工作目录,在cmd、shell中打开,就是shell当前所在目录,比如:
进入到目录E:\python+selenium+robotframe\python 该目录下有file1.txt文件,即file1.txt就在当前目录,可以直接打开,比如:
open(file1.txt)
也可以用另外一种方式打开,比如:
open(r’.\file1.txt’) #.即表示当前工作目录

如果进程操作文件,不是全路径,比如:
想打开python+selenium+robotframe的file2.txt文件,则可以在E:\python+selenium+robotframe\python目录用:
open(r’…\file2.txt’)

…/…/表示上级目录的上级目录,以此类推
相对于当前目录来寻找该文件称之为相对路径

1 如需对’‘转义,则可以在前面加上r,例:open (r’…\hello.py )或者还可以使用两个’\’ 表示转义,即表示后面的’'即为斜杠
windows下面的文本文件的换行符通常是:\r\n,所以是两个字符

文件指针的概念

可以把文件理解为一长串字符串,只是存在一个文件里

当使用open()打开文件的时候,底层系统会为我们创建一个文件指针

告诉操作系统是从什么位置开始读取该文件文件对象的tell 方法,即可以获取文件指针的位置

<1>
只读打开文件:
open(‘file’,‘r’) --file代表文件名称,r代表打开方式,可以不写,不写的话缺省为r,即以读的方式打开,文件指针在文件的开头,这也是缺省的文件打开方式

<2>tell() 默认是从文件开始位置读取 是文件对象的方法

<3>read(2) 参数‘2’即是指读取文件两个字符,如果不加参数即表示从当前指针的位置一直读取到末尾

指定文件指针的位置可以用seek()方法
<4>例如:seek(2) 即表示从文件的开头移动到第二个字符处
seek(2,1)1表示从当前指针文件开始往后移动两个位置字符
seek(-1,2)表示从末尾往前移动一个字符
在python3中,后面三种方式,一定要以二进制的方式打开文件:open(‘file’,‘br’)

<5>关闭文件:
fh.close() 关闭后不能再read 和 tell了,关闭文件后重新打开文件文件指针还是从开头开始读

读取文件
<6>
read:读取整个文件

readline:一行一行的读取,返回的是一行字符串
例如:fh.readline() 结果也包括换行符\n ,换行符\n代表一个字符,以文本方式打开就是自动转化成\n,如果以二进制方式打开就会将\r打印出来
例如:fh = open(‘abc’,‘rb’)
str1 = fh.readline()
str
b’1234567\r\n’

<7>readlines:返回的是一个列表,将文件全部读取,列表中每个元素就是文件汇总的每行内容,以列表的形式一行一行展示,如果有换行每一行后面会带有’\n’,如果需要去掉,可以用切片,也可以用splitlines 用法如下:
content = fh.read() #content返回的是一个字符串
content.splitlines() #用字符串方法splitlines()可以将\n去掉

<8>以写的方式打开文件,写的方式打开,指针都在末尾,写文件进去,用法:
fh = open(‘flle’,‘w’) 但是会将原来的内容清空,如果没有该文件则会新建一个文件

f = open(‘lx.txt’,‘w’,encoding=‘gbk’)
fh = f.write(‘孩子你是最棒的’)
写文件是先将文件写到缓冲器,然后再写到磁盘,如果想立刻写到磁盘上,可以再用f.write()后用fh .flush()

<9>以在文件末尾追加内容打开文件的方法,就不能用w方式打开,用法:
fh = open(‘file’,‘a’)
以该方式打开时,如果文件存在,文件指针在文件的末尾,如果文件不存在,则创建一个文件,很多OS强制写在末尾,不管文件指针被seek到了什么地方

<10>还有其他读写发打开方式:
r+ #又读又写,为了读取并且写文件而打开文件,如果文件不存在,会报错,文件指针在文件的开头
w+ #又读又写,为了读取并且写文件而打开文件,如果文件不存在,会创建一个文件,如果文件已存在,其内容将被清空,文件指针在文件的开头
a+ #又读又写,为了读取文件并且写文件而打开文件,如果文件不存在,会创建一个文件,文件指针在文件的结尾,很多OS上写操作永远会在文件结尾进行,不管是否用了seek

用open方式打开,需要用fh.close()关闭文件,否则会引起一些问题

为了防止忘记关闭文件,可以用打开文件的另一种写法
with open:执行结束时,系统自动调用f.close,不需要再关闭文件了,用法如下:with open(‘file’,‘r’) as f:
fh = f.read()

执行结束时,系统自动调用f.close(),不需要再执行f.close()
支持多个文件的打开
例如:with open(filename1) as file1,open with(filename2,‘w’) as file2:
fh = file1.read()
file2.write(fh)

24
1 循环嵌套
<1>用编程语言来表示:
boys = [‘jack’,‘mike’,‘jin’]
girls = [‘linda’,‘lisa’,‘lucy’]
for boy in boys:
for girl in girls:
print (’%s shakes with %s’ % (boy,girl))

当循环里有循环时,先执行完里层的循环再执行外层的,处理所有的可能性组合,debug可以看到执行的全过程,boy先指向’jack’,girl先分别指向’linda’,‘lisa’,‘lucy’执行完了,再执行外层的boy指向’mike’,以此类推。

用途:列出所有的可能性组合
先从外层循环里面取出一个元素,再执行内层的循环
当内层的循环都执行完后,再继续执行外层循环

列表生成式:从源列表里面一次取出元素,做同样的处理放入另一个列表中

员工的税前工资列表[10000,15000,8000,4000,5000]
每个员工扣税10%,请计算出所有员工的税后工资,存储在列表中

beforetax = [10000,15000,8000,4000,5000]
aftertax = []
for one in beforetax:
aftertax.append(int(one*0.9))
print(aftertax)

列表生成式方法:
aftertax = [int(one0.9)for one in beforetax]
print(aftertax)
如果想生成一个元祖,可以这样:
beforetax = (10000,15000,8000,4000,5000)
aftertax = tuple([int(one
0.9) for one in beforetax])
print(aftertax)

其他用法:加上过滤条件或者运算
aftertax = [one0.9 for one in beforetax if one >=10000]
aftertax = [(one
0.9) -3+6]

2算法
有些处理过程需要逻辑思维能力
比如,将给定列表中的元素,从小到大排列,不用sor()方法

可以用冒泡排序方法
def bubble(alist):
for j in range(len(alist)-1,0,-1):
for i in range(0,j):
if alist[1] > alist[i+1]:
alist[i + 1],alist[1], = alist[i],alist[i+1]
return alist

a = [3,5,7,2,56,34,54,23,21,56,33,2]
print(bubble(a))

3 if判断条件还可以简写
x = 9
if x:
print(‘x is not zero’)
只要x是非零数值、非空字符串、非空List等,就判断为True,否则为False,相当于if x !=’’

4 pass 关键字 定义一个空语句,编码时定义了函数名,还没想好实现方式,可以用pass

def meth_a(self):
pass(表示此处有一行语句)
5 函数里面调用其他函数
例如:def foo():
print(‘in foo()’)
bar()

25
字典的定义
1 怎么存储信息
存储学生的信息:姓名、年龄、身高、体重、昵称
<1>字典的定义
dict1 = {}
dict2 = {'key1: ‘value1’,‘key2’:‘value2’} #每个元素都有两个数据对象

key,value的概念

dict = {‘name’:‘jack’,‘age’:18}
print(dict[‘age’])
print(dict[‘name’])
就像真正的字典前面的索引
这种方式访问不存在的key会导致程序异常
字典中不允许存在两个一样的键,如果新增新的会覆盖前面一个

<2>还可以使用get的用法

dict2.get[‘name’]
dict2.get[name333,None]这种方式获取值如果没有name333,那么就返回None

<3>字典的特性:
可变的,可以存储任意数量的元素
可以存储任何python的数据类型

key可以是hash类型,最常用的,最高效率的是数字或者字符串,字典本身就是非hash的

value可以存储任何Python数据类型

如果key重复,后面的key的value会覆盖前面的value
根据key查找值的效率非常高!
list、string、tuple特性称之为sequence
dict特性称之为map(映射)

判断一个字典里面是否有某个键值,可以用:
dict = {‘name’:‘jack’,‘age’:18}
d = ‘name’ in dict
print(d) #d是一个布尔值

删除字典:
dict = {‘name’:‘jack’,‘age’:18}
del dict[‘name’]
print(dict)

或者
dict = {‘name’:‘jack’,‘age’:18}
dict.pop(‘name’) #有一个返回值,即键的值
print(dict)

字典可以进行遍历,用for循环
dicts = {‘jack’:{‘age’:18,‘weight’:125},‘lucy’:{‘age’:19,‘weight’:100}}

for name in dicts:
print(name) #获取的是key

更简便的方法:
for name,info in dicts.items():
print(name) #获取Key
print(info) #获取value

字典也可以用len()获取元素个数
dicts = {‘jack’:{‘age’:18,‘weight’:125},‘lucy’:{‘age’:19,‘weight’:100}}
print(len(dicts))

还可以:
dicts = {‘jack’:{‘age’:18,‘weight’:125},‘lucy’:{‘age’:19,‘weight’:100}}
print(len(dicts[‘jack’]))

清空字典
dicts = {‘jack’:{‘age’:18,‘weight’:125},‘lucy’:{‘age’:19,‘weight’:100}}
dicts.clear()
print(dicts)
或者对字典重新赋空值
dicts = {‘jack’:{‘age’:18,‘weight’:125},‘lucy’:{‘age’:19,‘weight’:100}}
dicts = {}
print(dicts)

常用操作
得到所有的keys返回在类list中
d.keys()
得到所有的value返回在类list中
d.values()
得到所有的(key,value)返回在类list中
d.items()

增加dict的内容
dicts = {‘jack’:{‘age’:18,‘weight’:125},‘lucy’:{‘age’:19,‘weight’:100}}
c = ‘hha’
e = ‘wrwr’
dict.update({c:e})
print(dict)

26
变量的作用域
<1>函数的内部变量称之为局部变量(local),函数外面的变量称之为全局golbal)如果变量名称相同,函数优先取局部变量,没有定义局部变量时,即取全局变量,局部变量只在局部内有效,外面无法调用,函数参数其实就是局部变量
<2>修改全局变量而不是重新赋值),用法如下:
x = 2

def fun():
golbal x
x = 9
print(x)

fun()
print(x)

声明修改两个全局变量用法,global需要放在重新赋值前面:
x = 2
y = 3
def fun():
global x, y
x = 9
y = 5
print(x)

fun()
print(x)
print(y)

缺省参数
<1>调用的时候不写明具体参数,就使用缺省参数,没有缺省值的参数就是必填参数,缺省参数可以定义多个:
def student(a, b=1,c=2,d=3):
student(18) #18传给a ,其他均使用默认参数使用,如果b、c、d指定了的话就用指定值

没有给默认参数的即必填参数
定义函数的时候一定要必填参数在前,缺省参数在后
缺省参数使用非常广泛,内置的库和第三方库里面大量地使用缺省参数

可变数量参数

def calc(numbers): #只能传一个参数,如果需要传多个,只能用元祖或者列表

但是用可便数量参数更方便,例如:
def calc(numbers):
total = 0
for n in numbers:
total = total + n
n
return total
calc(123)

参数展开
如果参数已经在一个list或者(tuple)中,怎么调用
nums = [1,2,3]

可以这样:
calc(nums[0],nums[1],nums[2])

如果有更多的参数就不方便,可以这样:
calc(*nums)
等价于calc(nums[],nums[],…,nums[n-1])例如:
def calc(numbers):
total = 0
for n in numbers:
total = total + n
n
return total

nums = [1,2,3]
print(*nums)

关键字可变数量参数:允许在调用函数时,传入任意个含参数名的参数

def student(name,age,**kargs):
print(‘name:’,name,‘age:’,age,‘other:’,kargs)
除了必选参数name和age外,还接受关键字参数kargs 传入的参数格式是 a = b,也可直接用定义的变量字典传入,例如:
def student(name,age,**kargs):
print(type(kargs))

student = {‘city’:‘beijing’,‘job’:‘Engineer’}
student(‘mike’,18,**student)

综合起来
必填参数、缺省参数(非必填)、可变参数(非必填)和关键字参数(非必填)一起使用
def hha(a,b,c= 0,*k,**hh):
print(‘a=’,a,‘b=’,b,‘c=’,c)
print(‘k=’,k,‘hh=’,hh)

hha(1,2,c=9) #注意一旦有参数比如以c=9传入,后面传的参数一定全部要按照这种格式,不然会报错

可变参数(非必填)和关键字参数(非必填)一起出现时,关键字参数一定要在可变参数后面

模块和包
<1>
一个.py文件就称之为一个模块(module)

把许多模块按照功能放到不同的目录中来组织模块,这些组织存放模块文件的目录,我们称之为包(package)

模块化的其他好处:
以库的形式封装功能,方便给别的代码调用:
库其实就是模块和包,可以使用自己写的库,Python标准库,第三方库
避免变量名冲突(包括函数名)
如果一个代码文件特别大,变量的名字容易发生重复
需要想出不同的变量名或者函数名
如果采用模块分割代码,每个模块文件代码都不是很多,就可以缓解这个问题
每个模块中的变量名作用域只在本模块中

模块的定义
<1>
模块就是一个Py文件,模块名就是Py文件名,不包括.py,模块中的标识符比如变量、函数、类都可以供外部使用

<2>
使用模块里面的方法一
import module:导入单个模块,模块在python中也是一个对象,import的模块的里面的内容先执行
import module1,module2,module3:导入多个模块
导入模块还可以取别名:import module as newname

使用模块里面的方法二
from module import 变量名1、函数名1
这种方法也适合用别名,例如:
from module import var as newname
长名字缩短
防止同名

from module import * 从某模块导入
所有的变量、方法,但是需要慎用,一般不建议使用,潜在的污染名字空间的危险
但是这种方式在使用其函数或者变量的时候可以直接使用,前面可以不用加模块名称.函数名

包(package)
<1> 存放文件目录的称之为包,其实就是目录
包下面的_init_.py文件的作用就是调用该包时起到初始化作用,可以是空文件,建议大家将py文件放到一个包里面的时候,带上这个文件,名字固定
<2>调用包内模块的方法
方法一:
import Phone.Mobile.Analog
调用的时候一定要写全,如下:Phone.Mobile.Analog.dial()

也可以取别名,比如:
import Phone.Mobile.Analog as abc
abc.dial()

方法二:
from Phone.Mobile import Analog
Analog.dial()

方法三:
from Phone.Mobile.Analog import dial
dial()

如果import一个包时,那么调用的就是其初始化文件_init_.py的方法或者变量

Python标准库:
安装包里面的提供的功能 模块和包,主要包括:
内置类型和函数:比如Len、int、open,直接使用,无需impotrt功能

功能模块:包含不是内置类型,但也在Python中的,程序设计所需的常用的功能,需要用import导入它们就可使用

例如:import time、import os
time.strtime(’%Y_%m_%d %H:%M:%S’)
os.system(‘calc’)

import 或者from……import时,解释器如何找到模块文件的?
解释器程序直接包含的,无需寻找,比如:sys

命令窗口
import sys
sys.bulitin_module_names 查看内置的模块,不需要到磁盘上去找的

sys.path #返回的是列表,如果不是内置的模块的话,解释器就用该列表来寻找模块文件路径
[’’, ‘C:\Python36\python36.zip’, ‘C:\Python36\DLLs’, ‘C:\Python36\lib’, ‘C
:\Python36’, ‘C:\Users\Administrator\AppData\Roaming\Python\Python36\sit
e-packages’, ‘C:\Python36\lib\site-packages’, ‘C:\Python36\lib\site-packag
es\win32’, ‘C:\Python36\lib\site-packages\win32\lib’, ‘C:\Python36\lib\
site-packages\Pythonwin’]

#’’ 代表当前工作目录 其他的都是标准库里的目录,安装好Python解释器之后就有了,先到当前工作目录找这个模块,如果找不到再依次往后找

pythonpath 指的是环境变量里包含的目录,这是一个环境变量设置,python解释器在运行时,会把该环境下的目录加载到sys.path下面
如果想在命令行里调用包下面的模块文件的方法或者变量,可以通过设置PYTHONPATH环境变量来找到该模块,在命令窗口选择项目所在目录执行:
set PYTHONPATH = 该模块文件所在目录,再执行该文件
该命令只在设置的当前命令窗口有效,换了一个终端无效
或者可以设置一个run.bat文件,将命令放在该文件内,再执行run.bat文件,文件中命令如下:
set PYTHONPATH=E:\python+selenium+robotframe\python\phone\apple
python py7.py

还有一个办法是在当前需要导入其他模块的py文件中将需要导入模块的文件路径添加到sys.path,例如:
import sys
sys.path.extend([r’E:\python+selenium+robotframe\python\phone\apple’,
r’E:\python+selenium+robotframe\python\phone\samsung\note’,
r’E:\python+selenium+robotframe\python\phone\samsung\s’])

print(sys.path)
import iphone6,iphone7,galaxy_note8,galaxy_s7

iphone6.askPrice()
iphone7.askPrice()
galaxy_note8.askPrice()
galaxy_s7.askPrice()

安装第三方库
可以使用pip安装,类似yum、apt—get、npm
pip install 第三方库名
有时可以使用国内豆瓣的源
pip instal Djago - i https://pypi.douban.com/simple/

字符集是用来表示语言符号的数字的结合,注意这只是叔叔(code point),不涉及用什么方法来存储数字

我们常见的字符集
ASCII 字符集 #美国人定义
Unicode #国际标准化组织定义的,用意是囊括全世界的语言符号
gb2312、gbk、gb 18030 #中国政府定义的,主要是针对中文字符定义的一套规范,兼容并包括ASCII字符集

字符编号和解码:
字符是以字节的方式存储和传输的,一个字节是由八个比特组成的,一个比特位只能存储二进制的一位,而二进制的一位上只能存储两个数字0或1,那么一个字节号最多可以存储0-255个数字,共计有256个可能的数字,一个字节是4k

字符编码是指吧字符串用字节序列(字节串)表示的方法

UTF8是专门针对Unicode的一种编码规范,用的较多,目前世界上最主流的,例如:
中文字符‘这’ 在Unicode中对应的数字是36825(换算成16进制是0x8fd9), 通过UTF8编码后变成E8BF99(16进制)
Python 内置函数ord()查看一个字符对应的Unicode数字
hex()可以查看该数字对应的16进制表示方式

我们常见的字符编码方式有:
ASCII码
UTF-8
UCS2 UCS4(UTF-16 UTF-32) #前者固定用2个字节,后者固定用4个字节来存储
gb2312、gbk、gb18030 (Extended Unix Code - EUC) #类似UTF8编码规则

python3 字符串默认是Unicode数字来表示

python3的编码:
从Unicode字符串编码为指定格式的字 节串:bytes对象,例如: ‘你好’.encode(‘utf8’)就会将其编码为 utf8的字节串

python3的解码:
从字节串(bytes对象)解码成字符串对象,例如:
byteVar = ‘你好’.encode(‘utf8’)
byteVar.decode(‘utf8’)

python3解释器缺省使用utf8 码来解码代码文件
python2解释器缺省使用ASCII 码来解码代码文件

声明代码文件编码方式:
在代码的第一行或者第二行显式的加上一个注释# coding=utf-8(等号前后不可有空格)指明代码文件的编码方式
如果是其他编码方式,比如gbk等,必须正确的指明# coding=gbk

也可以用这种方式:
# coding:gbk

终端输出中文:
终端里面执行下面的程序,来打印文字符到屏幕上print(‘中文’)
我们输入的字符是用utf8编码的
而’中文’两个utf8编码0xe4b8ad0xe69687
执行时,解释器将字符串内容解码为Unicode
输出给终端设备,必须将unicode编码为一种编码格式的字节串

输出到文件
Python3:
python 命令:import locale > locale.getpreferredencodig()查看当前操作系统使用的语言设置

如果open()参数里encoding没填,encoding只针对以文本模式打开,就要看当前操作系统的语言设置,如果当前是中文,那么默认就是以gbk编码方式打开

打开中文文件名:
windows目前最流行的ntfs文件系统而言,文件名都是用utf-16(有的文档说是Unicode)编码
with open(‘中文.txt’,encoding=‘utf8’)as f:
print(f.read())

Pychaem 作用:

project视图、代码结构视图
代码导航
语法高亮、自动补齐、错误提示、自动修复
代码重构
主流开发框架的支持(Django)
集成版本控制(git、svn)
单元测试
图形界面的调试功能

查看类、函数、方法在哪里被使用了:

点击类、函数、方法,选择find usage,找到后在需要修改的文件中修改,但是有的不是.py文件是找不到这些类、函数、方法的,这种可以用search in folder

点击项目名称,选择find in path,输入要查找的内容

搜索字符串
当前文件
指定文件夹查看

Python调试程序
step over : 如果有函数,则不会跳到函数里面去执行
step into:会进入到函数里面执行
step into mycode:不进去库函数,比如split
step out :一下子执行完整个函数,退到刚才调用的地方
run to cursor : 跳到光标指定的地方
resume program :恢复执行
面向对象:面向对象是软件设计的一种重要理念和方法, 如果使用该方法,那么设计出来的系统更容易理解和维护,python就是一种面向对象的语言

面向对象的设计和实现:
定义和实现对象可以直接对应现实世界里面的事物
面向对象的设计(00D -Object-Oriented Design):
分析要解决的现实世界的问题
定义出各种对象类型代表现实世界的对象
通过设计对象之间的关系和交互行为来设计系统
因为:
现实世界其实就是各种对象和对象之间的关系、交互行为
现实世界:事物的世界
物:对象、对象之间的关系(儿子、父亲;老板、员工)
事:对象之间的交互(厨师烧饭,人开汽车)
这种设计方式更容易被人所理解和接受

寻找对象:
首先,寻找要设计的系统里面的对象有哪些:
老虎

房间

类:
接下来,确定对象的定义:
对象的定义:定义对象在这个系统里面的属性和行为
比如老虎这类对象,在这个系统里面共有的属性,我们基本上一下就能想到的有体重(缺省200斤)、名称(tiger)。而行为呢,有叫唤(roar),有吃食物(eat)

00D软件设计把对象和它的属性和行为的定义叫类的定义,所谓类就是指的某一类对象所公有的特征,注意是一个抽象的对象
类表示了这类对象所共有的属性和行为
类是00D最基本的概念
类是00D中同一种类型对象的书面定义
定义了类,就定义了对象类型(包括在系统里面的属性和行为)

实例是类的具体化存在:
老虎这个类,描述了:
在这个系统里面共有的属性:体重(缺省200斤)、名称(tiger)。
行为有叫唤(roar),有吃食物(eat)
每个具体的老虎,是老虎这个类的实例
比如游戏中房间1里面的老虎

类和实例的关系有些像模具和根据模具造出来的东西一样

00D VS 00L
面向对象的语言 VS 面向对象的设计
面向对象的设计:是一种设计思路
面向对象的语言:提供对面向对象设计的语言支持
非面向对象的语言也能实现面向对象的设计思路

Python的类定义:
class ClassName:

关键字class+类名(通常首字母大写):
class Tiger:
classname = ‘tiger’

属性(attribute):classname 是一个属性

实例化:
t1 = Tiger() #返回一个Tiger类的一个实例对象,赋值给变量t1

实例化属性
t1.classname

静态属性和实例属性:
静态属性(类属性):#如果该属性改变的话,那么该类的所有静态属性都会改变,是有类的实例公有的属性
老虎的名称(该类的所有实例共享的属性)不需要实例化就有的属性,没有实例的时候也是可以使用的

实例属性: #每个实例独有的属性,可以指向相同的实例,也可以指向不同的实例
老虎的体重(每个实例独有的属性),有胖有瘦
通常定义在初始化方法__init__里,初始化是类的方法,就是属于类的行为,如果定义了初始化方法时,那么实例化的时候就会自动调用该方法
self的概念就是变量,参数就是变量,就是实例化好的对象,如下和t1指向同一个对象,weight=200是一个初始化参数,不传的话就默认使用该参数,self.weight = weight就是实例属性,self不需要传参,解释器会帮我们传,t1 = Tiger()就只需要传除了self以外的参数

例如:
class Tiger:
classname = ‘tiger’

def __init__(self,weight=200):
    print('haha,in __init__')
    self.weight = weight

t1 = Tiger()
t2 = Tiger(500)
print(t1.weight,t2.weight) #实例属性可以通过实例访问:

静态属性可以通过实例访问:

class Tiger:
classname = ‘tiger’

def __init__(self,weight=200):
    print('haha,in __init__')
    self.weight = weight

t1 = Tiger()
t2 = Tiger(500)
print(t1.classname,t2.classname)

静态属性也叫类属性,所以也可以直接通过类访问静态属性,所以可以:
class Tiger:
classname = ‘tiger’

def __init__(self,weight=200):
    print('haha,in __init__')
    self.weight = weight

t1 = Tiger()
t2 = Tiger(500)
print(Tiger.classname,Tiger.classname)

但是不可以通过类访问实例属性,不可以Tiger.weight

静态方法和实例方法:
对象的方法是描述对象相关行为的
通过定义在类里面的函数
比如初始化方法__init__函数

类的方法包括:
实例方法:每个具体实例相关的方法,访问某个具体实例属性,如果不需要操作任何实例属性,那么建议做成静态方法,操作实例方法一般创建、修改、删除某个实例属性的值,初始化方法通常就是一个实例方法,初始化方法里面也可以不创建任何实例属性,但百分之八九十都会创建一些实例属性,self代表实例对象,创建实例属性可以用self.属性名,通常写self参数,当然也可以写其它名字,除了初始化方法,还可以定义其他方法,可以在类里面定义另一个函数,参数都要带上self

静态方法:公有的方法,与每个具体实例无关,定义静态方法就要加上@staticmethod 修饰(装饰器),静态方法是不允许访问操作实例属性的,可以访问静态属性
例如:
class Tiger:
classname = ‘tiger’
@staticmethod
def roar():
print(‘wow’)

@staticmethod
def tighername():
    print(Tiger.classname)

通过一个例子看下静态属性、实例属性、静态方法、实例方法:
class Tiger:
classname = ‘tiger’
@staticmethod
def roar():
print(‘wow’)

@staticmethod
def tighername():
    print(Tiger.classname)

def __init__(self,weight=200):
    self.weight = weight
    print('my weight is %s' % self.weight)

def TellHeight(self,height=100):
    self.height = height
    print('my hight is %s' % self.height)
    return self.height

t1 = Tiger(500)
不可以通过类直接调用实例方法,比如:Tiger.TellHeight(),除非参数传入实例对象,Tiger.TellHeight(t1),不建议这样写。

除了静态方法和实例方法,还有类方法,类方法和静态方法有相似之处(不允许访问实例属性),也有不同之处,但是缺省传入类对象,用的不多,了解一下

对象的组合:
现实世界里面的对象
大对象里面有小对象
比如人这个对象可以有头、腿、胳膊、躯干
这就形成了组合关系:
人拥有(包含)头、腿、胳膊、躯干
头、腿、胳膊、躯干组合成了人

00D里面对象也是可以互相组合的:
房间对象包含老虎、羊
通过对象的属性表示这种组合关系的
在类定义的初始化函数里面表示对象的组合关系
class Room:
def init(self):
self.num = 1
self.animal = Tiger(300)
用类的属性指向另一个对象表示对象的组合关系
表示:Room对象拥有一个animal对象
房间对象由老虎对象和其他对象组成(这里是房间号,一个int对象)
这就是对象的组合

获取随机数
from random import randint
print(randint(0,1))
print(randint(1,10))
获取当前系统时间
import time
curTime= time.time()
print(curTime)
或者
from datetime import datetime
curTime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(curTime)

对象的继承(非常重要)
现实世界里面的对象:
对象是从属大类和小类的关系:
门、纲、目、科、属、种,比如人类:脊索动物们>哺乳科>灵长目>人科>人属>智人种
桌子>电脑桌
虎>华南虎>白纹华南虎
后面的对象继承前面的对象,前面的对象不一定有后面的特征,后面一定有前面的特征
拥有前面对象抽象的一切特征,可以直接说后面对象就是前面的对象,比如华南虎就是虎

Python里面对象的继承
class SubClassName(ParementClass1[,ParementClass2,…]):
class_suite

括号里面的是被继承的类,叫做父类(或者基类),SubClassName是继承类,叫做子类(或者继承类)
父类可以有多个(多重继承):
中国学生:中国人、学生(父类)
它同时拥有多个父类的所有属性
表示:子类对象一定是一个父类对象

游戏里面的例子
父类:老虎
子类:
东北虎,跳起来有3米高
华南虎,跳起来有2米高

实际好处:代码重用
通常子类初始化方法是必须要调用父类的初始化的方法的,可以调用多个父类

重定义
子类的属性和行为和父类有些不一样 #在子类属性里面重新定义,方法也可以重新定义
比如:重新定义东北虎的叫声,和它的名称
子类实例调用这些属性、方法会使用新的定义
多态:同样的方法调用,在不同的类型的对象上表现出不同的特征,就称之为多态
38
1 什么是异常:代码执行产生错误,无法继续
a = 8950/0
print(a)

2 异常的概念:
当解释器遇到错误的(无法执行)代码
中断当前代码的执行
解释器会抛出一个异常对象

如果没有任何异常处理
例子:
输入0会导致当前程序的异常退出
while True:
num = input(‘input a number:’)
print(‘10000 / %s = %s’ %(num,10000.0/int(num)))

希望的行为:
程序不要退出
提示用户输入错误
继续让用户重新输入数字,继续执行

3 开发者还可以自己定义异常对象,形形色色的异常,标识了不同的错误

4 异常的捕获和处理
关键字 try…except…例如:
try:
a = 4
b = 4/0
except ZeroDivisionError:
print(‘handle ZeroDivisionError’)

print(a)
print(‘结束’)

try代码块指明作用域,试着运行缩进下的代码,b = 4/0前面的代码a = 4会被执行,b = 4/0后面的代码不会继续执行,
ZeroDivisionError 指明专门捕获,如果上面抛出属于except捕获的异常,就跳到缩进后的代码执行这里的内容ZeroDivisionError 除零异常的类型
except下代码块是捕获异常后处理的代码
执行结果
除以0的异常被try…except…捕获了
并执行了except 里面的代码
其它的异常不会被捕获
意思是如果有这种除零的错误就跳到该错误的下面,处理该错误,如果try里面有多个错误,只要出现除零错误,即中断后面的操作进入异常处理,最后‘结束’会被执行,整个程序没有中断,try外面的还是会被执行

又比如:
try:
asfjsfg
b = 4/0
except ZeroDivisionError:
print(‘handle ZeroDivisionError’)
print(‘结束’)

像这种因为asfjsfg没有被定义,也没有被捕获,所以整个程序都会中断,后面的‘结束’也不会被执行

5 捕获多种异常
try:
asfjsfg
b = 4 / 0
print(b)
except ZeroDivisionError:
print(‘handle ZeroDivisionError’)
except NameError:
print(‘handle NameErrorr’)
print(‘结束’)

ZeroDivisionError 指明专门捕获除零的异常
NameErrorr 指专门捕获未定义的异常

asfjsfg b = 4 / 0 会一个个去匹配下面的异常处理一旦中间有一个被匹配上了就去执行匹配上的异常捕获处理代码,且认为异常已经被处理,没有匹配上就中断程序了,如上例子,只会打印出‘handle NameErrorr’和‘结束’,次只能匹配一个异常内容

6 捕获后得到详细的异常信息
try:
sfskf
except NameError as e:
print(‘handle NameError :’, e)
e就是异常对象,可以打印出里面存储的具体错误信息:handle NameError : name ‘sfskf’ is not defined

7 捕获所有异常
try:
sfskf
4/0
except Exception as e:
print(‘handle unkown Exception :’, e)

Exception 指明所有异常类型(父类)

可以简写成:
try:
sfskf
except :
print(‘handle unkown Exception :’)
但是无法知道异常信息

如果要知道异常信息,可以这样写
import traceback
try:
sfskf
except :
print(‘handle unkown Exception\n’ + traceback.format_exc())

8 finally语句:不管是否有异常,我们都要执行一段代码
try:
b = 4/0
sfkskf
except ZeroDivisionError:
print(‘handle ZeroDivisionError’)
except NameError:
print(‘handle NameErrorr’)
except:
print(‘handle unkown exception’)
finally:
print(‘in finally’)

finally一定要放在最后,即使没有异常,也会执行finally下面的代码
还可以和else组合,例如:
try:
b = 4
#sfkskf
except ZeroDivisionError:
print(‘handle ZeroDivisionError’)
except NameError:
print(‘handle NameErrorr’)
except:
print(‘handle unkown exception’)
else:
print(‘haha,noexception’)
finally:
print(‘in finally’)

如果有finally,else一定要放在finally前面,上段代码的意思是,如果没有捕获异常,就执行else下面的代码,finally下面的不管是否有异常都会执行

9 函数调用栈:异常从调用栈里抛出
def f3():
print(‘in f3 - begin’)
b = 4/0
print(‘in f3 - end’)
def f2():
print(‘in f2 - begin’)
f3()
print(‘in f2 - end’)
def f1():
print(‘in f1 - begin’)
f2()
print(‘in f1 - end’)

f1()
当执行到f3时,抛出异常时,代码会往上层看有没有捕获异常,如果上一层没有捕获,那再往上一层,捕获异常的下面平级代码不会被执行,如果三层都有捕获异常,那么离异常最近的那一层最先捕获

例如:
def f3():
try:
print(‘in f3 - begin’)
b = 4/0
print(‘in f3 - end’)
except:
print(‘f3 捕获了异常’)

def f2():
print(‘in f2 - begin’)
f3()
print(‘in f2 - end’)
def f1():
print(‘in f1 - begin’)
f2()
print(‘in f1 - end’)

f1()

打印结果:
in f1 - begin
in f2 - begin
in f3 - begin
f3 捕获了异常
in f2 - end
in f1 - end

如果这层不想处理,可以用raise抛给上层处理,例如:
def f3():
try:
print(‘in f3 - begin’)
b = 4/0
print(‘in f3 - end’)
except:
print(‘f3 捕获了异常’)
raise
def f2():
try:
print(‘in f2 - begin’)
f3()
print(‘in f2 - end’)
except:
print(‘f2 捕获了异常’)
def f1():
print(‘in f1 - begin’)
f2()
print(‘in f1 - end’)

f1()

打印结果:
in f1 - begin
in f2 - begin
in f3 - begin
f3 捕获了异常
f2 捕获了异常
in f1 - end

自定义异常
继承自Exception
class NameTooLongError(Exception):
pass
class NameTooShortError(Exception):
pass

使用raise抛出
raise NameTooLongError
抛出异常用在:
当函数里面出现错误,代码无法继续进行执行的时候,告诉上层调用代码,什么样执行错误产生了
由上层调用代码决定如何处理

39
1 我们经常用到远程操作Linux
自动安装产品到Linux
自动化用力的一些步骤
运维:环境监控、数据的自动获取、分析

2 Python远程控制主要使用库
方案:Paramiko、Pexpect(主要是为Linux使用的,在windows使用需要安装其他的)
安装Paramiko

pip install paramiko
pip install paramiko --default-timeout=60 防止中断可以加这个

保证ssh服务开启
#SSHClient

#SSHClient

import paramiko

#创建SSHClient实例对象
ssh = paramiko.SSHClient()

#调用方法,表示没有存储远程机器的公钥,允许访问
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

#连接远程机器的地址、端口、用户名、密码
ssh.connect(‘192.168.142.128’,22,‘wangfen’,‘123456’)

cmd = ‘’‘echo ‘5678
dfasfsfsd’>myfile
‘’’
ssh.exec_command(cmd)

stdin,stdout,stderr = ssh.exec_command(“cat myfile”)
print(stdout.read().decode())

ssh.close()

#stdin通常是写的,而非读的

注意点:
exec_commadn每次执行会新打开一个channel执行,新的channel可以理解为一个新的命令环境
新的环境,不在上次执行的环境里面
所以,我们不能多次调用,达到多次执行的目的

如果非要在一个终端执行一连串命令,可以多个命令一起执行,用分号隔开,比如:
ssh.exec_command(‘cd wangfen;python3 memory.py’)

传输文件到远程机器,sftp已经安装到paramiko库里了
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(‘192.168.142.128’,22,‘wangfen’,‘123456’)
sftp = ssh.open_sftp()
sftp.put(‘py10.py’,’/home/wangfen/wangfen1/py10.py’)

从远程机器拷贝文件到本机
import paramiko
#创建SSHClient实例对象
ssh = paramiko.SSHClient()
#调用方法,表示没有存储远程机器的公钥,允许访问
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#连接远程机器的地址、端口、用户名、密码
ssh.connect(‘192.168.142.128’,22,‘wangfen’,‘123456’)
sftp = ssh.open_sftp()
sftp.get(’/home/wangfen/wangfen1/py10.py’,‘d:/study/myfile.txt’)
sftp.close()

不可以直接从远程机器获取文件夹拷贝,可以通过tar打包再来拷贝

import os
os.makedirs(os.path.dirname(newfile)) #创建新目录

作业代码:
import paramiko
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
ssh.connect(‘192.168.142.129’,22,‘wangfen’,‘123456’)

count = 1
while count < 15:
count += 1
stdin, stdout, stderr = ssh.exec_command(‘cd /proc;cat meminfo’)
content = stdout.read().decode().replace(’ ‘, ‘’).split(’\n’)[:4]
memTotal = int(content[0].split(’:’)[1].split(‘kB’)[0])
memFree = int(content[1].split(’:’)[1].split(‘kB’)[0])
buffers = int(content[2].split(’:’)[1].split(‘kB’)[0])
cached = int(content[3].split(’:’)[1].split(‘kB’)[0])
avaMem = ‘% .2f%%’ % ((memFree + buffers + cached) / memTotal * 100)
result = ‘{} {}’.format(time.strftime(’%Y%m%d_%H:%M:%S’),avaMem)
print(result)
with open(‘ret1.txt’, ‘a’) as f:
f.write(result + ‘\n’)
time.sleep(5)

import paramiko,time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
ssh.connect(‘192.168.142.129’,22,‘wangfen’,‘123456’)

stdin,stdout,stderr = ssh.exec_command(‘ls’)
a = stdout.read().decode()
print(a)

if ‘wangfen’ in stdout:
print(‘wangfen already exits’)
else:
ssh.exec_command(‘mkdir wangfen’)

sftp = ssh.open_sftp()
sftp.put(‘memory.py’,’/home/wangfen/wangfen/memory.py’)
sftp.close()
transport = ssh.get_transport()
transport.set_keepalive(30)

ssh.exec_command(‘cd wangfen;python3 memory.py’)
print(‘wait for 30 seconds…’)
time.sleep(30)

sftp = ssh.open_sftp()
sftp.get(‘wangfen/ret1.txt’,‘ret.txt’)
sftp.close()
windows 的反斜杠\在路径里的会起到转义的作用,可以在前面加r表示不用转义

python语言调用外部程序
调用外部程序
os库里面的system函数
等于打开操作系统的shell命令,敲入一串命令
比如 mspaint命令
import os
os.system(‘mspaint’) #只要被调用程序没有结束,程序就会一直等在这里,成为阻塞式调用,可以在程序里面通过打开的程序找到其进程号杀掉进程关掉
print(‘after call’)

组装参数
工具软件命令行支持参数,组装出相应的参数
import os
os.system(‘mspaint e:\1.png’)
print(‘after call’)

两种方法:
1 os.system : os 库里面的system函数
相当于打开操作系统的shell,敲入一串命令

ret = subprocess.check_output(‘dir’,shell=true)
print(ret.decode(‘gbk’))

返回值:
有些程序退出后会有一个退出码,表示程序是否正确实现了其功能

Linux 命令:echo $? #查看上一个命令的退出码,通常退出码是0 代表该程序是正常运行的

Python在调用这些程序时,退出码作为返回值返回:
windows,如果是cmd.exe,返回值就是进程的退出码 比如:
import os
ret = os.system(‘dir’)
print(ret)
返回值是0,即是退出码

linux 会返回一个16位的数字,低位字节表示结束进程的信号数值,如果低位字节值为0,高位字节表示退出码

退出码对于python程序的意义是可以通过其退出码判断其进程是否有成功执行,比如:
import os
ret = os.system(‘mspaint’)
if ret == 0:
print(‘open pass’)
else:
print(‘open file’)

os.system无法获取输出内容结果

2.subprocess 调用外部程序
import subprocess
得到应用程序的输出内容结果,可以用subprocess里面的check_output函数得到
import subprocess
#shell=True表示使用终端shell执行程序,windows下面就是cmd.exe
#就是我们python程序调用cmd.exe再由cmd.exe执行,参数命令
ret = subprocess.check_output(‘dir d:\study’,shell=True,encoding=‘gbk’)
#如果有中文,需要decode,因为是中文os,所以cmd.exe输出是gbk编码
print(ret)
#print(ret.decode(‘gbk’))

subprocess.check_output 需要等到被调用程序退出,才能返回

但是subprocess库里面的Poen类,可以:
被调用程序运行时候,就获取其输出的信息
运行时,输入一些信息给被调用程序

调用外部程序
subprocess.Poen(args,stdin=None,stdout=None,stderr=None,shell=False,encoding=None等等其它参数)
args参数要么是列表,要么是一个字符串
当shell=False即表示不通过shell命令行里输入命令调用外部程序,这种此时传递给它的参数都必须放在一个列表里,这个列表中第一个元素是代表调用程序的路径,第二个元素是传给它的参数此时args参数应该是一个列表
比如:popen = Popen(args=[‘mspaint’,r’e:\1.jpg’])

当shell=True即表示用shell去执行,args应该是字符串
调用一个程序也可以不用启动shell,如果不启动shell

非阻塞式调用
from subprocess import Popen
popen = Popen(args=‘mspaint’,shell=True)
print(‘done’)

输入输出重定向,得到外部程序的输出

from subprocess import PIPE,Popen
popen = Popen(‘dir c:\dasfs’,stdout=PIPE,stderr=PIPE,shell=True,encoding=‘gbk’)
output,err = popen.communicate()
print(output)
print(‘分割线-----’)
print(err)

stdout=PIPE 类似这种格式,指的从管道里面获取输出内容,不打印到屏幕,但是要执行popen.communicate()

为什么我们程序打印到屏幕,就是因为标准输出和标准错误指向打印至屏幕

1 局部的函数只能在内部访问:方便维护,只需要在内部使用和调用,意图清晰,只需在函数内部用的东西就不需要放在外面了,比如:
def foo():
def bar():
print(‘in bar()’)

print('in foo()')
return bar  #返回的就是bar函数对象

inner = foo() # 此时inner就是等于bar
inner() #此时就
可以对bar进行调用
#或者
foo()()
函数里面的函数只能在大函数里面调用,它的有效范围只在函数内部

函数里面也可以定义类
def foo():
class My():
pass

print('in foo()')
return My #返回的就是My函数对象

foo()()
My的有效范围是函数内部

装饰器
定义类的静态方法时,就使用了装饰器
@staticmethod
def jump():
print(‘3 meter high’)

装饰器的特点就是用一个@开头的字符串
在我们阅读别人的代码时,会经常碰到装饰器
装饰器通常用来装饰函数
装饰器主要用来给函数增加一点功能
一般装饰器本身也是一个函数(callable)
我们可以想象成它包含了被装饰的函数

例子
def hello():
return ‘hello’

def hi():
return ‘hi’

我们需要返回值多两个感叹号
def endsign(func):
def wrapper():
return func() + ‘!!’
return wrapper

@endsign
def hello():
return ‘hello’
#相当于执行hello = endsign(hello)
print(hello())

有参数的函数
要装饰的函数参数都不同
def endsign(func):
def wrapper(*args,**kwargs):
print(‘args:’,args)
print(‘kargs:’,kwargs)
return func(*args,**kwargs) + ‘!!’
return wrapper

@endsign
def hello(arg1,arg2=’’):
print(‘arg1:’,arg1)
print(‘arg2:’,arg2)
return ‘hello %s %s’ % (arg1,arg2)

hello = endsign(hello)

@endsign
def goodbye(targets):
return 'goodbye %s ’ % ’ '.join(targets)

装饰器本身带参数
需要的结尾可能不同
def endsign(tail):
def innerone(func):
def wrapper():
return func() + ’ ’ + tail
return wrapper
return innerone

@endsign(’!!’)
def hello():
return ‘hello’

#相当于执行hello = endsign(’!!’)(hello)
print(hello())

如果装饰器有多个参数,那么就是在@endsign里面加两个参数,然后再定义装饰器的函数里面def endsign()里面也带上两个参数
进程的概念:运行着的程序,是由cpu解释进程二进制代码
可以通过任务管理器详细信息查看进程

线程的概念:

每个进程里面至少包含一个线程
线程是操作系统创建的,线程对应一个数据结构,是用来控制代码执行的数据结构,保存了代码执行中的一些重要状态信息,比如执行到了哪一条语句了等等,可以理解为代码是在线程中运行的
线程就像代码的执行许可证
每个线程对应了一段代码要执行的序列

当程序运行起来的时候,操作系统会自动为它创建一个线程,称为主线程,每个进程里面至少都有一个线程

举例:
一个服务窗口 = CPU的一个核,一个核是一个独立的处理单元,可以独立的处理、执行指令,如果是四核,那么就是四核的CPU
客户 = 进程
调度员 = 操作系统(OS)
服务号 = 线程

调度员分配服务号给客户 = OS分配线程给进程
服务窗口给客户办业务 = CPU核心执行线程代码

注意点:
在一个时间点上只能服务一个顾客业务
CPU核心,在一个时间点上只能执行一个线程代码

调度的概念:
一个客户不一定一直占用一个窗口直到它结束:
比如需要很长时间填写表格
这时候可以让下一个客户来办理
先前的客户填好了表格,再继续

操作系统不会让一个线程一直占用CPU,轮流的分配线程占用CPU

线程库

一个进程可以有多个线程,多个线程可以同时运行在多个CPU上

想要创建多个线程被CPU执行,程序代码如何产生新的线程?通过向OS申请,可以通过OS提供的编程接口,我们通常把这种编程接口称之为系统调用,现在操作系统的编程接口通常都是C语言编程接口,python里面把创建系统调用接口都封装起来了,通过threading库

线程库在python2里有两个库 一个是thread,一个是threading,在python3里只有threading

coding=utf8

import threading
from time import sleep

def thread1_entry():
print(‘child thread 1,start’)
sleep(15)
print(‘child thread 2,end’)

t1 = threading.Thread(target=thread1_entry)
t1.start() #暂时理解为这个执行后就会出现另一个线程,进程中的两段代码同时运行,不但主线程在运行,同时Cpu另外一个核心又在执行另一个线程,新的线程从该函数处开始执行
sleep(10) #主线程先结束,因为它只sleep了10秒
print(‘main thread end’)

都不sleep的话就看谁执行得快就谁先结束

多线程给了一个程序并行执行代码的能力,可以同时处理多个任务
我们为什么需要多线程
我们的大脑有时候需要同时处理多个事情
我们在酒席上可能会一边和朋友聊天,一边思考着各种其他事情
多线程给一个程序并执行代码的能力
同时处理多个任务

不同的操作系统线程的上限是不同的,通常可以创建上千个线程

我们有时候有这种需求:主线程等待子线程执行完主线程再接着执行,比如网络爬虫,已经启动了十几个子线程爬取数据,主线程需要等待子线程爬完后进行分析,这个时候可以用到一个线程对象的方法.join()

如果子线程函数对象中有参数,传入的参数必须放在一个元祖里

coding=utf8

import threading
from time import sleep,ctime

def thread1_entry(nsec):
print(‘child thread 1,start’,ctime())
sleep(nsec)
print(‘child thread 1,end’)

def thread2_entry(nsec):
print(‘child thread 2,start’,ctime())
sleep(nsec)
print(‘child thread 2,end’)

if name == ‘main’:
t1 = threading.Thread(target=thread1_entry,args=(1,))
t2 = threading.Thread(target=thread2_entry,args=(2,))
t1.start()
t2.start()

#等t1线程结束
t1.join()

# 等t2线程结束
t2.join()
print('main thread end')

threading.current_thread().ident 可以打印出进程的id号

多个线程指向一个函数,局部变量是不受多个线程的影响的:
import threading
from time import sleep
def thread1_entry():
#var 是局部变量
var = 1
for i in range(5):
print(‘th #{} :{}’.format(threading.current_thread().ident,var))
sleep(1)
var += 1

print(‘main thread start.’)
t1 = threading.Thread(target=thread1_entry)
t2 = threading.Thread(target=thread1_entry)
t1.start()
t2.start()

#等t1线程结束
t1.join()

等t2线程结束

t2.join()

print(‘main thread end’)
打印结果:
main thread start.
th #5308 :1
th #11448 :1
th #5308 :2
th #11448 :2
th #5308 :3
th #11448 :3
th #5308 :4
th #11448 :4
th #5308 :5
th #11448 :5
main thread end

如果是全局变量,被多个线程共享该变量时,则有可能被影响,共享对象的概念:
有些资源是某个时刻独占使用的:
如果不加锁:
某人使用厕所
另一个人也进入使用
发生冲突
锁保证了:
只有一个人去使用
别人必须等待

多线程使用共享数据的注意点
zhifubao_lock= threading.Lock() #调用Lock函数,返回一个锁对象

一个例子:
jcy用户在支付宝账号的余额为2000元
他乘坐滴滴打车要扣钱
他的余额宝会给他挣钱
处理滴滴打车扣钱逻辑在线程#1里执行
处理余额宝挣钱的逻辑在线程#2里执行
今天,他坐滴滴扣了10元,而余额宝挣的钱也是10元

coding=utf8

import threading
from time import sleep

#存储支付宝账户余额
zhifubao = {‘jcy’:2000,‘liming’:5000,‘wangan’:15000,‘zhaolei’:6005000,}

#线程1:滴滴打车处理,参数是用户账户和扣款金额
def thread1_didi_pay(account,amount):
print(’ t1:get balance from bank’)
balance = zhifubao[account]

#下面的sleep(2)表示一些处理过程需要花上2秒钟
print('* t1:to do something(like discount lockup) for 2 seconds')
sleep(2)

print('* t1:deduct')
zhifubao[account] = balance-amount

#线程2:余额宝处理,参数是用户账户和当前利息
def thread2_yuebao_interest(account,amount):
print(’$ t2:get balance from bank’)
balance = zhifubao[account]

#下面的sleep(2)表示一些处理过程需要花上1秒钟
print('$ t2:to do something2... for 1 seconds')
sleep(1)

print('$ t2:add')
zhifubao[account] = balance + amount

t1 = threading.Thread(target=thread1_didi_pay,args=(‘jcy’,10))
t2 = threading.Thread(target=thread2_yuebao_interest,args=(‘jcy’,10))
t1.start()
t2.start()
t1.join()
t2.join()
print(‘finally,jcy balance is %s’ % zhifubao[‘jcy’])

打印结果:
t1:get balance from bank

  • t1:to do something(like discount lockup) for 2 seconds
    $ t2:get balance from bank
    $ t2:to do something2… for 1 seconds
    $ t2:add
  • t1:deduct
    finally,jcy balance is 1990

在同一时间去访问共享的变量时,先执行完的线程中涉及到该共享变量的数据结果会被另外一个线程的结果覆盖

zhifubao_lock.acquire() #在代码访问共享对象之前加锁,当多个线程同时执行lock.acquire() 时,只有一个线程能成功地获取锁,锁就是锁起来的状态,然后继续执行代码,对共享数据操作完后释放锁,其他线程就待在哪里继续等待,直到获取到锁为止,只有释放了锁,其他的线程才可以获取到锁

zhifubao_lock.release() #访问结束后,一定要调用Lock对象的release方法,进行解锁操作,否则其它等待锁的线程将永远等待下去,成为死线程

coding=utf8

import threading
from time import sleep

#存储支付宝账户余额
zhifubao = {‘jcy’:2000,‘liming’:5000,‘wangan’:15000,‘zhaolei’:6005000,}

#调用Lock函数,返回一个锁对象
zhifubao_lock = threading.Lock()

#线程1:滴滴打车处理,参数是用户账户和扣款金额
def thread1_didi_pay(account,amount):
#在代码访问共享对象之前加锁
#当多个线程同时执行lock.acquire()时
#只有一个线程能成功获取锁,然后继续执行代码
#其他线程就继续等待,直到获得锁为止
zhifubao_lock.acquire()
print(’ t1:get balance from bank’)
balance = zhifubao[account]

#下面的sleep(2)表示一些处理过程需要花上2秒钟
print('* t1:to do something(like discount lockup) for 2 seconds')


print('* t1:deduct')
zhifubao[account] = balance-amount

#访问完共享对象释放锁
#访问结束后,一定要调用Lock对象的release方法,进行解锁操作
#否则其它等待锁的线程将永远等待下去,成为死线程
zhifubao_lock.release()

#线程2:余额宝处理,参数是用户账户和当前利息
def thread2_yuebao_interest(account,amount):
#在代码访问之前加锁
zhifubao_lock.acquire()
print(’$ t2:get balance from bank’)
balance = zhifubao[account]

#下面的sleep(2)表示一些处理过程需要花上1秒钟
print('$ t2:to do something2... for 1 seconds')


print('$ t2:add')
zhifubao[account] = balance + amount

#访问完共享线程,释放锁
zhifubao_lock.release()

t1 = threading.Thread(target=thread1_didi_pay,args=(‘jcy’,10))
t2 = threading.Thread(target=thread2_yuebao_interest,args=(‘jcy’,10))
t1.start()
t2.start()
t1.join()
t2.join()
print(‘finally,jcy balance is %s’ % zhifubao[‘jcy’])

打印结果:
t1:get balance from bank

  • t1:to do something(like discount lockup) for 2 seconds
  • t1:deduct
    $ t2:get balance from bank
    $ t2:to do something2… for 1 seconds
    $ t2:add
    finally,jcy balance is 2000

等待过长时间锁不会被破坏,记得获取锁之后一定要释放锁,否则另外一个线程会无限期的等待无法执行,另外zhifubao_lock =threading.Lock()-zhifubao_lock.acquire()是根据代码写的规则来保护共享数据对象的

name() 一般一个变量前后加两个下划线的都是内置的变量

有时我们定义方法或者类时,需要测试检查点:
if name() == “main”: #意思就是如果当前执行的模块就是当前文件的入口模块(就是当前代码所在的文件),那么就是 “main”: 就会执行该函数下的代码,如果是被其他模块引用过来使用,那么被引用的模块就不是它的入口模块,所以当前变量(name)的值就是它本身所在的模块名,就不会执行该函数下的代码
第39、40python操作mysql数据库
作用:
用例检查点
数据环境准备

手工测试:使用工具

自动化测试:编程语言访问数据库
接口无法返回数据或者数据有误时,或者测试用例数据准备,特别是性能测试时所需要的大量数据

app>Driver(库,专门用来访问数据库服务)>Server process(数据库服务程序,比如mysql oracle都有自己的数据库服务程序在运行着,我们是通过网络接口和数据库服务程序通讯的,传递命令和返回响应的数据,数据库服务可以维护多个数据库)>DB#1 …DB#n(不仅仅是磁盘,也有可能是固态盘)

需要一个Driver模块,这个通常是由数据库厂商开发的

安装mysqlclient库
第三方开发的,Django推荐的
文档:

pip install mysqlclient==1.3.12 (1.3.13还没编译好)

fetchone:
获取一行数据,返回的是元祖
返回的数据格式:(1, ‘Python’, ‘Python基础与实战’, 1)

fetchmany:
获取多行数据
返回的数据格式:((1, ‘Python’, ‘Python基础与实战’, 1),)

fetchall
返回所有数据
返回的数据格式:((1, ‘Python’, ‘Python基础与实战’, 1), (3, ‘梵蒂冈电饭锅’, ‘电饭锅的方式’, 1), (4, ‘测试一下’, ‘我不知道’, 111))

代码示例:
import MySQLdb

db= MySQLdb.connect(host=‘120.24.244.22’,
user=‘liming’,
passwd=‘liming’,
db=‘plesson’,
charset=‘utf8’,
connect_timeout=1)

#创建游标
c=db.cursor()

#执行sql语句
c.execute(’’‘select * from sq_course limit 1’’’)

c.execute(“show tables”)

#返回数据的行数
num = c.rowcount

#多行数据

date=c.fetchmany(20) #20表示20条数据,该参数如果不写,只显示第一条数据,数据量大的时候,可以一次100条的查询
print(date[0])

#返回所有数据

data=c.fetchall()
print(data)

返回一行数据

print(c.fetchone()[1])

for x in range(num):

row = c.fetchmany()

print(row)

批量插入数据代码示例:
with open(‘res.txt’,encoding=‘utf8’)as a:
lins=a.read().splitlines()

for line in lins:
    print(line.replace('  ',',').replace(' ',''))
    if line:
        c.execute(
            f'''insert into sq_course (name,`desc`,display_idx) values ({line.replace('  ',',').replace(' ','')})'''
        )

#执行插入操作
db.commit()
#返回数据的行数
num = c.rowcount
print(num)

删除操作代码示例:
import MySQLdb

db= MySQLdb.connect(host=‘120.24.244.22’,
user=‘liming’,
passwd=‘liming’,
db=‘plesson’,
charset=‘utf8’)

#创建游标
c=db.cursor()
#执行删除sql语句
c.execute(’’‘delete from sq_course where name like ‘初中%’ ‘’’)
#一定要执行此操作才能操作成功
db.commit()
#返回数据的行数
num = c.rowcount
print(num)

循环插入文件里的数据
import MySQLdb
conection = MySQLdb.connect(host=‘192.168.142.129’,user=‘songqin’,password=‘songqin’,db=‘plesson’,charset=‘utf8’)
c = conection.cursor()
with open(‘course1.date’,encoding=‘u’) as f:
lines = f.read().splitlines()
for line in lines:
#如果不是空行
if line:
c.execute(
f"""“INSERT INTO sq_course(name,desc,display_idx) VALUES ({line})”""
)
#一定要执行commit才能插入成功
conection.commit()
#c.rowcount 指明了这次插入记录的条数
print(c.rowcount)
#如果数据库操作都做完了就可以关闭了
conection.close()

更详细文档见:
https://mysqlclient.readthedocs.io/
软件开发离不开网络
而当今网络世界, 绝大部分都是基于TCP/IP协议
很多应用都是构建在TCP/IP协议的基础上的
浏览网页,手机微信、支付宝、滴滴打车
应用使用socket编程接口来通过TCP/IP协议进行通信
socket编程接口就是应用程序进行网络通讯的编程接口

网络协议栈
两种协议栈模型
TCP、UDP对应传输层,而IP协议对应网络层

OSI参考模型 TCP/IP五层模型
应用层 应用层
表示层
会话层
传输层 传输层
网络层 网络层
数据链路层 数据链路层
物理层 物理层

后四层负责数据的传送和接受,前三层统称为应用层,只不过在OSI中细分成了三层,主要负责数据传送和接收到后的处理,物理层比如以太网、wifi什么的,通常做应用程序的话,传输层和网络层通常是由操作系统帮我们实现的,数据链路层一部分由操作系统实现的,一部分是由设备的驱动程序厂商实现的,比如TPLINK的厂商实现的

实现网络协议的节点
TCP/IP只是一纸协议而已
它规范网络上的节点涉笔通信的规矩
程序实现协议的功能
在网络节点设备上:包括PC、手机、路由器
对TCP、UDP、IP协议(传输层、网络层)的实现

应用软件主要是实现TCP/IP层之上的功能,这里我们可以笼统看成是应用层的功能

应用层协议通过socket编程来实现

socket的概念
socket概念出现的时间比TCP/IP协议还要早
上世纪70年代,Berkeley Unix系统上就有了socket
它本来是用作同一台机器上的不同进程进行通信的

socket是操作系统的分配一种资源
在Unix/Linux系统上就是一种文件,但不在文件系统目录里

可以用来进行进程间通信(包括网络通信)
socket编程接口也就是操作系统API编程接口的概念
操作系统的服务以库的形式封装调用接口给应用程序
内存管理
进程
文件
socket
时间
外设
。。。

一般的操作系统提供的都是C语言开发的编程接口
但是Python已经通过库将其封装好了

是操作系统创建的概念
进程好比电话
socket好比电话插口
进程创建一个socket,就像电话用线接上了一个插口
不同的进程通过socket通信,就像电话接上电话插口进行通话

进程间的数据交换都可以称作进程间的通信

socket的参数项
使用什么传输层、网络层协议
目前最常用的是TCP,IP协议
也有其他的传输层协议UDP、SCTP
网络层,比如,IP协议,也分IPv6和IPv4

绑定哪个网络层地址(对方的IP地址和指定自己的地址方便接收)
比如IPv4协议就是一个IPv4地址,IPv6协议就是一个IPv6地址
网络层地址决定了唯一网络节点(比如电脑、手机)

使用什么端口号(通信双方都要指定端口号)
主机上会有很多进程,接受到的网络消息该送给哪个进程处理呢
用一个号码来标识,我们叫它端口号
比如http服务进程通常采用80作为端口号

不同的传输层协议可以用一个端口号

Python中的socket
TCP Socket通信
进程使用TCP Socket进行通信或许是现在网络世界的最广泛的应用
广泛使用的HTTP协议就是基于TCP的
TCP socket通信的特点
服务端 vs 客户端
通过(虚拟)连接
建立连接(three way handshake)
进行通信
拆除连接

三次握手
Client Server

syn(准备和你建立连接) seq=x(包号)

        syn ack=x+1 seq=y

ack=y+1 seq=x+1(告诉对方链路正常)

四次挥手
Client Server

fin seq=x(没有数据发送了)
ack=x+1(知道了)
fin seq=y(没有数据传送了)

ack=y+1(那就断开连接吧)

多线程处理多个客户连接
在while循环里面不断接受新的连接请求
并创建新线程专门和新客户端进行通信
多个客户端同时链接
我们程序的问题
#阻塞式等待连接请求
#得到了socket,后面就使用tcpCliSock和那个客户端互相发送消息了
#再也不管其他的连接请求了,因为后面的再也没有accept的调用了
tcpCliSock,addr = tcpSerSock.accept()
while True:
#阻塞式等待接收消息,BUFSIZ指定了一次最多获取多少byte的消息
#返回的是bytes类型
#缺省是阻塞模式,可以设置socket为nonblocking

data = tcpCliSock.recv(BUFSIZ)

#当对方关闭连接的时候,返回空字符串
if not data:
    break
print(data.decode())
tcpCliSock.send('** %s' % data) #发送消息

TCP传输的特点-流
客户端、服务端可以看成之间有个虚拟的连接
称之为流:像一根管道里面包含了两个管子
一根收信息,一根发信息
底层的tcp协议栈有可能会将消息进行分片发送
hello,mike,we are going to have dinner in the Backyard.
对方接受到消息后,底层系统会将消息放到系统缓冲中
供应用程序调用recv(num)去取
tcp协议底层会告诉对方,缓冲还可以存放多少字节的消息
获取到多少字节,是不确定的,看num和缓冲的内容

tcpserver.py

coding=utf8

from socket import *
#主机地址为空字符串表示所有的地址都绑定
#包括环回地址,所有网络接口的ip地址127.0.0.1
#客户端必须事先知道服务端的地址才能连接成功

HOST = ‘0.0.0.0’ #或者 HOST = ‘’
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST,PORT)

#创建socket,指明协议,AF_INET表示是IPv4协议,SOCK_STREAM表示是tcp协议
tcpSerSock = socket(AF_INET,SOCK_STREAM)

#绑定地址和端口,表示使用这个地址
#HOST为空字符串表示本机所有ip,port为21567
tcpSerSock.bind(ADDR)

#使socket处于监听状态,参数大意是指,允许等待连接的客户端的最大数量
#这个TCP服务进程监听在本机所有ip,port为21567,等待客户端的连接
tcpSerSock.listen(5)

print(‘等待客户端连接…’)

#accept阻塞式,等待连接请求,有客户端连接上来
#只有服务端程序调用了accept
#才会有syn ack,客户端收到后发送ack,三次握手完成,连接才能成功

#注意,这里返回了一个新的socket:tcpCliSock用来和这个连接上来的客户端进行通信
#原来的tcpSerSock还是负责监听
tcpCliSock,addr = tcpSerSock.accept()
print(‘连接来自:’,addr)

while True:
#阻塞式等待接收消息,BUFSIZ指定了一次最多获取多少byte的消息
#返回的是bytes类型
#缺省是阻塞模式,可以设置socket为nonblocking

data = tcpCliSock.recv(BUFSIZ)

#当对方关闭连接的时候,返回空bytes
if not data:
    tcpCliSock.close()
    break


#接收到的是bytes类型,需要解码
rstr = data.decode()
print(rstr)


#发送消息 send不一定能发送所有的数据
#sendall会反复尝试,直到所有的数据都发送完毕
#发送的也必须是bytes类型
tcpCliSock.sendall(f'** {rstr}'.encode())
tcpCliSock.send('** %s' % data)

tcpSerSock.close()

tcpclient.py

coding=utf-8

from socket import *

HOST = ‘127.0.0.1’
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST,PORT)

#创建socket,指明协议
tcpCliSock = socket(AF_INET,SOCK_STREAM)

#连接远程地址和端口,发送syn,等待syn ack,也是阻塞式的
tcpCliSock.connect(ADDR) #服务端的地址

while True:
data = input(’>> ')
if not data:
break
# 发送消息,必须是bytes类型
tcpCliSock.send(data.encode())

#阻塞式等待接收消息
data = tcpCliSock.recv(BUFSIZ)
#当对方关闭连接的时候,返回空字符串
if not data:
    break
#解码打印字符串
print(data.decode())

tcpCliSock.close()


版权声明:本文为weixin_40085907原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。