title: python入门与进阶
categories: python
tags: [python]
python入门导学
python的特点
- 是面向对象的编程语言
- 简介,灵活,优雅,哲学
- 易于上手难于精通
- 动态脚本的特点
缺点
- 慢
- 编译型语言c,c++事先编译成机器码
- 解释型语言:js,python
- java/c#属于什么类型?编译成中间代码
- 运行效率和开发的效率不可兼得
- 语言适合才是最好的
学习编程的误区
- web知识编程的方向,不要局限于此,使用编程解决实际的问题
python能做什么样的事情
- 爬虫
- 大数据与数据分析spark
- 自动化运维与自动化测试
- web开发:flask和django
- 机器学习
- 胶水语言
python的前景
- 数据方面的研发
- 计算机的性能已经能够满足了,更加关注的是开发的效率
课程维护与提问
- 在慕课的提问区提问
- 代码如果有更新,在慕课手记和知乎专栏:小楼昨夜又秋分
python环境安装
下载python安装包
- windows一键安装,2和3版本不是很兼容
- linux见自己的印象笔记linux安装python3.5
安装python
- windows安装之后需要配置一下环境变量
- linux需要删除以前的软连接建立新的软连接
idle和第一段python代码
理解什么是写代码与python的基本类型
代码与写代码
- 代码是实现世界食物再计算机世界中的映射,写代码是将现实世界中的事务用计算机语言来描述
数字:Number:整型和浮点型
- 整数:int,没有short,int,long之分
- 浮点数:float,再python中float为双精度
type(1)
输出1的数据类型- 数字相加精度向更高的精度类型
type(!*1.1111)
type(2/2)
得到的是一个float,type(2//2)
得到的是一个int
10,2,8,16进制
- / 是除法,// 表示整除
- 进制的相互计算
- 几进制就是满几进1
各进制的表示和转换
- 进制的表示
- 0b表示的是二进制0b11=3
- 0o表示的是8进制0o11=9
- 0x表示16进制0x1F=31
- 转换二进制
- 10进制转换成二进制
bin(10)
- 8进制转换成二进制
bin(0o7)
- 16进制转换成二进制
bin(0xE)
- 10进制转换成二进制
- 转换成10进制
int(0b11)
- 转换成16进制
hex(888)
- 转换成8进制
oct(0x777)
数字:布尔类型和复数
- boolean属于Number,空值都是False
- 真True
- 假False
bool(11)=True
bool(0)=False
只有0才会表示boolean假,其余的所有正数或者负数都表示真bool('abc')=True
bool('')=False
bool([])=False
bool({1,2,3})=True
bool({})=False
bool(None)=False
- complex复数
- 36j表示复数
字符串str,单引号与双引号
'let 's go
出现错误,"let's go"
就没有问题,反之外单内双也是可以的let"s go
- 引号用来表示字符串的时候,引号是成对出现的才是正确的
'let\'s go'
也是正确的,\ 叫做转义字符
多行字符串
-
三引号输入字符串的时候可以换行
''' hello hello hello '''
-
三个单引号和三个双引号的效果是一样的
-
在字符串中 \n 表示的是回车的意思
-
print("""hello \n hello""")
打印出字符串,显示换行 -
换行输入
>>>'hello\ // 在idle中测试,输出的结果是'hello world' world'
转义字符
- 特殊的字符
- 无法看见的字符,如换行符号等
- 与语言本身语法有冲突的字符,如单引号
- 举例子
- \n 换行
- ’ 单引号
- \t 横向制表符
- \r 回车,和换行不是一个概念
原始字符串
print('hello \\n world')
输出的结果是hello \n world,将\进行转义print(r'hello \n world')
加上一个 r 表示原始字符串,不再是一个普通字符串了,但是引号的问题没法用 r 解决
字符串运算一
"hello"+"world"
拼接字符串"hello"*3
其中乘法表示hello重复3次"hello[0]"
输出的是h,这样获取字符串中的某一个字符,数组下标"hello[-1]"
输出的是o,倒着数1次获取到的字符
字符串运算二
"hello world"[0:5]
截取到 hello ,到最后要截取的字符的下一位"hello world"[0:-1]
输出的结果是 hello worl,将d给排除掉了,负数表示步长,这里表示从0开始到字符串从末尾往回数1个字符
字符串运算三
"hello world"[6:]
不输入结尾的数字表示截取从第6开始到字符串的结尾"hello world"[-5:]
输出的是world"hello world"[:-5]
输出的是hello- 原始字符串的中的 r 可以小写 也可以大写 R,都表示原始字符串
python中表示组的概念与定义
列表的定义
- 现实世界中的事物成组出现,如moba游戏中的技能是成组出现的,世界杯总决赛也是分组出现的
type([1,2,3,4,5,6])
查看是否是列表类型- 列表的特性
- 列表类型可以随便,字符串,数字,boolean,字符串和数字的组合等等,区别于java的数组类型
[123,"hello",true]
[[1,2],[true,false]]
列表中可以嵌套列表,也就是java中的二位数组,在python中叫做嵌套列表
- 列表类型可以随便,字符串,数字,boolean,字符串和数字的组合等等,区别于java的数组类型
列表的基本操作
["技能1","技能2","技能3","技能4"][1]
输出技能2,得到字符串["技能1","技能2","技能3","技能4"][0:2]
输出[“技能1”,“技能2”],得到的是一个列表["aaa","bbb"]+["ccc"]
两个列表相加得到["aaa","bbb","ccc"]
["aaa","bbb"]*3
重复三次列表中的元素["aaa","bbb","aaa","bbb","aaa","bbb"]
- 没有减法的操作,编程需要实践
元组
(1,'-1',true)
- 访问元组的操作和操作列表是一样的
- 为什么有了列表还要元组?是有区别的
- int,str,list分别表示的是什么数据类型需要理解熟悉
type((1,2,3))
元组是tuple类型- 列表是list类型
- 字符串是str类型
- 整型是int类型
- 奇怪的现象
type((1))
最后的到的结果是int型,不是tuple型,当元组的元素是一个的时候,输出的类型就是元素本身的类型,只是做了数学运算而已
序列总结
-
有序
-
如何定义只有一个元素的元组
(1,)
,加上逗号假装后边还有 -
type(())
输出结果是tuple类型 -
str,list,tuple序列
- str,list,tuple在python中的学术名字叫做序列
- 序列的操作类似
- 每个元素都会有一个序号
-
对序列的截取叫做切片
"hello world"[2:4]
-
序列的一些操作
3 in [1,2,3]
表示3是否在这个列表中,结果是True3 not in [1,2,3]
表示3不在这个序列中,结果是Falselen([1,2,3])
查看列表中的元素个数,结果是3max([1,2,3])
求最大元素,结果是3min([1,2,3])
求最小元素,结果是1- ASCII码存储,
ord('w')
查看w字母的ASCII码
set集合
- 特性:
- 无序,没有下标索引,也不可能支持切片操作
- 不重复
- 集合的定义是花括号{}
type({1,2,3})
输出结果set{1,1,2,2,3}
输出的结果是{1,2,3}len({1,2,3})
输出长度31 in {1,2,3}
结果True1 not in {1,2,3}
结果False- 集合特殊的操作
- 从{1,2,3,4,5,6}中剔除掉{3,4}
{1,2,3,4,5,6} - {3,4} // 这个用到了 - 号,表示求差值
- 找出共有的元素,使用交集
{1,2,3,4,5,6} & {3,4}
- 找出共有的元素,使用并集(和集),不能出现重复的元素
{1,2,3,4,5,6} | {3,4,7}
- 定义一个空的集合
set()
,使用type(set())
输出set
- 从{1,2,3,4,5,6}中剔除掉{3,4}
dict字典
-
Key Value 通过Key找到Value的值,键值对
-
一个字典由很多的key 和 value 组成,也是集合类型的,不是序列
-
定义字典的方式
{key1:value1,key2:value2}
-
type({1:1,2:2,3:3})
结果是dict -
什么情况下使用字典dict
{'q':'技能1','w':'技能2','e':'技能3','r':'技能4'}
-
{'q':'技能1','w':'技能2','e':'技能3','r':'技能4'}['q']
通过key获取到value,输出技能1 -
字典中的key不能重复
-
字典的key键可以是字符串,也可以是数字等,字符1和数字1会识别成不一样的key,key必须是不可变的类型,如int ,str类型等是可以的,但是key不能是可变的列表类型,这只是一个例子,具体自己理解
-
value可以随便取值
{'q':{1:'技能1',2:'hello'},'w':'技能2','e':'技能3','r':'技能4'}
-
空的字典怎么表示
{}
空的花括号就可以表示空的字典
思维导图总结基本数据类型
- python基本数据类型
- 数字(Number)
- 整型 int
- 浮点型 float
- 布尔型 bool
- 复数 complex
- 组
- 序列(有序,可以下标索引来访问,切片操作[0:5])
- 字符串 str ,不可变
- 列表 list, 可变
- 元组 tuple, 不可变
- 集合
- set 无序,没有索引,不能切片
- 字典
- dict key:value 键值对是其最基本的概念
- 序列(有序,可以下标索引来访问,切片操作[0:5])
- 数字(Number)
变量与运算符
什么是变量
- 定义一个变量
A = [1,2,3,4,5,6]
其中 = 表示赋值符号,之后就可以多个变量计算A*3 + B + A
- 给变量起名字一定要有意义
- 命名可读性要强
变量的命名规则
- 字母、数字、下划线
- 字母、下划线可以作为变量名字的开头
- 系统关键字不能用在变量名字中,保留关键字如and,if,import等
- python的变量名字是要区分大小写的,Apple和apple是不一样的
- 不用指定变量的类型,类型不固定
a=1
a=1,b=a,a=3
输出b结果是1,a=[1,2,3],b=a,a[0]='1'
输出b的结果是[‘1’,2,3],这个地方类似于java中的引用类型
值类型与引用类型
-
int,str,tuple(不可改变) 是值类型,list,set,dict(可变)就是引用类型
-
区别
-
a="hello" a=a+"python" print(a) // 输出的结果是hellopython id(a) // 查看内存地址
- 其实a在做加法之前的内存地址和之后的内存地址不一样,使用
id(a)
这个函数可以查看,加法之后地址发生改变,说明是一个新的字符串,没有违背不可改变的原则 "python"[0]='o'
这个代码执行时错误的,因为字符串类型时不可改变的类型
- 其实a在做加法之前的内存地址和之后的内存地址不一样,使用
列表的可变与元组的不可变
-
a=[1,2,3] // 列表 id(a) // 输出内存地址hen(id(a))使用十六进制表示内存地址 a[0]='1' id(a) // 输出的内存地址和第一次时一样的,在同一个地址上被修改,所以时可变的
-
a=(1,2,3) a[0]='1' // 代码执行出现错误,元组不可改变
-
b=[1,2,3] b.append(4) // 在b列表中追加一个元素,但是对于元组时不可以追加和修改元素的 print(b)
-
为什么要有元组
- 因为为了代码的稳定性,代码之间的调用,不可改变时有一定优势的
-
a = (1,2,3,[1,2,3]) a[3][2] // 输出的结果时3,注意多维元组元素的获取 a[3][2]='4' // 这个时可以的,因为这个元组中改变的是列表元素,不是改变的元组
运算符号
- +加
- 列表*数字
- -减
- / 除法
- // 整除
- 5%2 求余数
- 2**2表示2的2次方
赋值运算符(python中没有++、–)
- =
- +=
- *=
- /=
- %=
- **=
- //=
比较运算符
- ==
- !=
>
<
>=
<=
不只是数字才能做比较运算
-
b=1 b+=b>=1 print(b) // 输出的结果是2,数字可以和bool相加的,bool是Number下边的一种 int(True) // 输出结果是1
-
'a'>'b'
结果是False,97和98,ascii码的比较 -
'abc'<'abd'
结果是True,ord(a)这个函数可以查询到a的ascii码 -
[1,2,3]<[1,2,4]
结果是True -
(1,2,3)<(1,3,2)
结果是True
逻辑运算符
-
and(且,与)
True and True
TrueTrue and False
False
-
or(或)
True or False
TrueFalse and False
False
-
not(非)
not False
Truenot True
False
-
1 and 1 // 1 'a' and 'b' // 'b' 'a' or 'b' // 'a' not 'a' // False
-
int,float取0的时候表示false,非0的时候表示的是ture
- 字符串,空字符串false,否则就被认为true
- 空的列表被认为是false,非空的是true
- tuple,set,dict和列表是一样的理解
-
逻辑运算操作的两个类型可以用非bool类型表示,结果也可以是非bool类型,但是最终的规则就是True或者False
-
1 and 0 // 0 0 and 0 // 0 // 下边两个返回的结果是最后比较的那个,注意理解,涉及到计算机的解析流程 1 and 2 // 2 2 and 1 // 1 1 or 2 // 1
成员运算符(返回的结果还是bool类型)
- int
a=1
a in [1,2,3,4,5] // True
- not in
b=6
b not in [1,2,3] // True
- 元组,字符串,集合都可以
- 字典成员运算符,主要是针对key的值是否存在
b='a'
b in {'c':1} // False
b=1
b in {'c':1} // False
b='c'
b in {'c':1} // True
身份运算符(python对象)
- is,如果两个变量的取值相等,则is返回true
- is比较的是两个变量的身份,可以理解位内存地址
- 关系运算符比较的是取值
a=1
b=2
a is b // False
a=1
b=1
a is b // True
a=1
b=1.0
a is b // False
a == b // True 比较的是取值,1和1.0是相等的
- is not
- 比较身份不相等
如何判断变量的值,身份,与类型(三大特征)
- 结合有序和无序
a={1,2,3}
b={2,3,1}
a == b // True 集合是无序的
a is b // False 内存地址是不一样的
c=(1,2,3)
d=(2,1,3)
c == d // False 元组有序的
c is d // False 内存地址是不一样的
- a == b 做值得判断
- a is b 是身份的判断
- 类型的判断,python的一切都是对象
a='hello'
type(a) == str // True
- 判断变量的类型
isinstance(a,str)
是否是strisinstance(a,(int,str,float))
判断是否三种当中的一种
- 对象的三个特征
- id 对应 is
- value 对应 ==
- type 对应 isinstance
- type()不能判断类的子类是属于什么类型的
- isinstance可以判断子类是什么类型的
位运算(把数字当作二进制数进行运算)
-
&
-
按位与
-
a=2 b=3 a & b // 2,10和11按位与之后是10
-
-
|
-
按位或
-
a=2 b=3 a | b // 3,10和11按位或之后是11
-
-
^
-
按位异或
-
a=2 b=3 a ^ b // 10和11相同位值不同则为1,相同位置相同则为0
-
-
~
-
按位取反
-
a=2 ~a // 10将0和1全部取反
-
-
<<
-
左移动
-
a=2 b=3 a << b // 将a转换成二进制10左移b位,后边添加b个0,最后的结果是a乘以2的b次方
-
-
-
>>
-
右移动
-
a=2 b=3 a >> b // 将a转换成二进制10右移b位,去掉b位,最后的结果是a除以2的b次方(取整数)
-
分支,循环,条件与枚举
什么是表达式
-
表达式Expression是运算符operator和操作数operand所构成的序列
-
a=1+2*3 a+2 c = int('1')+2 a=1 b=2 c = a and b or c
表达式的优先级
-
有优先级顺序
-
a or (b and c) a or b and c 先计算b and 从,再和a计算,因为and的优先级要高于or a + b * c
-
python优先级
** 指数运算(次幂) ~ + - 补码,一元加减(最后两个的方法名称是+@和-@) * / % // 乘法 除法 取模 整除 + - >> << 向右向左移位 & 按位与 ^ | <= < > >= 比较运算符 <> == != 等于运算符 = %= /+ //= -= += **= 赋值运算符 is is not 身份运算符 in not in 成员运算符 not or and 逻辑运算符,and 的优先级高于 c
-
解析器默认的解析顺序
- 同级从左到右
3+2-1
- 最好使用()括号进行你自己的优先级,()的优先级最高
a or b and c
先计算b and c,再和a计算,- 一般同级从左向右,左结合,通常是这样的
c=a+b
这个就是右结合,先计算右边,有 = 号要考虑右结合c = a or b
这个也是右结合,注意理解- 尽量使用()实现自己的运算
- 同级从左到右
表达式优先级练习
-
(not a) or ((b + 2) == c)
-
注意优先级比较 not > and > or
在文本文件中编写python代码
-
以前学的是基本类型与基本概念
-
可以再idle中的file中写代码,ctrl+n快捷键
a=1 b=2 c=3 print('hello world')
-
- 再windows上的cmd中
- cd 到保存hello.py的目录下边
python hello.py
-
保存的时候要保存成.py扩展名的文件
-
开发工具(IDE:继承开发环境)
- pycharm
- vscode
- sublime
熟悉vscode开发环境与python插件安装
- ctrl + `打开命令界面
- vscode的优点
- 智能感知
- 断点调试
- 安装插件
- python
- terminal(多个ide同步)
- vim
- ctrl + p 快速检索文件
流程控制语句之条件控制一
-
语句的结束可以不用机上 ;
-
不需要 { }区分代码,而是通过缩进的方式进行区分
-
if else for while 这四个都有,没有switch
-
单行注释:
# 注释内容
使用快捷键 ctrl + / -
多行注释:
''' 注释内容 '''
快捷键alt + shift + a -
条件控制
- 选择性问题
mood = True if mood : print('go to left') print('back away') print('没有包含在if语句中')
mood = True if mood : print('go to left') else : print('go to right')
- 没有使用 { } 区分代码,而是在不同的代码块前使用 4 个空格进行区分,缩进
流程控制语句之条件控制二
-
python建议使用下划线分割两个单词组合
-
支持表达式的条件
a=1 b=2 c=2 if a or b +1 == c: print('left') else: print('right')
-
简单的用户登陆
account = 'qiyue' password = '123456' # input() 命令行输入的函数 print('please input account:') user_account = input() print('please input password:') user_password = input() if user_account == account and user_password == password: print('success') else: print('fail')
常量与pylint的规范
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python==3.4.0.12
解决速度问题
-
查看规范介绍https://www.ibm.com/developerworks/cn/linux/l-cn-pylint/
-
安装pylint
- 下载脚本https://bootstrap.pypa.io/ez_setup.py
- 运行脚本
python ez_setup.py
- 下载脚本https://pip.pypa.io/en/stable/installing/
- 运行脚本
python get-pip.py
- 安装pylint
python -m pip install pylint
- 设置setting.json中的
"python.linting.pylintPath": "C:/Users/安装路径/Python/Python37/Scripts/pylint.exe"
-
规范代码
- python中的常量不是真正意义上的常量,只是形式上的常量,需要大写
- 缺失模块的描述,一个文件就是一个模块
- pylint认为不是在函数和类中定义的变量都是常量,形式常量就需要大写
- 缩进是4个空格键,并不是tab键,除非确保tab键有4个空格
''' 模块说明 ''' ACCOUNT = 'qiyue' PASSWORD = '123456' # input() 命令行输入的函数 print('please input account:') # pylint认为不是在函数和类中定义的变量都是常量,形式常量就需要大写 user_account = input() print('please input password:') user_password = input() # :前边不能有空格 # 比较运算符前边应该有空格 if ACCOUNT == user_account and PASSWORD == user_password: print('success') else: print('fail') # 程序的末尾是需要一个空行的
流程控制语句之条件控制三(snippet、嵌套分支、代码块的概念)
-
snippet 片段,快速构建代码的片段,其实就是代码片段的提示,是用tab键进行位置的跳转
-
if 可以单独的使用,可以不用配合else
-
pass 是空语句,占位语句
if True: pass # 如果没有写pass,空着的话就会报错
-
-
区分if else或者if的结构体
-
嵌套分支
if condition: if condition: pass else: pass else: if condition: pass else: pass
-
伪代码和代码块(code1、code2),代码块中同一级别的一行代码被执行,所有的代码都会被执行,注意理解
if condition: code1 code2 else: pass
-
python中没有goto关键字的用法
-
养成号的代码封装成函数的习惯,特别是在分支结构中的使用
-
缩进相同的代码属于同一个代码块
流程控制语句之条件控制四(elif的优点)
-
a = 1 if a == 2: print('2') elif a == 1: print('1') else: print('other')
-
解决分支的问题和代码的简洁,python中没有switch,官方提倡使用字典的方式代替别的语言中的switch
思考题解答与改变定式思维
-
input()接受到的是字符串,所以在比较数字的时候可能出现问题
-
a = input() a = int(a) # 通过转换将输入的字符串转变成数字整型
-
注意各种语言的缺点,有点和缺点的理解
-
如何写出优美得代码
a = 1 b = 0 # a 和 b不可能同时位False a or b # 这里使用 or 比使用 if else 更加的好
包、模块、函数与变量的作用域
while循环与使用场景
-
解决问题的思维模式
-
while和for
-
while
CONDITION = True while CONDITION: print('i am while')
- 不是死循环的while,while可以和else结合使用,在while条件为false的时候执行else
condition = 0 while condition <= 10: print(condition) condition += 1 else: print('EOF')
-
在使用递归的场景下边使用while的时候比较多
for和for-else循环
-
代码结构
for target_list in expression_list: pass
-
for主要用来遍历或者循环序列、集合、字典的元素
a = [['apple','orange','banana','grape'],(1,2,3)] for x in a: for y in x: print(y)
print(y,end='')
# 如果不想换行打印,需要添加一个end=’'参数
-
for 也可以和else结合使用,当遍历完成之后执行else
-
跳出循环
a = [1,2,3] for x in a: if x == 2: break print(x)
-
跳过当前循环
a = [1,2,3] for x in a: if x == 2: continue print(x)
-
for循环中加入了break之后就不会执行后边的else了,continue是会执行else的
a = [1,2,3] for x in a: if x == 2: break print(x) else: print('end')
-
在嵌套的循环中使用break,跳出的只是内部嵌套的循环,而外部的循环还在执行,外部循环执行结束之后还会去执行else语句
for与range
-
类似于其他语言中的for中的i++
# 表示的是0到9,从0开始10个偏移量 for x in range(0,10): print(x)
-
输出0,2,4,6,8有2的相差,递增的等差数列
for x in range(0,10,2): print(x)
-
以行的方式打印出来,同时使用 | 分割开来
for x in range(0,10,2): print(x,end=' | ')
-
递减的等差数列
for x in range(10,0,-2): print(x,end=' | ')
-
对于一个列表打印出相差的间隔元素出来
for x in range(10,0,-2): print(x,end=' | ')
-
比for循环更简单的取有间隔的元素
a = [1,2,3,4,5,6,7,8] b = a[0:len(a):2] # 2表示的是间隔 for x in range(0,len(b)): print(b[x],end=' | ')
新篇章导言
- 会写代码很容易,难在写出高性能,封装性(复用性)很高的代码,抽象
- 代码美不美
python工程的组织结构:包、模块、类
- 包是文件夹
- 文件就是一个一个的模块
- 模块下边就是类
- 类的下边包含函数和变量
python包与模块的名字
- 区分不同包的两个同名模块包名.模块,如seven.c4和six.c4,这形成了一个路径,就是命名空间
- python如何区分一个普通的文件夹和一个包的呢?
- 必须在一个文件夹下边有一个特定的文件
_init_.py
,这个文件夹就是一个包了 - init 这个模块的名字就是文件夹的名字
- 必须在一个文件夹下边有一个特定的文件
import导入模块
-
讲公用或者重复的东西提取到一个模块当中,其他模块中去引用就行了
-
导入,引入的时候要遵循先后顺序
import module_name // 在一个模块中定义变量,另外一个模块中引入这个模块,必须注意顺序 print(module_name.变量) // 引入之后使用变量
-
import只能引入模块
import t.c7 as m // 重命名 命名空间 print(m.a)
from import 导入变量
-
from module import a,def
// 引入变量或者函数from t.c7 import a // 从t.c7中引入a这个变量 print(a)
-
引入模块
from t import c7 print(c7.a)
-
引入多个变量
a=1 // t文件夹下的c7.py文件中定义三个变量 b=2 c=3 from t.c7 import * print(a) print(b) print(c)
-
导入指定的变量 a、b
_all_ = ['a','b'] // 被称为模块的内置属性,变量 a=1 // t文件夹下的c7.py文件中定义三个变量 b=2 c=3 from t.c7 import * print(a) print(b) print(c) // 打印c的时候报错,因为c没有被引入
_init_.py
的用法
-
在vscode中排除显示一些文件 files.exclude,将其中的pycache设置成true,缓存文件夹就不显示了
-
引入变量
from c9 import a,b,c
-
加入需要导入的变量特别多,解决换行的方法 \,不推荐
from c9 import a,b,\ c
-
加上括号换行
from c9 import (a,b, c)
-
init 文件
- 一个包被导入的时候自动运行
- init 中的代码,这个init文件在包 t 下边
a = 'this is a init file' print(a)
- 在c11.py文件中引入这个包文件
import t // 引入这个包就可以了,执行c11.py这个文件的时候就会自动的去执行 init 文件中的代码
-
说明:无论是导入的包还是包下边的模块,init 文件中的代码都会运行
-
在模块中使用
_all_
可以决定哪些变量被导出,在 init 文件中可以使用_all_
决定包中的哪些模块被导出,以下是限制只能导出 c7 这个模块的 init 文件代码_all_ = ['c7'] // init 文件代码 from t import * // 将t包下的所有模块导入 print(c7.a) print(c8.a) // 因为限制了c7可以导出,所以 c8 会报错
-
批量的导入多个模块可以在 init 文件中导入,然后在别的模块导入包就行了
import sys // init 文件中导入多个模块 import io import t // 在别的模块中导入包 t 就可以使用这些模块了 print(t.sys.path)
包与模块的几个常见错误
- 包和模块不会重复被导入
- 避免循环导入,也就是两个模块之间相互导入
- 入口文件的概念
模块内置变量
-
模块只是视作文件,不是真实的文件
-
a=1 b=2 c=3 infos = dir() // dir 函数就是打印出这个模块中的变量的 print(infos)
关注带有下划线的变量,这些都是系统定义好的内置的变量
''' this is a doc ''' print('name:'+__name__) print('package:'+__package__) print('doc:'+__doc__) print('file:'+__file__)
入口文件和普通模块内置变量的区别
-
''' this is a doc ''' print('name:'+__name__) print('package:'+ (__package__ or '当前模块不属于任何包')) print('doc:'+__doc__) print('file:'+__file__)
-
注意入口文件和普通模块的内置变量的比较,这两者是不一样的
_name_
的经典应用
-
dir 函数的用法
import sys infos = dir(sys) print(infos)
-
_name_
-
让这个模块脚本文件不仅可以被其他模块调用,还可以自己单独执行,再代码的逻辑处理上要有一定的处理
-
if __name__ == '__main__': print('作为一个入口文件') print('this is a module')
-
-
python -m 命名空间.module.py
通过参数 -m 将一个可执行的文件当作一个模块来执行,但是这个文件必须是属于模块身份的,即在包的下边
相对导入和绝对导入
-
注意什么是顶级包,根据可执行文件的路径有关系
-
绝对路径:从顶级包一直往下找
-
相对路径: . 当前目录 … 上上级目录 … 上上上级目录
-
import不能使用相对导入
-
from .module import b
相对导入from .package2.package4.m2 import m
-
-
引入超过顶级包的包会报错,入口文件没有包
-
入口文件中不能使用相对路径导入包
-
在入口文件的上一级将入口文件作为模块运行
python -m demo.main.py // 在demo的上一级目录运行这个目录
-
python函数
认识函数
- round函数
a = 1.12386
result = round(a,2) // 保留小数点后两位,能进行四舍五入,round是内置函数
- 打开python命令行,之后输入
help(round)
查看内置函数,输入import this
会出现一些优美的语句 - 函数
- 功能性
- 隐藏细节
- 避免编写重复的代码
- (组织代码,自定义函数)
函数的定义及运行特点
- 函数的基本结构
def funcname(parameter_list): // def 关键字,参数名称,参数列表
pass // 空语句函数体,注意缩进
- parameter_list这个参数列表可以没有
- return value、None有返回值或者没有返回值
- 实现两个数字的相加
def add(x,y):
result = x + y
return result
- 打印输入的参数
def print_code(code):
print(code)
- 函数在定义之后需要调用才能使用
a =add(1,2) // 调用函数,参数的顺序和取值
b = print_code('python')
print(a,b) // print可以传递多个参数,只要是需要打印的东西就可以
// 执行的结果是,注意理解代码执行的顺序
python
3 None
- python是解释型的语言,所以函数的定义要在前边,调用放在后边
- python的递归处理次数(一般默认次数,不同电脑不一样),可以设置
import sys
sys.setrecursionlimit(100000) // 设置自己的允许的递归长度
- 定义函数的时候避免和内置的函数同名
如何让函数返回多个结果
- 函数内部遇到return语句之后,return后边的语句是不再执行的
- python不用指定返回结果的类型
- 函数返回多个结果
def damage(skill1,skill2):
damage1 = skill1 *3
damage2 = skill2 * 5
return damage1,damage2
damages = damage(3,4)
print(type(damages)) // 返回的类型是元组tuple
print(damages)
-
如何使用元组类型的返回结果?
- 一般的获取方法,不推荐
print(damages[0],damages[1]) // 通过下标获取元组的元素,但是这种方式不好
- 序列解包的方法,推荐使用
# 推荐的获取返回结果的方法 skill1_damage,skill2_damage = damages(3,4) // 两个变量接受两个返回结果 print(skill1_damage,skill2_damage)
序列解包与链式赋值
- 赋值的写法
a = 1
b = 2
c = 3
# 三行的代码可以使用这样一行替换
a,b,c = 1,2,3
- 一个变量接受三个数值
d = 1,2,3
print(type(d)) // 结果是 tuple
-
序列解包就是将tuple分拆成单独的值
- 需要注意是序列的个数在解析的时候要和变量个数相等
d = 1,2,3 print(type(d)) // 结果是 tuple a,b,c = d // 这样就实现了tuple的拆分,也就是序列解
-
补充
a = 1 b = 1 c = 1 a,b,c = 1,1,1 a = b = c = 1
必须参数与关键字参数
-
必须参数
- 在函数参数列表中定义的参数是必须传递的
- 形式参数(形参),定义的时候
- 实际参数(实参),在函数调用的时候传递的参数的实际取值
def damage(skill1,skill2): damage1 = skill1 *3 damage2 = skill2 * 5 return damage1,damage2 damages = damage(3,4) // 两个参数都要传递
-
关键字参数(不用考虑参数传递的顺序),明确告诉实参是给那个形参使用的
- 强调了代码的可读性
def add(x,y): result = x + y return result c = add(y=3, x=2) // 明确指定实参和形参对应,可以不用考虑传递的顺序了
-
必须参数和关键字参数的区别是在函数的调用上
默认参数
-
可以让参数有一个默认的值
def print_students_files(name,gender='男',age=18,college='人民小学'): print('我叫' + name) print('今年' + str(age)) print('我是' + gender) print('我在' + college) print_students_files('名字') // 调用有默认参数的函数 print_students_files('名字','女') // 调用有默认参数的函数,对不需要的默认参数传值
-
默认参数的坑
- 非默认参数在前边,默认参数在后边
- 实参的顺序和形参的顺序必须对应,如果不对应,使用关键字参数的方式进行传递
- 不能把默认参数和必须参数混合在一起调用,会报错
可变参数
-
print('a','b','c')
这样的调用就是可变参数 -
定义一个拥有可变参数的函数
- python会将可变参数的列表组装成一个tuple
def demo(*param): // *parame可变参数列表 print(param) print(type(param)) demo(1,2,3,4)
- 也可以自己组装tuple作为参数
def demo(param): print(param) print(type(param)) demo((1,2,3,4)) // 自己组装
-
可变参数的特性
- 定义了可变参数,但是使用的时候传递了一个元组怎么办呢?
def demo(*param): // *parame可变参数列表 print(param) print(type(param)) a = (1,2,3,4) demo(*a) // 使用 *a 就是解包作用,将元素平铺出来作为参数传递
- 必须参数和可变参数结合使用的时候,必须参数必须放在前边,可变参数放在中间,默认参数放在最后边
def demo(param1,*param,param2=2): // 必须参数,*parame可变参数列表, 默认参数 print(param1) print(param) print(param2) demo('a', 1,2,3 ,param2 = 'param') // 在调用的时候将默认参数采用关键字参数的方式进行值得传递
-
再定义函数的时候保证形参列表的简单
关键字可变参数
-
编写一个求平方和的函数
def squsum(*param): sum = 0 for i in param: sum += i*i print(sum) squsum(1,2,3)
-
任意个数的关键字参数
def city_temp(**param): print(param) for key,value in param.items(): // 这样就能遍历字典 print(key,':',value) print(type(param)) // 结果是 字典dict city_temp(bj='32',xm='45',sh='31') a = {'bj':'32','xm':'45'} city_temp(**a) // 传入字典dict city_temp() // 如果什么都不传递的话就是传递的空的字典
变量作用域
- 函数内部的变量不会影响函数外部定义的变量,作用的范围是不一样的
- 函数外部定义的变量可以被函数内部引用
- 全局变量、局部变量
- 如果想在函数的外部使用函数内部的变量,采用的方法是先在函数的外部初始一个值
a=''
,之后在函数的内部对值进行引用,其实就是使用全局变量 - 对于 for 循环来说,可以在for循环的外部调用for循环内部定义的变量
- for ,while,if else等中的变量会被视为和函数内部的变量同一级别的变量,正确理解各种变量之间的作用域是什么
作用域链
- 逐层寻找变量,链式特性,作用域的理解
- 理解清楚作用域
global关键字
-
全局变量不仅仅只是作用域模块中的,可以在整个应用程序中都能引用
-
局部变量很容易覆盖全局变量
-
将局部变量变成全局变量
-
关键字global
-
def demo(): global c c = 2 demo() // 至少要先调用一下函数,后边才能打印 c print(c)
-
通过global定义的全局变量可以在整个应用程序中进行使用
-
-
python没有代码块作用域
划算还是不划算
- python最适合用来解决问题
- 编写一个应用程序:一个来自游戏的问题,游戏也有经济系统,分析购买一个东西到底划不划算
- 合成6级的石头
- 购买1级石头,消耗金和钻石
- 1级石头合成3级石头,消耗金,体力,1级石头
- 3级石头合成4级石头,消耗金,体力,1级石头,一定概率
- 4级石头合成6级石头,消耗金,体力,四级石头
- 没有写程序
- 合成6级的石头
高级部分:面向对象
类的定义
-
类 = 面向对象
-
关键字 class
-
类的命名规则
- 首字母大写
- 类的名字首字母都大写
StudentName
,区别变量名字的命名
-
类中可以定义若干个变量
-
类中可以定义函数
-
类的实例化以及使用
class Student(): name = '' age = 0 def print_file(self): // 必须加上固定的关键字 print('name:' + self.name) // 调用类中的变量需要使用self这个参数 print('age:' + str(self.age)) student = Student() student.print_file()
-
总结
- 类最基本的作用就是封装代码
- 类只负责定义,不能再类当中去调用类中的方法,运行调用必须再类的外部进行
-
在另外一个模块中去调用定义好的类
from c1 import Student student = Student() student.print_file()
浅谈函数与方法的区别
- 方法:设计层面
- 函数:程序运行,过程的一种称谓,面向过程的概念(一般定义在类中的函数称为方法,在模块中的叫函数),变量和数据成员的区别和理解
类与对象
- 什么是类
- 类是现实世界或思维世界中的实体在计算机中的反映
- 将数据以及这些数据的操作封装在一起
- 行为与特征,行为就是方法,特征就是数据成员,行为一定要找对主体
- 类是抽象但是不是具体
- 对象
- 表示一个具体的概念
- 类可以产生很多个对象,类只是一个模板
构造函数
-
id(?)
查看内存地址 -
构造函数
_init_
- 构造函数的调用是自动进行的
- 也可以显示的去调用构造函数,但是一般很少显示的去调用
- 注意构造函数的返回结果
- 对于构造函数来讲只能返回一个None,不写return
class Student(): name = '' age = 0 def _init_(self): // 构造函数 pass def print_file(self): // 必须加上固定的关键字 print('name:' + self.name) // 调用类中的变量需要使用self这个参数 print('age:' + str(self.age)) student = Student() student.print_file() a = student._init_() print(type(a)) // 如果显示的调用构造函数就和一般的方法没有什么区别了
-
构造函数的作用
- 初始化对象的属性
class Student(): name = '' age = 0 def _init_(self,name,age): // 构造函数 name = name age = age student = Student('name',18) // 这样就类似于在直接调用构造函数_init_ print(student.name,str(student.age)) // 访问对象的属性,结果什么都没有,因为有局部变量的作用域有关系
区别模块变量与类中的变量
- 类变量,实例变量
- 类的变量之间的关系要和模块中的变量之间的行为进行区分,分别对待
类变量与实例变量
-
类变量和类关联在一起
-
实例变量和实例关联在一起的
-
保存对象的特征值(定义实例变量),区别类变量的定义,self可以随便起名字
class Student(): name = 'qiyue' // 定义类变量 age = 0 def _init_(self,name,age): // 构造函数 self.name = name // 定义实例变量 self.age = age student = Student('name',18) // 这样就类似于在直接调用构造函数_init_ print(Student.name,str(Student.age)) // 类的变量 print(student.name,str(student.age)) // 访问对象的属性
-
类抽象,对象具体
-
python易于上手难于精通的语言
类与对象的变量查找顺序
-
查看实例对象中的变量
student = Student() print(student.__dict__) // 实例对象 print(Student.__dict__) // 类
-
访问实例变量的变量的时候,先去实例中查找,如果没有查找到,就会去类中去找
self与实例方法
- 实例方法定义需要self,调用的时候不用传递参数,需要显示的指定 self
- self和实例对象对应
- 实例变量和实例方法(实例可以调用的方法)
在实例方法中访问实例变量
class Student():
sum = 0
def _init_(self,name,age): // 构造函数
self.name = name // 定义实例变量
self.age = age
print(self.name,self.age) // 实例方法中访问实例变量
self.__class__.sum += 1
print('创建对象的个数:' + self.__class__.sum) // 实例方法中访问类变量
student = Student('name',18) // 这样就类似于在直接调用构造函数_init_
- python类
- 变量
- 类变量
- 实例变量
- 方法
- 实例方法
- 类方法
- 静态方法
- 构造函数:是一个特殊的实例方法,调用的方式不一样而已,初始化类的各种特征的
- 成员可见性
- 面向对象三大特性
- 继承
- 封装
- 多态
- 变量
- 访问实例变量加上
self.变量名
- 实例方法中去访问类变量(两种方法)
- 类外部访问类变量
Student.name
,在实例方法中访问类变量也是Student.name
- 第二种在实例方法中访问类变量的方法
self.__class__.name
- 类外部访问类变量
类方法
-
如何定义一个类方法
@classmethod
,这个是装饰器,类方法操作类变量class Student(): sum = 0 def _init_(self,name,age): self.name = name self.age = age #self.__class__.sum += 1 #print('当前班级学生人数:' + str(self.__class__.sum)) def do_homework(self): print('homework') @classmethod // 增加了这个装饰器,这个方法就称为了类方法 def plus_sum(cls): cls.sum += 1 // 操作类变量 print(cls.sum) student = Student() student.do_homework() Student.plus_sum()
-
类方法和实例方法的区别
- 实例和类的区别
- 操作和对象无关的变量使用类方法
-
调用类方法
- 可以使用对象调用,但是这样是说不通的,所以不推荐
类名.类方法
:Student.plus_sum()
静态方法
-
静态方法的调用
@staticmethod
装饰器的使用class Student(): sum = 0 def _init_(self,name,age): self.name = name self.age = age #self.__class__.sum += 1 #print('当前班级学生人数:' + str(self.__class__.sum)) def do_homework(self): print('homework') @classmethod // 增加了这个装饰器,这个方法就称为了类方法 def plus_sum(cls): cls.sum += 1 // 操作类变量 print(cls.sum) @staticmethod // 装饰器指定静态方法 def add(x,y): // 不需要传递过来一个self或者cls print(Student.sum) // 静态方法调用类变量 print('static method') student = Student() student.do_homework() Student.plus_sum() Student.add(1,2) student.add(2,4)
-
和实例方法和类方法的区别
- 不需要传递slef或者cls
- 可以使用类或者对象直接调用静态方法
- 静态方法和类方法比较类似
-
静态方法和类方法访问实例变量要报错
-
不要经常使用静态方法,和面向对象没有太大联系
成员可见性:公开和私有
-
外部的调用就是在类的外边调用类中的方法
-
内部调用就是在类的内部,方法之间的调用
class Student(): sum = 0 def _init_(self,name,age): self.name = name self.age = age def do_homework(self): print('homework') self.do_chinese_homework() // 内部调用 def do_chinese_homework(self): print('chinese homework') student = Student() student.do_homework() // 外部调用
-
安全正确的操作内部变量,通过方法操作实例变量是安全的
class Student(): sum = 0 def _init_(self,name,age): self.name = name self.age = age self.score = 0 def marking(self,score): // 通过方法操作类内部的变量 if(score < 0): self.score = 0 self.score = score print(str(self.score)) student = Student() student.marking(33)
-
所有类下边的变量的更改通过方法的方式进行修改,不要直接调用,因为可以在方法中对数据进行一定的保护
-
阻止类的外部直接访问变量,
-
公开的public,外部可以直接访问,
-
私有的private,外部无法访问,双下划线 _ _
,变量名或者方法名字前边加上双下划线就可以成为私有的了class Student(): sum = 0 def _init_(self,name,age): // 构造函数是特有的函数,有了双下滑线 _ _ 也可以松外部访问 self.name = name self.age = age self.__score = 0 // 变成了私有变量 def __marking(self,score): // 双下划线__定义私有方法 if(score < 0): self.__score = 0 self.__score = score print(str(self.__score)) student = Student() student.__marking(33) // 变成了私有的了,无法访问 student.__score = -1 // 不会报错
-
类似于构造函数
__init__
这种下划线开始和下划线结束的函数,python不会认为是私有的,只有在开始的时候使用了下划线才会是私有的__marking
-
上边调用私有方法就会报错,但是访问私有变量__score没有报错,是为什么呢?
student.__score = -1
由于动态语言的特性,这里属于给student新添加了一个实例变量,这里有赋值所以没出错,但是这里的__score
和原来的__score
是不一样的
-
没有什么事不能访问
-
接着上边如果是以下的代码就会报错
stu = Student() print(str.__socre) // 这里没有赋值,所以这是去访问原来的私有变量,无法访问报错
-
私有变量加上下划线之后,使用
Student.__dict__
查看之后,出现了**_Student__score**,这就是私有的了,python对私有的变量进行了更改,属于对私有变量的保护,可以通过student._Student__score
对私有变量进行访问
继承
-
避免定义重复的方法和变量
-
代码示例(建议一个模块一个类)
# 模块 c6.py class Human(): sum = 0 def __init__(self,name,age): self.name = name self.age = age def get_name(self): print(self.name)
# 模块 c5.py form c6 import People class Student(Human): // 继承:Peopel是弗雷,Student是子类 def __init__(self,school,name,age): self.school = school Human.__init__(self,name,age) // 显示的调用父类的构造函数,需要传递 self,只是普通方法调用的方式 def do_something(self): print('do something') student = Student('小学','xiao lei',23) pirnt(student.sum) // 0 pirnt(student.name) // xiao lei pirnt(student.age) // 23 pirnt(Student.sum) // 0 pirnt(student.get_name) // xiao lei
-
父类
- 子类1
- 子子类11
- 子子类12
- 子类2
- 子子类21
- 子子类22
- 子类1
-
在python中是允许一个子类可以有多个父类
子类方法调用父类方法:super关键字
-
上一节中子类调用父类的构造函数的手段不是太好
-
通过类名调用方法
Student.do_something(student)
,这样调用可以,但是不太好知道吗,self 变成了一个普普通通的参数 -
使用super调用父类的方法,以后优先使用super
- 子类的方法和父类同名,先调用子类的方法
- super不仅可以调用构造函数,而且可以调用普通方法
# 模块 c6.py class Human(): sum = 0 def __init__(self,name,age): self.name = name self.age = age def get_name(self): print(self.name) def do_something(self): print('do something 父类') ''' ''' # 模块 c5.py form c6 import People class Student(Human): // 继承:Peopel是弗雷,Student是子类 def __init__(self,school,name,age): self.school = school super(Student, self).__init__(name,age) // 通过super关键字调用父类的方法 def do_something(self): super(Student, self).do_something() // 子类的方法中调用父类的方法 print('do something 子类') student = Student('小学','xiao lei',23) pirnt(student.sum) // 0 pirnt(student.name) // xiao lei pirnt(student.age) // 23 pirnt(Student.sum) // 0 pirnt(student.get_name) // xiao lei student.do_something() # do something 父类 # do something 子类
正则表达式与JSON
初识正则表达式
-
定义:是一个特殊的字符序列,一个字符串是否与我们所设定的字符序列相匹配
-
快速检索文本,实现一些替换文本的操作
-
用途举例
- 检查数字是否是电话号码
- 检查email
- 替换指定的单词
-
使用(优先使用内置函数)
-
检查是否包含了Python
a = 'C|C++|Java|Python' a.index('Python') > -1 // True 使用python的内置函数 print('Python' in a) // True, in 操作符也能判断
-
-
re 模块操作内置函数
import re a = 'C|C++|Java|Python' r = re.findall('Python',a) // 在a中搜索Python,返回的是列表list的形式 if len(r) > 0: print('字符串中包含了 Python') else: print('not found')
-
正则表达式的灵魂在于规则
元字符与普通字符
-
提取数字
import re a = 'C6C++2Java3Python' r = re.findall('\d',a) // \d 表示数字 print(r) // ['6','2','3']
-
'Python'
普通字符,'\d'
元字符,正则表达式是由元字符来表示 -
元字符:百度百科正则表达式可以查看,分析业务需求再找单个的意义,最后组合起来
字符集
-
找出中间是c或者是f的单词,字符集用中括号包含 [ ]
import re s = 'abc,acc,cdc,aec,afc,ahc' r = re.findall('a[cf]c',s) print(r)
-
找出中间不是c或者不是f的单词
import re s = 'abc,acc,cdc,aec,afc,ahc' r = re.findall('a[^cf]c',s) // ^ 表示取反的意思 print(r)
-
匹配中间是c到f
import re s = 'abc,acc,cdc,aec,afc,ahc' r = re.findall('a[c-f]c',s) // - 表示c到f print(r)
概括字符集
- 如
\d
就是一个概括字符集,表示所有的阿拉伯数字0到9,\D
非数字 \d
=[0-9]
,\D
=[^0-9]
\w
字母和数字和下划线等字符,相当于[A-Za-z0-9_]
\W
匹配非单词字符\s
匹配['','\t','\n','r']
等这一类空白字符,\S
非空白字符- 字符集只能匹配单一的字符
数量词
-
匹配单词,数量词表示单个字符的数量
import re s = 'python 111java 3434php' r = re.findall('[a-z]{3,6}',s) // 匹配字符长度是3到6的单词,3,4,5,6个的都可以 print(r)
贪婪与非贪婪
- 上一节中,为什么匹配到三个之后还要往后匹配
- 贪婪(默认):将数量词限定在一个区间,会尽量去取最大的区间,所以要匹配出 python 然后结束,而不是 pyt 就重新匹配
- 非贪婪模式加上 ?:
r = re.findall('[a-z]{3,6}?',s)
,这样就 pyt 结束,然后 hon 就这样匹配下去
匹配0次1次或者无限次
python*
表示匹配0次或者无限多次 ***** 号前边的字符npython+
表示匹配1次或者无限多次 ***** 号前边的字符n,就是至少出现一次python?
表示匹配10次或者无限多1次 ***** 号前边的字符n,用来去重复比较好用
边界匹配符
-
^
从字符串的开始匹配,$
从字符串的末尾开始匹配,这两个字符一前一后 -
验证qq号,4到8位之间
import re qq = '100007891' r = re.findall('^\d{4,8}$',qq) // ^ 开始 $ 结束 print(r)
组
-
判断是否包含3个python
import re qq = 'pythonpythonpythonpython' r = re.findall('(python){3})',qq) // () 括号就是一个组 print(r)
-
[]
或关系和()
且关系,区分开
匹配模式参数
-
re.findall(param1,parame,param3)
param3是匹配模式 -
忽略大小写
import re qq = 'pythonC#pythonpythonpython' r = re.findall('c#',qq,re.I) // re.I 就是一种忽略大小写的模式 print(r)
-
多个模式的使用
r = re.findall('c#',qq,re.I | re.S)
,| 在这里表示且的关系,其中re.S就是指明.
号可以匹配所有的字符包括换行符\n
import re qq = 'pythonC#\npythonpythonpython' r = re.findall('c#.{1}',qq,re.I | re.S) // C#\n 会被匹配到 print(r)
-
.
匹配除换行符\n
之外的其他字符
re.sub正则替换
-
字符串的替换
r = re.sub(正则表达式,替换成的字符串,搜索的源字符串,最大的替换次数,匹配模式)
-
将C#替换成Go
import re qq = 'pythonC#\npythonpythonpython' r = re.sub('C#','Go',qq,1) // 替换一次 C# print(r) lan = qq.replace('C#','Go') // 新生成的 lan print(lan)
- count = 0 表示无限制的匹配下去
- count = 1 表示匹配第一个
-
sub强大的地方是第二个参数可以是一个函数,将函数作为参数
import re qq = 'pythonC#\npythonpythonpython' # 这个value是object对象,告诉的是匹配到的字符串在源字符串中的位置,span=(6,8)表示前边有6个字符,从第7个开始到8是匹配的字符 def convert(value): matched = value.group() // 获取到匹配到的字符串 return '!!' +mathced+ '!!' // 被动态替换 r = re.sub('C#',convert,qq,1) // 将匹配到的结果作为convert的参数传递过去,返回结果作为替换 print(r)
把函数作为参数传递
-
找出字符串中的所有数字,将大于等于6的数字替换成9,小于6替换成0
import re s = 'A8C3721D86' def convert(value): matched = value.group() if int(matched) >= 6: // 将字符转换成数字 return '9' // 返回的替换值必须是字符串形式 else: return '0' r = re.sub('\d',convert,s) print(r)
-
函数做参数,处理过程很灵活
search与match函数
-
这两个函数匹配成功一个就立刻停止了
-
match从字符串的首字母开始匹配
import re s = 'A8C3721D86' # 找到所有的数字 r = re.match('\d',s) print(r.span) // 返回位置 print(r) // 开始不是数字,返回空
-
search搜索整个字符串,返回搜索到的位置object对象
import re s = 'A8C3721D86' # 找到所有的数字 r = re.search('\d',s) print(r.group) // 8 print(r) // 第一个数字是8,返回结果object:span=(0,1) ...
group分组
-
分组的概念
-
提取life和python之间的字符和两个python之间的字符串
import re s = 'life is short,i use python,i love python' r = re.search('life(.*)python(.*)python',s) // 结果有两个分组 print(r.group(0)) // 0 是组号,默认是 0,始终记录的是完整的匹配结果 # life is short,i use python,i love python print(r.group(1)) // 正式获取组需要从 1 开始 # is short,i use print(rr.group(2)) # ,i love print(r.group(0,1,2)) # ('life is short,i use python,i love python','is short,i use',',i love') print(r.groups()) // 只有中间匹配的结果,没有完整的了 # ('is short,i use',',i love') rr = re.findall('life(.*)python',s) // 这里的结果也是组中的结果 print(rr) # ['is short,i use']
一些关于学习正则表达式的建议
- python用于爬虫,数据处理分析
- 正则表达式分析处理
理解JSON
-
什么是JSON:JavaScript Object Notation,JavaScript对象标记
-
本质:是一种轻量级的数据交换格式
-
字符串是JSON的表现形式
-
符合JSON格式的字符串叫JSON字符串(载体)
-
JSON VS XML
-
JSON的优势
- 易于阅读
- 易于解析
- 网络传输效率高
- 跨语言交换数据
-
XML
<?vml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body> Dont forget me this weekend! </body> </note>
-
JSON应用场景
- 网站后台到浏览器之间HTML,浏览器获取数据
反序列化
-
json字符串转换成python数据结构
import json json_str = '{"name":"qiyue","age":18}' // 符合json规范的字符串 student = json.loads(json_str) // 转换 print(type(student)) // dict 字典类型 print(student['name']) // 访问得到数据
-
不同的语言把json解析成不同的数据结构,如javascript中是对象,python中是字典dict也可能是别的,针对json数据的结构
-
json 小写的 false 解析成大写的 False
序列化
-
json和python的解析
json python object dict array list string str number int number float true True false False null None -
序列化就是转换成json格式的数据
import json student = { {'name':'xiao hua','age':11}, {'name':'xiao lei','age':12} } json_str = json.dumps(student) print(type(json_str)) print(json_str)
小谈JSON、JSON对象与JSON字符串
- json 对象
- 放在javascript中说json对象是成立的,但是别的语言可能不成立
- json有自己的数据类型,但是不等同于javascript的数据类型
- rest服务的标准格式
python的高级语法与用法
枚举其实是一个类
-
枚举的本质是一个类,python中的枚举是Enum的子类
- 枚举可读性高
- 标识名字大写
- 打印出来枚举的名字
- 重在标签而不是值
from enum import Enum class VIP(Enum): // 继承Enum类 YELLOW = 1 GREEN = 2 BLACK = 3 RED =4 print(VIP.YELLOW) // VIP.YELLOW 打印出的结果和普通的类是有区别的
枚举和普通类相比有什么优势
-
没有枚举怎么表示(最大的缺陷就是可变、没有防止相同标签的功能)
yellow = 1 green = 2 # dict a = {'yellow':1,'green':2} class TypeDiamond(): yellow = 1 green = 2
- 可以使用字典dict,最贴近于枚举
- 可以在类下边定义
- 模块中直接定义变量也可以
-
枚举特点
- 枚举中的值是不可以被更改的,是常量,保护功能
- 标签不能重复
枚举类型,枚举名称,枚举值
-
获取枚举具体的数值.value属性
-
.name获取得到标签的名字
from enum import Enum class VIP(Enum): // 继承Enum类 YELLOW = 1 GREEN = 2 BLACK = 3 RED =4 print(VIP.YELLOW) // VIP.YELLOW 打印出的结果和普通的类是有区别的 print(VIP.YELLOW.value) // 通过.value访问得到具体的值 1 print(VIP.YELLOW.name) // 通过.name访问得到标签的名字 YELLOW # 通过type()查看得到类型 print(VIP['GREEN']) // VIP.GREEN 打印枚举本身
-
注意:
- 枚举类型
- 枚举的名字
- 枚举的值
-
枚举的遍历
from enum import Enum class VIP(Enum): // 继承Enum类 YELLOW = 1 GREEN = 2 BLACK = 3 RED =4 for v in VIP: print(v) // 通过遍历获取到枚举下边每一种类型
枚举的比较运算
-
支持的比较
- 身份比较
- 等值比较
-
不支持的比较
- 不能做枚举和数值之间的比较
- 不能做枚举之间的大小比较
- 不同的枚举不支持相互之间的比较
from enum import Enum class VIP(Enum): // 继承Enum类 YELLOW = 1 YELLOW_ALIAS = 1 // 只是YELLOW的别名,不能代表独立的枚举类型 GREEN = 2 BLACK = 3 RED =4 result = VIP.YELLOW == VIP.GREEN // 等值比较 result = VIP.YELLOW is VIP.YELLOW // 身份比较
枚举注意事项
-
不能出现相同的标签
-
允许不同标签的数值相同,第二个标签只是第一个标签的别名
-
有相同值得时候遍历不会打印别名
-
需要打印别名得遍历
from enum import Enum class VIP(Enum): // 继承Enum类 YELLOW = 1 YELLOW_ALIAS = 1 // 只是YELLOW的别名,不能代表独立的枚举类型 GREEN = 2 BLACK = 3 RED =4 for v in VIP.__members__: print(v) // 得到所有标签得名字,包括别名 for v in VIP.__members__.items(): print(v) // 通过遍历获取到枚举下边每一个类型,得到得结果是元组(标签名称,取值)
枚举转换
-
理解可读性
-
from enum import Enum class VIP(Enum): // 继承Enum类 YELLOW = 1 GREEN = 2 BLACK = 3 RED =4 a = 1 print(VIP(a)) // VIP.YELLOW 获取得到枚举的类型
枚举小结
-
IntEnum 强制枚举下边全部是数字
from enum import IntEnum class VIP(IntEnum): // 继承IntEnum类 YELLOW = 1 GREEN = 2 BLACK = 3 RED =4
-
限制枚举的值不能相同
from enum import IntEnum,unique @unique class VIP(IntEnum): // 继承IntEnum类 YELLOW = 1 GREEN = 1 BLACK = 3 RED =4 # 运行程序之后就会报错,因为YELLOW和GREEN的值相同了
-
枚举实现是单例模式,不能对枚举进行实例化
-
了解23种设计模式
进阶内容开场白
- 之前都是入门,之后就是进阶的知识了
- 基础知识和高阶知识
- 高阶知识
- 包,类库的开发者
一切皆对象
-
淡化函数式编程
-
函数
-
其他语言
- 只是一段可执行的代码,并不是对象,不能实例化
-
python一切皆对象(结构)
- 函数可以实例化
- 把一个函数作为参数传递,或者作为函数的返回结果
a = 1 // 赋值的类型不指定 a = '2' a = def
-
什么是闭包
-
闭包的概念和作用域有关
-
函数的作用域
def curve_pre(): a = 25 // 环境变量 def cureve(x): return a*x*x return cureve // 函数作为返回结果 a = 10 f = curve_pre() // 函数赋值给变量 f(2) // 100
-
闭包
- 一个函数以及在它定义的时候外部的环境变量构成的一个整体叫做闭包
- 闭包=函数+环境变量
- 环境变量必须在函数的外部
- 环境变量不能是全局变量
- 闭包的环境变量保存在了
f.__closure__
内置的变量里边的 - 取出闭包的环境变量
f.__closure__[0].cell_contents
一个事例看看闭包
-
闭包的意义保存的是一个环境,函数调用的现场保存下来了,环境变量在函数中只能用于引用,而不能去赋值
-
def f1(): a = 10 def f2(): a = 20 // 这个语句的存在,此时的a不是去引用外部的环境变量,而是一个局部变量,所以这不是闭包 print(a) print(a) f2() print(a) f1() # 10 # 20 # 10
闭包的经典误区
-
以上代码不是闭包
def f1(): a = 10 def f2(): a = 20 // 这个语句的存在,此时的a不是去引用外部的环境变量,而是一个局部变量,所以这不是闭包 return c = 20 * a // 环境变量只能被引用不能被赋值 return f2 f = f1() print(f.__closure__)
出个题,用闭包解决
- 旅行者
- x表示起点 x = 0
- 通过函数知道旅行者当前所处的位置
我先用闭包解决一下
-
不用闭包
- 在go函数内部使用origin的时候,origin在函数定义的时候就声明了origin这个变量,所以python就认为函数内部有了roigin,就不会去调用外部的origin,所以函数运行的时候出错
origin = 0 def go(step): gobal origin // 进行全局声明,没有全局声明就会报错 new_pos = origin + step origin = new_pos return origin # 预期结果 print(go(2)) // 2 print(go(3)) // 5 print(go(6)) // 11
再用闭包解决一下
-
闭包实现(函数式编程)
origin = 0 def factory(pos): def go(step): nonlocal pos // nonlocal 强制声明pos不是本地的局部变量 new_pos = pos + step pos = new_pos return new_pos return go tourist = factory(origin) print(tourist(2)) print(tourist(3)) print(tourist(5)) # 2 5 10 print(tourist.__closure__[0].cell_contents) // 打印闭包的环境变量
-
闭包记忆住了上一次调用的状态
-
强调函数
小谈函数式编程
- 闭包
- python注重环境变量
- javascript注重的是简介通过函数的方式在外部调用函数内部的变量
- 注意内存泄漏的问题
- 闭包只是函数式编程的一种应用
函数式编程:匿名函数 (python中就是lambda表达式),高阶函数
lambda表达式( :后边是强调的是表达式)
-
匿名函数:在定义函数的时候不需要定义函数名字
# 普通函数 def add(x,y): return x+y # 匿名函数 lambda parameter_list: expression // expression只是简单的表达式 # 没有名字,没有参数列表,没有return,:后边的表达式作为返回结果 f = lambda x,y: x+y // 将匿名函数赋值给一个变量 a = f(1,2) print(a)
-
lambda定义的函数——> lambda 表达式
三元表达式
-
表达式版本的if else 语句
-
题目:x,y中 x>y取x 否则取y
-
其他语言
x > y ? x : y
-
python版本
# 伪代码 条件为真时返回的结果 if 条件判断 else 条件为假时返回的结果 x = 2 y = 1 r = x if x > y else y print(r) // 2
-
map
-
也是推荐的函数式编程的方式,有列表x的值,求y的值
- 常规方式
list_x = [1,2,3,4,5,6,7,8] def square(x): return x*x for x in list_x: square(x)
- map方式
list_x = [1,2,3,4,5,6,7,8] def square(x): return x*x r = map(square,list_x) // 对每一个x传到square函数中去 print(r) // map object print(list(r)) // 将map对象转换成list
map与lambda
-
map真正的使用方式,结合lambda表达式使用
- map使用的时候,lambda表达式的参数个数和map传递的参数个数必须一样
- map个结果个数取决于比较短的参数的个数
list_x = [1,2,3,4,5,6,7,8] list_y = [2,2,2,3,4,1,2,4] r = map(lambda x,y: x*x+y, list_x, list_y) // 使用lambda替换square函数 print(r) // map object print(list(r)) // 将map对象转换成list
reduce
-
导入reduce
from functools import reduce
-
reduce的使用
- reduce下的lambda表达式必须要有两个参数
- reduce做连续的计算
- 第一次取1和2得到3,reduce把3作为x传递给lambda表达式,然后y取第三个元素3
- y就是list_x第三个开始
- x是每次计算的结果
from functools import reduce list_x = [1,2,3,4,5,6,7,8] r = reduce(lambda x,y: x+y, list_x) print(r) m = reduce(lambda x,y: x+y, list_x, 10) // 第三个参数10是作为初始值,计算第一次就是10+1,第二次计算11+2 print(r) (((1+2)+3)+4)...
-
reduce做连续计算,连续调用lambda表达式
-
map/reduce编程模型 (映射、归约),大数据中的并行计算
filter
-
过滤
-
lambda 表达式返回的结果必须是能代表True或者False的,
-
filter通过返回的boolean值决定元素是否还被保留在集合里
- 剔除0元素
list_x = [1,0,0,1,1,0] r = filter(lambda x: True if x ==1 else False, list_x) print(list(r)) // [1,1,1] m = filter(lambda x: x, list_x) print(list(m)) // [1,1,1]
命令式编程vs函数式编程
- 常用的是命令式编程
- 之前用的map,reduce,filter,lambda是函数式编程
- 命令式编程的特征
- def
- if else
- for
- 函数式编程
- map,reduce,filter
- lamdba 算子
- lisp函数式编程的鼻祖语言
装饰器一
-
装饰器就是java中的注解
import time def f1(): print(time.time()) print('this is a function') # unix 时间戳 f1()
-
如果有很多函数需要打印时间怎么办?解决这个问题,专门编写一个函数打印时间
import time def print_current_tiem(func): print(time.time()) func() def f1(): print('this is a function') def f2(): print('this is a function') print_current_tiem(f1) print_current_tiem(f2)
-
-
对修改时封闭的,对扩展是开放的(原则)
-
unix 时间戳
- unix时间戳(unix timestamp)
- unix时间(unix time)
- POSIX时间,是一种时间表示方法
- 定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数
装饰器二
-
装饰器只是一种模式
import time def decorator(func): // 装饰 def wrapper(): // 封装 print(time.time()) func() return wapper def f1(): print('this is f1') f = decorator(f1) f()
装饰器三
-
语法糖 @ 真正体现了装饰器
import time def decorator(func): // 装饰 def wrapper(): // 封装 print(time.time()) func() return wapper @decorator // 使用语法糖 @,保证原来的调用不变 def f1(): print('this is f1') f()
-
ES6中可以只用class定义javascript类
装饰器四
-
支持一个参数的装饰器
import time def decorator(func): // 装饰 def wrapper(func_name): // 封装 print(time.time()) func(func_name) return wapper @decorator // 使用语法糖 @,保证原来的调用不变 def f1(func_name): print('this is f1' + func_name) f1('test')
-
支持多个参数的装饰器
import time def decorator(func): // 装饰 def wrapper(*args): // 参数列表 print(time.time()) func(*args) return wapper @decorator // 使用语法糖 @,保证原来的调用不变 def f1(func_name): print('this is f1' + func_name) @decorator def f2(func_name1,func_name2): print('this is f1' + func_name1 +':'+ func_name2) f1('test') f2('test1','test2')
装饰器五
-
优化
import time def decorator(func): // 装饰 def wrapper(*args, **kw): // 参数列表 print(time.time()) func(*args, **kw) return wapper @decorator def f2(func_name1,func_name2,**kw): // ** 表示任意个关键字参数 print('this is f1' + func_name1 +':'+ func_name2) print(kw) f2('test1','test2', a=1,b=2,c='123')
装饰器六
-
装饰器的基本思想通过装饰器改变函数的行为,而不去改变实现
-
装饰器体现了代码的复用,不需要破坏代码的实现
-
代码欣赏
# 使用flask框架 @api.route('/get',methods=['GET']) def test_javascript_http(): p = request.args.get('name') return p,200 @api.route('/psw',methods=['GET']) @auth.login_required def get_psw(): p = request.args.get('psw') r = generate_password_hash(p) return 'aaaaaa',200
实战:原生爬虫
分析抓取的确定抓取页面
- 目的:爬取熊猫tv某一个游戏下边主播人气排行
整理爬虫常规思路
- 网页的现实原理
- chrome浏览器F12
- 如何解析HTML页面,使用正则表达式进行解析
- 爬虫思路
- 明确目的
- 找到数据对应的网页
- 分析网页结构找到数据所在的标签位置
- 编码
- 模拟http请求,向服务器发送这个请求,获取到服务器返回的html
- 正则表达式提取数据(名字,人气)
vscode中调试代码
- 断点调试
- F5启动程序,(从一个断点跳到下一个断点也是这个快捷键)
- F10一步一步的走程序
- F11跳转到函数的内部
- 鼠标停在代码上查看变量的值
html结构分析基本原则二条
- 寻找标签和标识符定位需要的信息
- 原则
- 尽量选择唯一性的标签
- 尽量选择需要提取数据的标签
数据提取层级分析及原则三条
- 首先选择
<div class="video-info">...</div>
- 再
<span class="video-nickname" title="LPL熊猫官方直播2台">...</span >
主播姓名 - 最后
<span class="video-number">40.5万</span>
观看人数 - 思维导图
- htmls
- video-info 一个主播
- video-nickname 主播姓名
- video-number 观看人数
- video-info 一个主播
- htmls
正则分析html
- video-info:
root_parttern = '<div class="video-info">([\s\S]*?)</div>'
- 使用正则表达式
- 只需要中间的匹配使用 分组 ([\s\S]*?) 的概念
正则分析获取名字和人数
name_pattern = '</i>([\s\S]*?)</span>'
名字number_pattern = '<span class="video-number">([\s\S]*?)</span>'
人数
数据精炼
- 获取的数据进行规范化
sorted排序
sorted(需要排序的立标, key = 指定的排序关键字, reverse = 是否将顺序反转)
案例总结
- 准确的注释和描述
- 适当的空行
- 总结
- 这个代码例子没有思想
- 抵御业务需求的能力太差
- 中大型爬虫
- Beautiful Soup库、scrapy框架
- 爬虫最难的地方是反爬虫、反反爬虫
- 数据分析、数据挖掘
- ip 可能被封
- 爬虫代码
'''
模块注释:爬虫例子
'''
import re
from urllib import request
class Spider():
'''
类注释:爬虫类
'''
# 网页
url = 'https://www.panda.tv/cate/lol'
'''
爬取的位置匹配
'''
root_parttern = '<div class="video-info">([\s\S]*?)</div>'
name_pattern = '</i>([\s\S]*?)</span>'
number_pattern = '<span class="video-number">([\s\S]*?)</span>'
def __fetch_content(self):
'''
方法的注释:爬取网页
'''
r = request.urlopen(Spider.url)
htmls = r.read()
htmls = str(htmls, encoding='utf-8')
return htmls
def __analysis(self, htmls):
'''
分析方法
'''
root_html = re.findall(Spider.root_parttern, htmls)
anchors = []
for html in root_html:
name = re.findall(Spider.name_pattern, html)
number = re.findall(Spider.number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
return anchors
def __refine(self, anchors):
'''
精练(去掉空格换行符,列表转换)
'''
l = lambda anchor: {
'name':anchor['name'][0].strip(),
'number':anchor['number'][0]
}
return map(l, anchors)
def __sort(self, anchors):
'''
排序
'''
# 默认是升序排列,revers 参数调整
anchors = sorted(anchors, key = self.__sort_seed, reverse = True)
return anchors
def __sort_seed(self, anchor):
'''
对排序指定的key编写比较的函数,比较的种子
'''
# 提取数字
r = re.findall('\d*', anchor['number'])
number = float(r[0])
if '万' in anchor['number']:
number *= 10000
return number
def __show(self, anchors):
'''
展示数据
'''
for rank in range(0, len(anchors)):
print('rank---' + str(rank + 1)
+ '---' + anchors[rank]['name']
+ '---' + anchors[rank]['number'])
def go(self):
'''
总控方法
'''
htmls = self.__fetch_content()
anchors = self.__analysis(htmls)
anchors = list(self.__refine(anchors))
anchors = self.__sort(anchors)
self.__show(anchors)
# 实例化对象
spider = Spider()
# 测试爬虫
spider.go()
pythonic与python杂技
导言
- 把写代码当作兴趣
- 注意方法定义和调用的顺序
用字典映射代替switch case语句
day = 0
def get_sunday():
return 'Sunday'
def get_monday():
return 'Monday'
def get_tuesday():
return 'Tuesday'
def get_default():
return 'Unknow'
switcher = {
0: get_sunday,
1: 'Monday',
2: 'Tuesday',
}
day_name = switcher.get(day, get_default)() // get方法第二个参数指定没有day值的时候,类似于default,这里返回的是方法,所以最后需要加上括号
day_name = switcher.get(day, 'unknow') // 注意字典得取值是方法或者是值的区别
列表推导式
- 已经存在一个列表,想创建一个新的列表
- set集合、dict字典、tuple元组都可以被推导
a = [1,2,3,4,5,6,7,8]
b = [i**2 for i in a] // 这就是列表推导式i**2表示i的2次方
print(b) // 结果是列表
c = [i**2 for i in a if i >= 5] // 有条件的筛选,对i>=5的i操作
print(c) // 结果是列表
a1 = {1,2,3,4,5,6,7,8}
d = {i**2 for i in a1 if i >= 5} // 使用[]得到列表,{}得到集合
print(d) // 结果是集合
字典dict如何编写列表推导式
student = {
'喜小乐':12,
'小明子':18
}
b = {value:key for key,value in student.items()} // 颠倒字典
c = (key for key,value in student.items()) // 得到元组,不建议元组,建议列表[]
for x in c:
print(x)
None
- 表示空,空就是空,是NoneType类型
- 不等于空字符串、空的列表、0、False,从类型和值上都不等于
- 判空操作不推荐
a==None
,而是推荐if a
或者if not a
对象存在并不一定是True
-
None 对应的是False
-
自定义对象
class Test(): def __len__(self): return 0 test = Test() if test: print('s') // 不会打印出来,即使test是一个实例化对象,test对象存在也有可能被转换成false class Test(): pass test = Test() if test: print('s') // s
-
和类中定义的两个方法有关系
__len__
与__bool__
内置方法
-
没有bool和len方法的时候默认是True
class Test(): pass test = Test() if test: print('s') // s
-
定义了boo和len
class Test(): def __bool__(self): return False def __len__(self): return 0 // 取非0的时候就是True,这里可以返回整形数字或者boolean值True、False test = Test() len(test) // 0 调用内置的len函数的时候会去调参数内部的len方法 if test: print('s') // 根据具体情况判断是false还是true
-
如果类中没有定义bool,定义了len,最后是true还是false是由len决定的
-
两者都存在的时候由bool方法决定对象的取值
装饰器的副作用
-
装饰器改变了函数名字
import time def decorator(func): def wrapper(): print(time.time()) func() return wapper @decorator def f1(): print('this is f1') print(f1.__name__) f() //注释掉装饰器 # @decorator 的时候打印出 f1 // 加了装饰器之后, 函数的名字叫 wrapper 了,函数名字被改变会影响代码
-
函数的描述出现问题
import time def decorator(func): def wrapper(): print(time.time()) func() return wapper @decorator def f1(): ''' 描述 ''' print('this is f1') print(f1.__name__) print(helo(f1)) // 由于增加了装饰器,查看不到函数的描述文档了
-
如何解决装饰器的副作用
@wraps(func)
// 这个装饰器很重要import time from functools import wraps def decorator(func): @wraps(func) // 这个装饰器很重要 def wrapper(): print(time.time()) func() return wapper @decorator def f1(): ''' 描述 ''' print('this is f1') print(f1.__name__) print(helo(f1)) // 这样就变正确了