《python全栈开发》学习笔记

python全栈开发

代码规范

PEP8 第8篇

PEP-8 网址:https://www.python.org/dev/peps/pep-0008/

注释

  • 单行 #
  • 多行 双引号"…" or三引号 ‘’’…’’’

变量

变量名

  • 字母 数字 下划线组成 下划线不可开头 大小写敏感
    命名法:大驼峰 小驼峰 posix
    • 大驼峰命名给类用
  • age = 21
    var1 = var2 = var3 = var_value
    var1, var2, var3 = 1, 2, 3

变量类型

  • 严格来讲 python 只有一个类型
  • 标准意义:6种
    数字/str/list/tuple/dict/set
  • python数字没有大小限制

常见数字分类

  • 整数
    • 二进制 0b / 八进制 0o / 十六进制 0x
  • 浮点数
    • 简写 0.2 = .2
  • 科学计数法
    • e后的数字表示10的指数
    • 3145 = 3.145e3
  • 复数
    • 虚部用j/J表示
  • 布尔值
    • Ture/False 1/0
    • 在python中可以当数字使用 True = 1 False =0
    • 数字可当布尔值使用 0为False 除0外的数为True
  • 字符串
    • 单引号 双引号 都一样 只可表示一行
    • 三引号
      " " "



      " " " 多行信息
  • None类型
    • 无。 通常用来占位
      用于返回一个空

可变vs不可变数据类型

  • 可变数据类型: list, dict, set 数据一旦发生变化,不会在内存中开辟一个新的存储空间来存储新的对象
  • 不可变数据类型:tuple,str 数据,数字int,float,bool,complex一旦发生变化,就会在内存中开辟一个新的存储空间用于存储新的对象,原来的变量名会指向一个新的存储地址

表达式

运算符

算数运算符

  • 加+ 减 - 乘 * 日常运算一样
  • 普通除法 / python2.x 和 3.x 不同 3.x 得数为小数
    地板除/整数除 // 得数取整
    取模 % %在C++/java 为取余 python为取模
    取模和取余

对于整型数a,b来说,取模运算或者求余运算的方法都是:
1.求 整数商: c = a/b;
2.计算模或者余数: r = a - c*b.
求模运算和求余运算在第一步不同: 取余运算在取c的值时,向0 方向舍入(fix()函数);而取模运算在计算c的值时,向负无穷方向舍入(floor()函数)。

  • 指数 **
  • python无自增自减运算符

比较/关系运算符

  • 等于 ==;不等于 != ; > ; >= ;< ; <=

赋值运算符

  • =
  • a = 2 < 3 print(a) True
  • a = b = 9
  • a, b = 1, 9
  • c = c + 1 c += 1

逻辑运算符

  • and / or / not
  • 计算时 可将 and当作乘法 or 当作加法

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

位运算

  • ~ 按位翻转
  • “ + ”
  • “ - ”
  • “>>”
  • “<<”
  • “&”
  • “|”
  • “^” 异或

成员运算符

  • in
  • not in

身份运算符

  • 确定两个变量是不是同一个变量
  • is
  • is not
  • 整数 [-5,256] python放入内存 变量不会变化

运算符优先级

  • 小括号最高优先级

程序结构

三种程序结构

  • 顺序/循环/分支

分支结构

  • if
  • 双向分支 if else
  • 多路分支 if elif… else
  • python无switch

* input()

  • 从屏幕上接收输入的信息 返回数据为字符串类型
  • input(括号内输入提示)

循环结构

for

  • for 变量 in 队列

for…else

  • for循环结束 需要进行一些收尾工作 需要else

break continue pass

  • break 无条件结束整个循环
  • continue 继续
  • pass 占位符 无跳过功能

range 函数

  • 生成有序数列
  • 生成数字队列可以定制
    • range(1,101) 1-100 [)
    • range python2和3不一样 2生成列表对象 3生成可迭代对象

while

  • 满足条件即循环 适用不知道循环次数的问题

函数

  • 先定义 再调用
  • 只定义不会被执行

函数的参数和返回值

参数

  • 传递函数一些必要的信息
    • 形参 函数定义时的占位符
    • 实参 调用函数输入的值

返回值

  • 调用函数的执行结果
    • 使用return返回结果
    • 无值可以返回 return None 表示函数结束
    • 一旦有return语句 函数立即结束
    • 没有return 默认返回None
  • 函数和过程的区别 有无返回值

* print()

  • print(,end = " ")
  • end 控制打印间隔 默认换行

参数详解

参数分类

  • 普通参数/位置参数
    • def normal_para(p1, p2, p3,…)
      func_body
      调用:
    • 调用的时候 具体值参考的是位置 按照位置赋值
      func(v1,v2,v3,…)
  • 默认参数
    • 形参带有默认值
    • 调用的时候 如果没有相对应形参赋值 则使用默认值
    • def default_para(p1 = v1, p2 = v2,…)
      func_body
      调用:
      • func()
      • value1 = 100
        value2 = 200
        func(value1,value2)
  • 关键字参数
    • def keys_para(one, two, three)
      调用时直接用名字赋值 以防位置记错
      keys_para(three=3, two=1, one=2)
  • 收集参数
    • 把没有位置 不能和定义时的参数位置相对应的参数 放入一个特定的数据结构中

关键字参数

  • 语法
    def func(p1 = v1, p2 = v2…):
    func_body
    调用函数:
    func(p1 = value1, p2 = value2…)
    调用函数:
  • 比较麻烦 但也有好处
    • 不容易混淆 一般实参和形参只是按照位置一一对应即可 容易出错
    • 使用关键字参数 可以不考虑参数位置

收集参数

  • 语法
    def func(*args):
    func_body
    调用:
    func(p1,p2,p3,…)
  • 参数名args不是必须这么想 但是约定俗成用
  • 参数名args前需有*
  • 收集参数可以和其他参数共存
# 收集参数代码
# 函数模拟一个学生进行自我介绍 但具体内容不清楚
#args把它看作一个tuple
def stu( *args):
	print("hello everyone 我简单自我介绍一下:")
	# type作用检测变量类型
	print(type(args))
	for item in args:
		print(item)
	
stu("liuming",18,"heu","wangxiaojing","single")
stu("zyx")
hello everyone 我简单自我介绍一下:
<class 'tuple'>
liuming
18
heu
wangxiaojing
single
hello everyone 我简单自我介绍一下:
<class 'tuple'>
zyx
# 收集参数实例
# 收集参数可以不带任何实参调用 此时收集参数为空tuple
stu()
hello everyone 我简单自我介绍一下:
<class 'tuple'>

收集参数之关键字收集参数

  • 把关键字参数按照字典格式存入收集参数
  • 语法
    def func( **kwargs):
    func_body
    调用:
    func(p1 = v1, p2 = v2, p3 = v3,…)
    • kwargs一般约定俗成
    • 调用时 将多余的关键字参数放入kwargs
    • 访问kwargs需要按照字典格式访问
def stu( **kwargs):
	print("hello let introduce myself:")
	print(type(kwargs))
	for k,v in kwargs.items():
		print(k,"----",v)

stu(name="liuying", age = 19, addr = "heu", lover = "tom" , work = "boss")
print("*"*50)
stu(name = "zhoujie")
hello let introduce myself:
<class 'dict'>
name ---- liuying
age ---- 19
addr ---- heu
lover ---- tom
work ---- boss
**************************************************
hello let introduce myself:
<class 'dict'>
name ---- zhoujie

收集参数混合调用的顺序问题

  • 收集参数 关键字参数 普通参数可以混合使用
  • 使用规则是 普通参数和关键字参数优先
  • 定义的时候一般找普通参数 关键字参数 收集参数tuple 收集参数dict
# stu模拟一个学生的自我介绍
def stu(name, age,*args, hobby = "没有",  **kwargs):
	print("hi")
	print("i am {0}, i am {1} years old".format(name,age))
	if hobby == "没有":
		print("i have no hobby")
	else:
		print("my hobby is {0}".format(hobby))
	print("~"*20)
	for k,v in kwargs.items():
		print(k,"---",v)

# 开始调用函数
name = "zero"
age = 10
# 调用的不同格式
stu(name, age)

stu(name, age, hobby = "swim")

stu(name,age,"xiaojing","stone",hobby = "swim", hobby2 = "cook", hobby3 = "movies")
hi
i am zero, i am 10 years old
i have no hobby
~~~~~~~~~~~~~~~~~~~~
hi
i am zero, i am 10 years old
my hobby is swim
~~~~~~~~~~~~~~~~~~~~
hi
i am zero, i am 10 years old
my hobby is swim
~~~~~~~~~~~~~~~~~~~~
hobby2 --- cook
hobby3 --- movies

收集参数的解包问题

  • 把参数放入list或dict中 直接把list/dict中的值放入收集参数中
  • 语法 参看案例
  • 对list类型解包用*
  • 对dict类型解包用**
def stu(*args):
	print("hahahaha")
	# n表示循环次数
	n = 0
	for i in args:
		print(type(i))
		print(n)
		n += 1
		print(i)
 
l = ["a",18,29,"cc"]
stu(l)
# 此时 args表示好形式是字典内一个list类型的元素 

stu(*l)
# 此时调用需要解包符号 在前面加一个*
hahahaha
<class 'list'>
0
['a', 18, 29, 'cc']

hahahaha
<class 'str'>
0
a
<class 'int'>
1
18
<class 'int'>
2
29
<class 'str'>
3
cc

函数文档

  • 函数文档的作用是对当前函数提供使用相关的参考信息
  • 文档的写法
    • 在函数内部开始的第一行使用三引号
    • 一般具有特定格式
  • 文档查看
    • 使用help函数 形如 help(func)
    • 使用__doc__
# 文档案例
# 函数stu是模拟一个学生自我介绍的内容
def stu(name, age, *args):
	'''
	line1
	line2
	line3
	'''
	print("this is func stu")
# 查看函数文档
#1
help(stu)
#2
stu.__doc__
Help on function stu in module __main__:

stu(name, age, *args)
line1
line2
line3

'\n\tline1\n\tline2\n\tline3\n\t'

变量作用域

  • 变量由作用范围限制

变量分类

  • 按照作用域分类
    • 全局 global :在函数外部定义
    • 局部 local :在函数内部定义

变量的作用范围

  • 全局变量:在整个全局范围都有效
  • 全局变量在局部可以使用 (即函数内部可以访问函数外部定义的 变量)
  • 局部变量在局部范围可以使用
  • 局部变量在全局范围无法使用

LEGB原则

  • L local 局部作用域
  • E Enclosing function locale 外部嵌套函数作用域
  • G Global module 函数定义所在模块作用域
  • B Buildin python内置模块的作用域

globals locals函数

  • 可以通过globals和locals显示出局部变量和全局变量
a = 1
b = 2

def fun(c,d):
	e = 111
	print("local={0}".format(locals()))
	print("globals={0}".format(globals()))

fun(100,200)

在这里插入图片描述

eval()

  • 把一个字符串当成一个表达式来执行 返回表达式执行后的结果
  • 语法
    eval(string_code, globals = None, locals = None)

exec()

  • 与eval功能类似 但不返回结果
  • 语法
    exec(string_code, globals = None, locals = None)
x = 100
y =200
# 执行x+y
# z = x+y
z1 = eval("x+y")
z2 = exec("x+y")
print(z1)
print(z2)
z3 = exec("print('x+y:',x+y)")
300
None
x+y: 300

str字符串

str

  • 表示文字信息
  • 用单引号 双引号 三引号括起来
  • 三引号可包含多行信息

转义字符

  • 用特殊方法表示不好表示的内容 如回车键 换行符等
  • 借助反斜杠\ 进行转义
  • 不同系统对换行操作转义不同
    • win: \n
    • Linux: \r\n
      -单个斜杠用法
      python里,单个反斜杠表示此行为结束 出于美观 下一行继续

格式化

  • 把字符串按照一定格式进行打印或填充

格式化分类

传统格式化

  • 使用%进行格式化 %为占位符
    • %s 字符串
    • %d 十进制整数
s = "hello %s"
print(s) 
print(s%"world")
hello %s
hello world  	
s = "forever %d"
print(s%21) 
forever 21
s = "%fkg %fm"
# 格式化信息多于一个 用括号括起来  
#默认格式打印
print(s%(50.5,1.65))
#规定小数点后位数
s = "%.2fkg %.2fm"
print(s%(50.5,1.65))
50.500000kg 1.650000m  
50.50kg 1.65m

format

  • 使用函数形式进行格式化 代替%
# method1
s = "{} {}!"
print(s.format("hello","world"))
# method2
s = "{} {}!".format("hello","world")
print(s) 
# 设置指定位置
s = "{1} {0}".format("hello","world")
print(s)
# 使用命名参数
s = "我是{name},我的口号是{motto}"
s = s.format(name="zyx",motto="nobody is perfect")
print(s)
# 通过字典设置参数,需要解包 使用命名参数
s = "我是{name},我的口号是{motto}"
s_dict = {"name":"zyx","motto":"nobody is perfect"}
# **是解包操作
s = s.format(**s_dict)
print(s)
hello world!
hello world!
world hello
我是zyx,我的口号是nobody is perfect
我是zyx,我的口号是nobody is perfect
# 对数字的格式化需要用到
s = "I am {:.2f}m, {:.2f}kg"
print(s.format(1.65,50.5)) 
# ^, < , > 分别为居中 左对齐 右对齐 后面带宽度
# :后面带填充的字符 只能是一个字符 不指定则默认是空格填充
# +表示在正数前显示+ 复数前显示- (空格)表示在正数前加空格
# b d o x 分别是 二 十 八 十六进制
# 用{}转义{}
I am 1.65m,50.5kg

str内置函数

字符串查找类

  • find 查找字符串中是否包含一个子串
s = "zhao qian sun li"
s1 = "zhao"
# 返回第一次发现这个字符串的位置
s.find(s1) 
# 返回-1表示没有找到
s2 = "kai"
s.find(s2)
  • index找到子串返回位置 没有找到子串的时候会报错
# 使用的时候可以使用区间
s = "zhao qian sun li and zhou wu zheng"
s1 = "zhao"
s.find(s1,25)
  • lfind rfind 从左开始查找 从右开始查找

判断类函数

  • isalpha 判断是否是字母
    • 默认字符串至少包括一个字符 否则返回False
    • 汉字认为是alpha 此函数不可区分英文字母与汉字 区分中英文应使用unicode码
  • isdigit, isnumeric, isdecimal 判断数字
  • 此类函数不建议使用 后期爬虫中 判断是否是数字建议采用正则表达式
    • isdigit:
      True: Unicode数字 byte数字(单字节) 全角数字(双字节) 罗马数字
      False: 汉字数字
      Error: 无
    • isnumeric:
      True: Unicode数字 全角数字(双字节) 罗马数字 汉字数字
      False:无
      Error: byte数字(单字节)
    • isdecimal
      True:Unicode数字 全角数字(双字节)
      False:罗马数字 汉字数字
      Error: byte数字(单字节)

内容判断类

  • startswith/endswith 是否以xxx开头或结尾
    • 检测某个字符串是否以某个子串开头 常用三个参数
    • suffix 被检查的字符串 必须有
    • start 检查范围的开始
    • end 检查范围的结束
  • islower/isupper 判断字母是否小写/大写

操作类函数

  • format 格式化
  • strip 主要作用是删除字符串两边的空格 可自定义删除字符串两边的字符 默认是空格
    lstrip和rstrip 指删除左边和右边的指定字符
  • join 对字符串进行拼接
# 使用s1作为分隔符 把ss内的内容拼接在一起
s1 = "-"
ss = ["a","b","c"]
print(s1.join(ss))
a-b-c

list列表

  • 一组由有序数据做成的序列
    • 数据就有先后顺序
    • list内数据可以不是一个类型数据

list的创建

  • 直接创建 用中括号 内容用英文逗号隔开
  • 使用list创建
# 创建空列表
L = []
# 直接赋值创建列表
L1 = [1,2,3,4,5]
# 列表内数值可以不是一个类型
L2 = [1,2,3,"a","b"]
# 创建列表第二种方式
L3 = list()
s = "a bc"
L4 = list(s)
print(L4)
#若只想创建包含一个字符串的列表使用 L = [s]
['a',' ','b','c']
# for 创建
a = ['a','b','c']
b = [ i for i in a]
print(b)
['a', 'b', 'c']
# 对a中所有元素乘以10 生成一个新list
a = [1,2,3,4,5]
# 用list a创建一个list b
# 下面代码的含义是 对于所有a中的元素 逐个放入新列表b中
b = [i*10 for i in a]
print(b)
[10,20,30,40,50]
# 可以过滤原来list中的内容并放入新列表中
a = [x for x in range(1,11)]
b = [m for m in a if m%2 == 0]
print(b)
[2,4,6,8,10]
# 列表生成可以嵌套
a = [i for i in range(1,4)]
print(a)
b = [i for i in range(100,400) if i%100 == 0]
print(b)
# 两个for循环嵌套
c = [m+n for m in a for n in b]
print(c)

# 嵌套的列表生成式加条件表达式
d = [m+n for m in a for n in b if m+n<250]
print(d)
[1,2,3]
[100,200,300]
[101,201,301,102,202,302,103,203,303]
[101,201,102,202,103,203]

内置函数
help 帮助函数
type 显示变量的类型
id 显示变量的id
print

列表的常见操作

访问

  • 使用下标操作 即索引
  • 列表元素索引从0开始

切片操作

  • 对列表进行截取
  • 切片时注意取值范围 左包括右不包括
  • 切片后生成的是全新的列表
  • L[ : ]
# 切片时注意取值范围 左包括右不包括
L1 = [1,2,3,4,5,6,7,8,9,10]
print(L1[1:6])
# 通过内置函数id可以判断切片生成全新的列表
# ID的作用是判断两个变量是否是一个变量
L2 = L1[0:10]
print(id(L1))
print(id(L2))
# 切片下标可以为空
L1[:4]
L1[1:]
L1[:]
#切片可以定义步长 默认为1
print(L1[::1])
print(L1[::2])
# 下标可以超出范围 超出后不再考虑多余下标内容
print(L1[:100])
# 默认分片从左向右截取 即正常情况 分片左边的值一定小于右边的值
# 下标值为负数 表明顺序从右往左 数组最后一个为-1
print(L1[-2:-5])
print(L1[-2:-5:-1])

列表的常用函数

len

  • 求列表长度

max

  • 求列表中最大值
a = [x for x in range(1,100)]
print(len(a))

print(max(a))

b = ['man','film','python']
print(max(b))
99
99

list

  • 将其他格式的数据转换成list
a = [1,2,3]
print(list(a))

s = "I love you!"
print(list(s))
[1, 2, 3]
['I', ' ', 'l', 'o', 'v', 'e', ' ', 'y', 'o', 'u', '!']
# 把range产生的内容转换成list
print(list(range(12,19)))
[12, 13, 14, 15, 16, 17, 18]

append

  • 在末尾追加内容
a = [i for i in range(1,5)]
print(a)
a.append(100)
print(a)
[1, 2, 3, 4]
[1, 2, 3, 4, 100]

insert

  • 指定位置插入
  • insert(index, data) 插入位置是index
print(a)
a.insert(3,666)
print(a)
[1, 2, 3, 4, 100]
[1, 2, 3, 666, 4, 100]

pop

  • 把最后一个元素取出
print(a)
last_ele = a.pop()
print(last_ele)
print(a)
[1, 2, 3, 666, 4, 100]
100
[1, 2, 3, 666, 4]

remove

  • 在列表中删除指定的值的元素
print(a)
print(id(a))
a.remove(1)
print(a)
print(id(a))
[1, 2, 3, 4, 666]
4373626952
[1, 2, 3, 4]
4373626952

clear

  • 清空
a.clear()
print(a)
[]

reverse

  • 原地翻转列表内容
a = [1,2,3,4]
a.reverse()
print(a)
[4,3,2,1]

extend

  • 扩展列表
  • 两个列表 将一个列表直接拼接到后一个上
a = [1,2,3]
b = [4,5,6]
a.extend(b)
print(a)
[1,2,3,4,5,6]

count

  • 查找列表中指定元素值的个数
a = [1,2,3,1,1]
num = a.count(1)
print(num)
3

copy 浅拷贝

  • 拷贝 浅拷贝
a = [1,2,3,4,5,666]
print(a)
# list类型 简单赋值操作 是传地址
# 改变b a也改变了
b = a
b[3] = 777
print(a)
print(id(a))
print(b)
print(id(b))
print("~"*20)
# 为了解决以上问题 list赋值需要采用copy函数
b = a.copy()
print(a)
print(id(a))
print(b)
print(id(b))
print("*"*20)
b[3] = 888
print(a)
print(b)
[1, 2, 3, 4, 5, 666]
[1, 2, 3, 777, 5, 666]
4371798984
[1, 2, 3, 777, 5, 666]
4371798984
~~~~~~~~~~~~~~~~~~~~
[1, 2, 3, 777, 5, 666]
4371798984
[1, 2, 3, 777, 5, 666]
4373523464
********************
[1, 2, 3, 777, 5, 666]
[1, 2, 3, 888, 5, 666]
# 深拷贝和浅拷贝的区别
a = [1,2,3,[10,20,30]]
b = a.copy()
print(id(a))
print(id(b))
# 双层列表只拷贝了一层 即浅拷贝
# 深拷贝需要使用特定工具
print(id(a[3]))
print(id(b[3]))
a[3][2] = 666
print(a)
print(b)
4372425928
4373338696
4373523464
4373523464
[1, 2, 3, [10, 20, 666]]
[1, 2, 3, [10, 20, 666]]

tuple

  • 可以理解为不允许更改的列表

tuple的创建

# 1.直接用小括号创建
T1 = ()
print(type(T1))
T2 = (100) 
print(type(T2))
# 创建一个元素的元组需加个逗号分隔 表示是元组
T3 = (100, )
print(type(T3))
T4 = (100, 200, 300)
print(T4)
 class `tuple`
 class `int`
 class `tuple`
 (100,200,300)
# 2.直接用逗号
T1 = 100print(type(T1))
T2 = 100, 200, 300, # 后面可以加或不加逗号
print(type(T2))
class `tuple`
class `tuple`
# 3.使用tuple定义
T1 = tuple()
print(T1)

li = [1,2,3,"zyx"]
t2 = tuple(li) #要求tuple参数必须可迭代

tuple其余特征

  • 有序
  • 可以访问不可以被修改
  • 元素可以是任意类型
  • list所有特性除了可修改外 元组都具有
  • 也就意味着 list的一些操作 如索引/分片/序列相加/相乘/成员资格操作等 一摸一样
# tuple索引操作
L1 = ["i","love","zyx"]
print(L1)
T1 = tuple(L1)
print(T1[2])
["i","love","zyx"]
zyx
# tuple的分片操作
T1[:]
T1[:2]
T1[-1::-1 ]
("i","love","zyx")
("i","love")
("zyx","love","i ")
# 元组相加
T1 = (100,200,300)
T2 = ("i","love","zyx")
T3 = T1 + T2
print(T3)
(100,200,300,"i","love","zyx")
# tuple乘法
T3 = T1*2
(100,200,300,100,200,300)
# tuple 成员检测
if100in T1:
	print("right")
right
 # 元组遍历
for i in T1:
	print(i)
100
200
300
# 嵌套元组的访问
T1 = ((10,20,30),("i","love","zyx"),(100,200,300))
# 1.双层循环访问
for i in T1:
	print(i)
	for j in i:
		print(j)

# 2.使用单层循环
for i,j,k in T1:
	print(i,j,k)
# 上面访问有一个规定,即ijk要跟元组的元素个数对应 

常用元组函数

 # 常用元组函数
T1 = (1,34,5,6,3)

#长度:len
print(len(T1))

#最大值:max
print(max(T1))
5
34
# 对某一元素计数 count
T1 =12311print(T1.count(1))

# 某一元素所在位置 index
print(T1.index(1))
6
0
# tuple特殊用法
a = 100
b = "zyx"

#要求对ab值进行互换
#此用法python独门
a, b = b, a

集合 set

  • 与数学中概念一致
  • 内容无序且内容不重复

集合的定义

# 集合的定义
# 1.通过set关键字
S1 = set()
print(S1)
L1 = [2,3,5,1,3,4,2]
S2 = set(L1)
print(S2) 

# 2.使用大括号
S3 = {1,2,3,4,1,2,4,21}
print(S3)
set()
{2,3,5,1,4}
{1,2,3,4,21}

集合 in

# in操作
if 2 in S3:
	print(2)
for i in S3:
	print(i)

集合遍历

# 集合的另一种遍历
S1 = {(1,2,3),(4,5,6),("a","b","c")}
for i,j,k in S1:
	print(i,j,k)
a b c
4 5 6
1 2 3 

集合生成式

# 集合的生成式
S1 = {1,2,3,4,5,6,7,8,9,10}

# 利用S1生成一个S2
S2 = {i for i in S1}
print(S2)
S3 = {i for i in s1 if i%2 == 0}
print(S3)

# 双重for循环
# 把S1中的每一个元素的平方生成一个新的集合
# 1. 用一个for
S4 = {i**2 for i in S1}
# 2.用两个for
S5 = {m*n for m in S1 for n in S1}
{1,2,3,4,5,6,7,8,9,10}
{2,4,6,8,10}

集合内置函数

# 集合的内置函数
# len长度
print(len(S1))
# max/min 最值
# add 向集合中添加元素
S2 = S1.add(4)
# clear 清空
S1.clear()
# 删除操作
# remove和discard区别
S1 = {1,2,3}
S1.remove(1)
print(S1)
# remove删除的值若不在集合中,则报错 
# discard则不会报错 
{2,3}
# pop 弹出集合的一个内容
# 删除的内容是随机的
S1.pop()

集合的数学操作

#交集
S1.intersection(S2)

#差集
S1.difference(S2)
#差集的另一种表示
S1-S2

#并集
S1.union(S2)
#并集的另一种表示
S1+S2

frozenset 冰冻集合

  • 不允许修改的集合
S1 = {1,2,3}
print(S1)
S2 = frozenset(S1)
print(S2)
{1,2,3}
frozenset({1,2,3})

dict字典

  • 字典是一种没有顺序的组合数据 数据以键值对形式出现

字典的创建

# 创建空字典1
d = {}
print(d)

# 创建空字典2
d = dict()
print(d)

# 创建有值的字典 
# 每一组数据用冒号隔开 每一对键值对用逗号隔开
d = {"one":1, "two":2, "three":3}
print(d)

# 用 dict创建有内容的字典1
d = dict({"one":1, "two":2, "three":3})
print(d)

# 用 dict创建有内容的字典2
# 利用关键字参数
d = dict(one=1, two=2, three=3)
print(d)

#
d = dict([("one",1), ("two",2), ("three",3)])
print(d)
{}
{}
{"one":1, "two":2, "three":3}
{"one":1, "two":2, "three":3}
{"one":1, "two":2, "three":3}
{"one":1, "two":2, "three":3}

字典的特征

  • 字典是序列类型 但是是无序序列 所以没有分片和索引
  • 字典中的数据每个都有键值对组成 即kv对
    • key 必须是可哈希的值 比如 int string float tuple 但是 list set dict不行
    • value 任何值

字典的常见操作

访问/修改/删除/数据

d = {"one":1,"two":2,"three":3}
# 注意访问格式
# 中括号内是键值
print(d["one"])

# 修改数据
d["one"] = "eins"
print(d)

# 删除某个操作
# 使用del
del d["one"]
print(d)
1
{"one":"eins", "two":2, "three":3}
{"two":2, "three":3}

成员检测

# 成员检测in 检测的是key的内容
d = {"one":1,"two":2,"three":3}

if 2 in d:
	print("value")
if "two" in d:
	print("key")
if ("two",2) in d:
	print("kv") 
key

dict遍历

# 遍历在python2和3不一样
# 按照key来使用for循环
d = {"one":1,"two":2,"three":3}
# 使用for循环 直接按key值访问
for k in d:
	print(k, d[k])

上述代码可以改写如下
for k in d.keys():
	print(k, d[k])

# 只访问字典的值
for v in d.values():
	print(v)

# 注意以下特殊用法
for k,v in d.items():
	print(k,'--',v)
one 1
two 2 
three 3
one 1
two 2 
three 3
1
2
3
one -- 1
two -- 2
three -- 3

字典生成式

d = {"one":1,"two":2,"three":3}

# 常规字典生成式
dd = {k:v for k,v in d.items()}
print(dd)

# 加限制条件的字典生成式
dd = {k:v for k,v in d.items() if v%2 == 0}
print(dd)
{"one":"eins", "two":2, "three":3}
{"two":2}

字典相关函数

str

# 通用函数 len max min dict
# *str(字典):返回字典的字符串格式
d = {"one":1,"two":2,"three":3}
print(str(d))
{"one":"eins", "two":2, "three":3}

items

#clear 清空字典
#items 返回字典的键值对组成的元组格式
d = {"one":1,"two":2,"three":3}
i = d.items()
print(type(i))
print(i)
<class 'dict_items'>
dict_items([('one', 1), ('two', 2), ('three', 3)])

keys

#
k = d.keys()
print(type(k))
print(k)
<class 'dict_keys'>
dict_keys(['one', 'two', 'three'])

values

#
v = d.values()
print(type(v))
print(v)
<class 'dict_values'>
dict_values([1, 2, 3])

get

# get 根据指定键返回相应值 好处是可以设置默认值
d = {"one":1,"two":2,"three":3}
# 用get函数 如果未找到对应键值 则返回默认值 
# print(d["on333"])则报错
print(d.get("on333"))

# get 默认值是None 可以设置
print(d.get("one",100))
print(d.get("one333",100))
None  
1
100

fromkeys

  • 使用指定的序列作为键 使用一个值作为字典所有键的值
l = ["eins","zwei","dree"]
# 注意fromkeys两个参数的类型
d = dict.fromkeys(l,"haha")
print(d)
{'eins': 'haha', 'zwei': 'haha', 'dree': 'haha'}

递归函数

  • 递归:函数可以间接或直接调用自己
  • 递归分两个过程
    • 往下调用
    • 往上回溯
  • 递归一定要有结束条件
  • pro vs con
    • pro 简洁 理解容易
    • con 对递归深度有限制 消耗资源大
      python对递归深度有限制 超过限制报错
def fun_a(n)
	# 递归一定要有结束条件 
	if n == 1:
		return 1
	return n * fun_a(n-1)	
rst = fun_a(5)
print("f(5) = ",rst)
f(5) = 120
# fibo数列
def fib(n):
	if n == 1 or n == 2:
		return 1
	return fib(n-1)+fib(n-2)
rst = fib(10)
print("rst = ",rst)
rst = 55
#汉诺塔
a = "A"
b = "B"
c = "C"
def hano(a,b,c,n):
	if n == 1:
		print("{}"-->"{}".format(a,c))
		return None
	if n == 2:
		print("{}"-->"{}".format(a,c))
		print("{}"-->"{}".format(a,b))
		print("{}"-->"{}".format(b,c))
		return None
	
	hano(a,c,b,n-1)
	print("{}"-->"{}".format(a,c))
	hano(b,a,c,n-1)

anaconda基本使用

  • anaconda主要是一个虚拟环境管理器
  • 还是一个安装包管理器
  • conda list: 显示anaconda安装的包
  • conda env list:显示anaconda的虚拟环境列表
  • conda create -n xxx python=3.6 创建python版本为3.6的虚拟环境 名称为xxx

OOP

思想

  • 以模块化思想解决工程问题
    接触到任意一个任务 首先想到的是任务这个世界的构成 是由模型构成的
  • 面向过程 VS 面向对象
  • 由面向过程转向面向对象
  • 面向对象编程
    • 基础
    • 公有私有
    • 继承
    • 组合 Minxi
  • 魔法函数
    • 魔法函数概述
    • 构造类魔法函数
    • 运算类魔法函数

常用名词

  • OO 面向对象 object oriented
  • OOA 面向对象分析
  • OOD 面向对象设计
  • OOP 面向对象编程
  • OOI 面向对象实现
  • OOA-> OOD -> OOI

类 VS 对象

  • 类:抽象 描述的是一个集合 侧重于共性
  • 对象:具象 描述的是单个个体

类的内容

  • 表明事物的特征 叫做属性(变量)
  • 表明事物功能或动作 称为成员方法(函数)

is-a has-a

在这里插入图片描述

is-a is-like-a

在这里插入图片描述

类的基本实现

类的命名

  • class关键字
  • 类命名
    • 遵循大驼峰
    • 尽量避开跟系统命名相似的命名

类的声明

  • 必须用class关键字
  • 类由属性和方法构成 其他不允许出现
  • 成员属性定义可以直接
  • 用变量赋值 如果没有值 可以使用None

实例化类

  • 变量 = 类名() #实例化了一个对象
#定义学生类 和几个学生
class Student():
	#此处定义一个空类
	#pass是关键字,占位用的,无意义,否则会报错
	pass

#定义一个对象
xiaobai = Student()

#
class PythonStudent():
	name = None
	age = 18
	course = "Python"
	'''
	注意定义类中的函数的缩进层级
	系统默认有一个self参数
	'''
	def doHomework(self):
		print("doing...")
		# 推荐在函数末尾使用return语句
		return None
# 实例化一个叫xiaobai的学生 是一个具体的人
xiaobai = PythonStudent()
print(xiaobai.name)
print(xiaobai.age)
print(xiaobai.course)
xiaobai.doHomework()
None
18
Python
doing...

访问对象成员

  • 使用点操作符
    • obj.成员属性名称
    • obj.成员方法名称
  • 可以通过默认内置变量检查类和对象的所有成员
    • 对象所有成员检查
      obj.__dict__ (dict前后两个下划线)
    • 类的所有的成员
      class_name.__dict__

类和对象的成员分析

  • 类和对象都可以存储成员 成员可以归类所有 也可以归对象所有
  • 类存储成员时使用的是与类关联的一个对象
  • 独享存储成员是存储在当前对象中
  • 对象访问一个成员时 如果对象中没有该成员 尝试访问类中的同名成员
    • 如果对象中有此成员 一定使用对象中的成员
  • 创建对象的时候 类中的成员不会放入对象当中 而是得到一个空对象 没有成员
  • 通过对象对类中成员重新赋值或者通过对象添加成员时,对应成员会保存在对象中 而不会修改类成员
class A():
	name = "anna"
	age = 18
A.__dict__

a = A()
a.__dict__
mappingproxy({'__module__': '__main__', 'name': 
'anna', 'age': 18, '__dict__': <attribute '__dict__' of 'A' 
objects>, '__weakref__': <attribute '__weakref__' of 'A' 
objects>, '__doc__': None})

{}
  • 对象访问一个成员时:
    如果对象中没有该成员 尝试访问类中的同名成员
    如果对象中有此成员 一定使用对象中的成员
class A():
	name = "dana"
	age =18
	# 注意say的写法 参数有一个self
	def say(self):
		self.name = "aa"
		self.age = 20
# 此时 A可理解为类实例
print(A.name)
print(A.age)

print("*"*20)
# id可以鉴别两个变量是否同一个变量
print(id(A.name))
print(id(A.age))

print("*"*20)
a = A()

print(a.name)
print(a.age)
print(id(a.name))
print(id(a.age))
dana
18
********************
4373594664
4367448000
********************
dana
18
4373594664
4367448000
# 可以看出A和a的id一致
# 此案例说明 类实例的属性和其对象实例的属性在不对对象实例属性赋值的前提下 指向同一个变量 

类的属性

# 类的定义
class Student():
	name = "heu"
	age = 19
	
	def sayHi(self):
		print("love,peace")
		return None 

# 实例化
zyx = Student()

# 实例调用函数
yaoyao = Student()

#yaoyao调用sayHi没有输入参数
#因为默认实例作为第一个参数传入
yaoyao.sayHi()
love,peace

self

  • self在对象的方法中表示当前对象本身 如果通过对象调用一个方法 那么该对象会自动传入到当前方法的第一个参数中
  • self并不是关键字 只是一个用于接受对象的普通参数 理论上可以用任何一个普通变量名代替
class Student():
	name = "dana"
	age = 18
# 注意say的写法 参数有一个self
	def say(self):
		self.name = "aa"
		self.age = 20
		print("name:{0}".format(self.name))
		print("age:{0}".format(self.age))
	
yueyue = Student()
yueyue.say
name: aa
age: 20
  • 方法中有self形参的方法称为非绑定类的方法 可以通过对象访问 没有self的是绑定类的方法 只能通过类访问
class Teacher():
	name = "dana"
	age = 19
	def say(self):
		self.name = "yaona"
		self.age = 17
		print("name:{0}".format(self.name))
		print("age:{0}".format(self.age))
	
	def sayAgain():
		print("nice to meet you")

t = Teacher()
t.say()
# 直接使用t.sayAgain() 会报错 
# 调用绑定类函数使用类名
Teacher.sayAgain()
name: yaona
age:17
nice to meet you
class A():
	name = "liuying"
	age = 18

	def __init__():
		self.name = "aaaa"
		self.age = 200
	
	def say(self):
		print(self.name)
		print(self.age)

class B():
	name = "bbbb"
	age = 90

a = A()
# 此时 系统默认把a作为第一个参数传入函数
a.say()
# A.say() 报错 缺少self参数
# 此时 self被a替换
A.say(a)
# 同理把A作为参数传入
A.say(A)
# 把B作为参数传入
# 此时传入的是类实例B 因为B具有name和age属性 所以不会报错
A.say(B)

# 以上代码 利用鸭子模型
aaaa
200
aaaa
200
liuying
18
bbbb
90

类变量作用域

  • 类变量 属于类自己的变量
  • 实例变量 属于实例的变量
  • 访问实例的属性没有定义 则自动访问类的属性
  • 如果类也没有定义属性 则报错
class Student():
	#name age 是类的变量
	#注意类的变量的定义位置和方法
	#不需要前缀
	name = "zyx"
	age = 18
	def sayHi(self,n,a):
		self.name = n
		self.age = a 
		print("name:{} age:{}".format(self.name,self.age))
		return None
# 实例变量可以借用类的变量
xiaohua = Student()
# 如果访问实例的属性没有定义 则自动访问类的属性
# 如果类也没有定义 则报错
xiaohua.sayHi()
xiaohua.sayHi("xiaohua",17)
name:zyx age:18
name:xiaohua age:17

访问类的属性

  • 在类里面如果强制访问类的属性 则需要使用__class__ (注意前后有两个下划线)
  • 类方法:
    • 定义类的方法的时候 没有self参数
    • 类的方法中只允许使用类的内容
    • 两种访问类的内容
      • className
      • class
class Student():
	#name age 是类的变量
	#注意类的变量的定义位置和方法
	#不需要前缀
	name = "zyx"
	age = 18
	
	# sayHai是类的方法
	# 如何访问类的变量
	# 但是调用需要用到特殊的方法
	def sos():
	# 类方法中不允许访问实例的属性
	# 如果访问类的内容 注意两种方法
	print("name:{} age:{}".format(Student.name,__class__.age))
		return None
#调用类方法的例子
Student.sos()
name:zyx age:18

类相关函数

issubclass

  • 检测两个类的父子关系
# 利用刚才定义的Bird BirdMan Person Teacher 检测父子关系
print(issubclass(BirdMan,Bird))
print(issubclass(BirdMan,Person))
print(issubclass(BirdMan,Teacher))
True
True
False

isinstance

  • 检测是否是对象实例
class A():
	pass

a = A()

print(isinstance(a, A))
print(isinstance(A, A))
True
False

hasattr

  • 检测一个对象是否含有成员xxx
class A():
	name = "NoName"

a = A()
print(hasattr(a, "name"))
print(hasattr(a, "age"))
True
False

setattr

delattr

dir

  • 获取对象的成员列表
class A():
	pass
dir(A)
a = A()
dir(a)

类的成员描述符(属性)

  • 类的成员描述符是为了在类中对类的成员属性进行相关操作而创建的一种方式
# 属性案例
# 创建Student类 描述学生类
# 学生具有Student.name属性
# 每个实例的名字大小写不同 看着不一致
# 用增加函数的方法统一 但是很麻烦!!!!!
class Student():
	def __init__(self, name, age):
		self.name = name
		self.age = age
	
	def intro(self):
		print("name:{0}".format(self.name))
		
s1 = Student("LIU Ying",19)
s2 = Student("michi stangle",24)

s1.intro()
s2.intro()	
LIU ying
michi stangle
class Student():
	def __init__(self, name, age):
		self.name = name
		self.age = age
		#统一名字
		self.setName(name)
	def intro(self):
		print("name:{0}".format(self.name))
	# 建函数统一名字格式
	def setName(self):
		self.name = name.upper()
		
s1 = Student("LIU Ying",19)
s2 = Student("michi stangle",24)

s1.intro()
s2.intro()	
LIU YING
MICHI STANGLE

属性的操作

get

  • 获取属性的操作

set

  • 修改或者添加属性的操作

delete

  • 删除属性的操作
# 类属性 property
# 应用场景
# 对变量除了普通的三种操作 还想增加一些附加的操作 那么可以通过property完成 
class A():
	def __init__(self):
		self.name = "haha"
		self.age = 18
	# fget是对类变量进行读取操作的时候应该执行的函数功能
	def fget(self):
		print("i was read")
		return self.name
	# fset是对类变量进行读取操作的时候应该执行的函数功能
	def fset(self):
		print("i was written")
		self.name = "heu"+name
	# fdel模拟的是删除变量的时候进行的操作
	def fdel(self):
		pass
	# property的四个参数顺序是固定的
	# 第一个参数代表读取的时候需要调用的函数
	# 第二个参数代表写入的时候需要调用的函数
	# 第三个时候删除
	name2 = property(fget, fset, fdel,"this is an property example")

a = A()
print(a.name)
print(a.name2)
haha
i was read
haha

使用类的成员描述符

  • 变量的三种方法
class A():
	def __init__(self):
		self.name = "haha"
		self.age = 18
	
a = A()
# 属性的三种用法
#1.赋值
a.name = "dana"
#2.读取
print(a.name)
#3.删除
del a.name

使用类实现描述器

  • 适合多个类中的多个属性共用一个描述符

使用属性修饰符

  • 适用当前类中使用 控制一个类中的一个属性

使用property函数

  • 适用当前类中使用 可以控制一个类中多个属性
  • property(fget, fset, fdel, doc)
# 定义Person类 有name age属性
# 任意输入name 大写保存 age 整数保存
class Person():
	def fget(self):
		return self._name *.2

	def fset(self, name)
		self._name = name.upper()

	del fdel(self):
		self._name = "NoName"
	
	name = property(fget, fset, fdel, "对name进行操作")
p1 = Person()
p1.name = "Tuling"
print(p1.name)
TULINGTULIN

类的内置属性

__dict__

  • 以字典的方式显示类的成员组成

__doc__

  • 获取类的文档信息

__name__

  • 获取类的名称
  • 如果在模块中使用 则获取模块的名称

__bases__

  • 以元组的方式显示某个类的所有父类
class Person():
'''
this is a person with good taste!
'''
	def __init__(self, name, age):
		name = NoName
		age = 0
print(Person.__dict__)
print(Person.__doc__)
print(person.__name__)
print(Person.__bases__)
{'__module__': '__main__', '__doc__': '\n    this is a person with 
good taste!\n    ', '__init__': <function Person.__init__ at 
0x1006df8c0>, '__dict__': <attribute '__dict__' of 'Person' 
objects>, '__weakref__': <attribute '__weakref__' of 'Person' 
objects>}

this is a person with good taste!

Person
(<class 'object'>,)

类的常用魔术方法

  • 魔术方法就是不需要人为调用的方法 基本是在特定时刻自动触发
  • 魔术方法统一特征 方法名前后各两个下划线

__init__

  • 构造函数
class A()
	def __init__(self, name = 0):
		print("我被调用了")
	
a = A()
我被调用了

__new__

  • 对象实例化方法
  • 此函数较特殊 一般不需要使用

__call__

  • 对象当函数使用的时候触发
class A()
	def __init__(self, name = 0):
		print("我被调用了")
	def __call__(self):
		print("我被调用again")
a = A()
a()
我被调用了
我被调用again

__str__

  • 当对象被当作字符串使用的时候调用
class A()
	def __init__(self, name = 0):
		print("我被调用了")
	def __str__(self):
		return "__str__的例子"
a = A()
print(a)
我被调用了
__str__的例子

__repr__

  • 返回字符串 跟__str__具体区别

描述符相关

__set__

__get__

__delete__

属性操作相关

__getattr__

  • 调用属性异常
class A():
	name = "NoName"
	age = 0
	def __getattr__(self, name):
		print("no found")
		print(name)
a = A()
print(a.name)
print(a.addr)
NoName
no found
addr
None

__setattr__

  • 对成员属性进行设置的时候触发
  • 参数
    • self用来获取当前对象
    • 被设置的属性名称 以字符串形式出现
    • 需要对属性名称设置的值
  • 作用:进行属性设置的时候进行验证或修改
  • 注意:在该方法不能对属性直接进行赋值操作 否则死循环
class Person():
	def __init__(self):
		pass
	
	def __setattr__(self, name, value):
		print("set attr:{0}".format(name))
		#下面语句会导致问题 死循环
		# self.name = value
		# 此种情况 避免死循环 规定统一调用父类魔法函数
		super().__setattr__(name, value)
p = Person()
print(p.__dict__)
p.age = 18

运算分类相关魔术方法

__gt__

  • 进行大于判断的时候触发的函数
  • 参数
    • self
    • 第二个参数是第二个对象
    • 返回值可以是任意值 推荐返回bool
class Student():
	def __init__(self, name):
		self._name = name
	
	def __gt__(self,obj):
		print("{0} bigger than {1}?".format(self,obj))
		return self._name> obj._name

stu1 = Student("one")
stu2 = Student("two")
print(stu1 > stu2)
one bigger than two?
False

抽象类

  • 抽象方法:没有具体实现内容的方法称为抽象方法
  • 抽象方法的主要意义是规范子类的行为和接口
# 抽象
class Animal():
	def sayHello(self):
		pass
	
class Dog(Animal):
	def sayHello(self):
		print("sniff")

class Person(Animal):
	def sayHello(self):
		print("kiss")

d =Dog()
d.sayHello()
p = Person()
p.sayHello()
  • 抽象类的使用需要借助abc模块
    • 抽象类可以包含抽象方法 也可以包含具体方法
    • 抽象类中可以有方法也可以有属性
    • 抽象类不允许直接实例化
    • 必须继承才可以使用 且继承的子类必须实现所有继承来的抽象方法
    • 假定子类没有实现所有继承的抽象方法 则子类也不能实例化
    • 抽象类的主要作用是设定类的标准 以便于开发的时候具有统一的规范
import abc
# 声明一个类并且指定当前的类的元类为抽象类提供的元类
class Human(metaclass = abc.ABCMeta)
	#定义一个抽象的方法
	@abc.abstractmethod
	def smoking(self):
		pass
	#定义类抽象方法
	@abc.abstractclassmethod
	def drink():
		pass
	#定义静态抽象方法
	@abc.abstractstaticmethod
	def play():
		pass
	def sleep(self):
		print("sleeping")

自定义类

  • 函数名可以当变量使用
def sayHello(name):
	print("{0},hi,go?".format(name))

sayHello("yueyue")
liumang = sayHello
liumang("yueyue")
yueyue,hi,go?
yueyue,hi,go?
  • 可以定义类和函数 然后自己通过类直接赋值
class A():
	pass

def say(self):
	print("saying")

say(9)

A.say = say

a = A()
a.say()
saying
saying
  • 可以借助于MethodType实现
# 将函数绑定实例
from types import MethodType

class A():
	pass

def say(self):
	print("saying")

a = A()
a.say = MethodType(say, A)
a.say() 
saying
  • 借助于Type实现
# 用type创建一个类
# 先定义类应该具有的成员函数
def say(self):
	print("saying")
def talk(self):
	print("talking")
# 用type来创建一个类
A = type("AName",(object, ),{"class_say":say,"class_talk":talk})
# 然后可以像正常访问一样使用类
a = A()
a.class_say()
a.class_talk()
saying
talking
  • 利用元类实现 MetaClass
    • 元类是类
    • 备用来创造别的类
# 元类的写法固定 必须继承自type
# 元类一般命名以MetaClass结尾
class HeuMetaClass(type):
	def __new__(cls, name, bases, attrs):
		print("hh")
		attrs['id'] = '000000'
		attrs['addr'] = 'nantongdajie101'
		return type.__new__(cls, name, bases, attrs)

class Teacher(object, metaclass = HeuMetaClass):
	pass
t = Teacher()
print(t.id)
hh
000000

类和对象的三种方法

实例方法

  • 定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
  • 只能由实例对象调用

类方法

  • 定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
  • 实例对象和类对象都可以调用

静态方法

  • 定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
  • 实例对象和类对象都可以调用
class Person():
	# 实例方法
	def eat(self):
		print(self)
		print("eating...")
	# 类方法
	# 类方法的第一个参数 一般命名为cls 区别于self
	@classmethod
	def play(cls):
		print(cls)
		print("playing...")
	# 静态方法
	# 不需要用第一个参数表示自身或者类
	@staticmethod
	def say():
		print("saying...")

yueyue = Person()
# 实例方法
yueyue.eat()
# 类方法
Person.play()
yueyue.play()
# 静态方法
Person.say()
yueyue.say()
<__main__.Person object at 0x1017cd890>
eating...
<class '__main__.Person'>
playing...
<class '__main__.Person'>
playing...
saying...
saying...

面向对象的三大特征

  • 封装
  • 继承
  • 多态

封装

  • 封装就是对对象的成员进行访问限制

封装的三个级别

  • 公开 public
  • 受保护的 protected
  • 私有的 private
  • public private protected 不是关键字
  • 判别对象的位置
    • 对象内部
    • 对象外部
    • 子类中

私有

  • 私有成员是最高级别的封装 只能在当前类或对象中访问
  • 在成员前面添加两个下划线即可
  • Python的私有不是真私有 是一种成为name mangling的改名策略 可以使用“对象._(一个下划线)类名__(两个下划线)属性名”访问

受保护的封装 protected

  • 受保护的封装是将对象成员进行一定级别的封装 然后在类中或者子类中都可以进行访问 但是在外部不可以
  • 封装方法:在成员名称前添加一个下划线即可

公开 public

  • 公共的封装实际对成员没有任何操作 任何地方都可以访问
class Person()
	# name公有变量
	name = "liuying"
	# __age私有变量
	__age = 18

p = Person()
# name公有变量
print(p.name)
# __age私有变量
# print(p.__age) 会报错
print(p._Person__age)
liuying
18

继承

  • 继承即 一个类可以获得另外一个类中的成员属性和成员方法
  • 子类是父类的一个子集

继承的作用

  • 减少代码
  • 增加代码的复用功能
  • 同时可以设置类与类之间的关系

继承与被继承的概念:

  • 被继承的类叫做父类/基类/超类
  • 继承的类叫做子类/派生类

继承的实现

  • 父类(被继承的类) 基类(Base class) 超类(Super class)
  • 子类(有继承行为的类)

继承的特征

  • 所有的类都继承自object类 即所有的类都是object类的子类
# 所有类必须有父类
# 默认是任何类的共同父类是object
class Person1():
	pass
class Person2(object):
	pass
  • 子类一旦继承父类 则可以使用父类中除私有成员外的所有内容
  • 子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
  • 子类中可以定义独有的成员属性和方法
class Person():
	name = "NoName"
	age = 0
	def sleep(self):
		print("sleeping...")

# 父类写在类定义的时候的括号里
class Teacher(Person):
	def make_test(self):
	pass

t = Teacher()
print(t.name)
print(Teacher.name)
NoName
NoName
class Person():
	name = "NoName"
	age = 18
	# 考试成绩是秘密 只要自己知道
	__score = 0
	# 小名 是保护的 子类可以用 但不可公用
	_petname = "sec"
	def sleep(self):
		print("sleeping...")

# 父类写在类定义的时候的括号里
class Teacher(Person):
	def make_test(self):
		print("attention")

t = Teacher()
print(t.name)
print(t._petname)
# 访问私有变量 报错
# print(t.__score)
t.sleep()
t.make_test()
NoName
sec
sleeping...
attention
  • 子类中定义的成员和父类成员如果相同 则优先使用子类成员
class Person():
	name = "NoName"
	def sleep(self):
		print("sleeping...")

class Teacher(Person):
	name = "dana"
	def make_test(self):
		print("attention")

t = Teacher()
print(t.name)
dana
  • 子类如果想扩充父类方法 可以定义新方法同时访问父类成员来进行代码重用 可以使用“父类名.父类成员” 的格式来调用父类成员 也可以使用“super().父类成员”的格式来调用
# 子类扩充父类功能的案例
# 人有工作的函数 老师也有工作的函数 但老师的工作需要讲课
class Person():
	name = "NoName"
	age = 18
	def sleep(self):
		print("sleeping...")
	def work(self):
		print("make money...")

class Teacher(Person):
	name = "dana"
	def make_test(self):
		print("attention")
	def work(self):
		# 扩充父类的功能只需要调用父类相应的函数
		Person.work(self)
		# 扩充父类的另一种方法
		# super代表得到父类
		super.work()
		self.make_test()

t = Teacher()
t.work()
make money...
attention
make money...
attention

继承变量函数的查找顺序问题

  • 优先查找自己的变量
  • 没有则查找父类
  • 构造函数如果本类中没有定义 则自动查找调用父类构造函数
  • 如果本类有定义 则不再继续查找
# 继承中的构造函数 -1
class Animal():
		pass

class Reptile(Animal):
	pass

class Dog(Reptile):
	# __init__即构造函数
	# 每次实例化的时候 第一个被自动调用
	# 因为主要工作是进行初始化 所以得名
	def __init__():
		print("init a dog")
# 实例化的时候 括号内的参数需要跟构造函数参数匹配
# 实例化的时候 自动调用了Dog的构造函数
kaka = Dog()
init a dog
# 继承中的构造函数 -2
class Animal():
	def __init__():
		print("Animal")

class Reptile(Animal):
	def __init__():
		print("Reptile")

class Dog(Reptile):
	def __init__():
		print("init a dog")
# 实例化的时候 自动调用了Dog的构造函数
kaka = Dog()
# 猫没有构造函数
class Cat(Reptile):
	pass
# 此时自动调用构造函数 因为Cat没有构造函数 所以查找父类构造函数
# 在Reptile中查找到了构造函数 则停止向上查找
c = Cat()
init a dog
Reptile
# 继承中的构造函数 -3
class Animal():
	def __init__():
		print("Animal")

class Reptile(Animal):
	def __init__(self, name):
		print("Reptile {0}".format(name))

class Dog(Reptile):
	def __init__():
		print("init a dog")
# 实例化Dog时 查找到Dog的构造函数 参数匹配 不报错
kaka = Dog()

class Cat(Reptile):
	pass
# 此时 因为Cat没有构造函数 则向上查找
# 在Reptile中的构造函数需要两个参数 实例化的时候给了一个
# c = Cat() 报错
init a dog
# 继承中的构造函数 -4
class Animal():
	def __init__():
		print("Animal")

class Reptile(Animal):
	pass
	
class Dog(Reptile):
	pass

d = Dog()

class Cat(Reptile):
	pass

c = Cat()
Animal
Animal

单继承和多继承

单继承

  • 每个类只能继承一个类
  • 优点:传承有序 逻辑清晰 语法简单 隐患少
  • 缺点:功能不能无限扩展 只能在当前唯一的继承链中扩展

多继承

  • 每个类允许继承多个类
  • 优点:类的功能扩展方便
  • 缺点:继承关系混乱
class Fish():
	def __init__(self, name):
		self.name = name
	
	def swim(self):
		print("swim")


class Bird():
	def __init__(self, name):
		self.name = name
	
	def fly(self):
		print("fly")


class Person():
	def __init__(self, name):
		self.name = name
	
	def work(self):
		print("work")


class SuperMan(Person, Bird, Fish):
	def __init__(self, name):
		self.name = name

s = SuperMan("yueyue")
s.fly()
s.swim()
s.work() 	
fly
swim
work

菱形继承/钻石继承问题

  • 多个子类继承自同一个父类 这些子类又被同一个类继承 于是继承关系图形成一个菱形图
  • 关于多继承的MRO
    • MRO就是多继承中 用于保存继承顺序的一个列表
    • python本身采用C3算法多继承的菱形继承进行计算的结果
    • MRO列表计算原则
      • 子类永远在父类前面
      • 如果多个父类 则根据继承语法中括号内类的书写顺序存放
      • 如果多个类继承同一个父类 孙子类中只会选取继承语法括号中第一个父类的父类

* 构造函数

  • 类在实例化的时候 执行一些基础性的初始化工作
  • 使用特殊的名称和写法
  • 在实例化的时候自动执行 是在实例化的时候第一个被执行的函数
  • 要求第一个参数必须要 一般推荐self
  • 一般不手动调用 实例化的时候自动调用 参数需写入类名称后面的括号中
  • 如果定义了构造函数 则实例化时使用构造函数 不查找父类构造函数
  • 如果没定义 则自动查找父类构造函数
  • 如果子类没定义 父类的构造函数带参数 则构造对象时的参数应该按照父类参数构造
class Person():
	# 对Person类进行实例化的时候
	# 姓名要确定
	# 年龄要确定
	# 地址要确定
	# 构造函数名称固定 写法相对固定
	def __init__(self):
		self.name = "NoName"
		self.age = "18"
		self.address = "NoWhere"
		print("Init Func")

# 实例化一个人
p = Person()
Init Func
  • 如果子类没有写构造函数 则自动向上按照MRO顺序查找 直到找到为止
# 构造核函数的调用顺序-1
class A():
	def __init__(self):
		print("A")
	
class B(A):
	def __init__(self):
		print("B")
	
class C(B):
	pass
# 此时 首先查找C的构造函数
# 如果没有 则向上按照MRO的顺序查找父类的构造函数 直到找到为止
c = C()
B
# 构造核函数的调用顺序-2
class A():
	def __init__(self):
		print("A")
	
class B(A):
	def __init__(self, name):
		print("B")
		print(name)
	
class C(B):
	pass
# 此时 首先查找C的构造函数
# 如果没有 则向上按照MRO的顺序查找父类的构造函数 直到找到为止
# 此时 会出现参数结构不对应错误 少一个name参数
c = C()
# 构造核函数的调用顺序-3
class A():
	def __init__(self):
		print("A")
	
class B(A):
	def __init__(self, name):
		print("B")
		print(name)
	
class C(B):
	# C中想扩展B的构造函数
	# 即调用B的构造函数后再添加一些功能
	# 有两种方法实现
	'''
	# 第一种通过父类名调用
	def __init__(self, name):
		# 首先 调用父类的构造函数
		B.__init__(self, name)
		# 其次 增加自己的功能
		print("add c func")
	'''
	# 第二种 使用super调用
	def __init__(self, name):
		# 首先 调用父类的构造函数
		super(C, self).__init__(name)
		# 其次 增加自己的功能
		print("add c func")
# 此时 首先查找C的构造函数
# 如果没有 则向上按照MRO的顺序查找父类的构造函数 直到找到为止
# 此时 会出现参数结构不对应错误 少一个name参数
c = C("i am c")
B
i am C
add c func

* super

  • super不是关键字 而是一个类
  • super的作用是获取MRO(MethodResolutionOrder)列表中的第一个类
  • super于父类直接没任何实质性关系 但通过super可以调用到父类
class A():
	pass

class B(A):
	pass
# mro 返回类的家谱
print(A.__mro__)
print(B.__mro__)
(<class '__main__.A'>, <class 'object'>)
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

多态

  • 多态就是同一个对象在不同情况下有不同的状态出现
  • 多态不适合语法 是一种设计思想
  • 多态性: 一种调用方式 不同的执行效果
  • 多态:同一事物多种形态 水 液态 固态 气态

Mixin设计模式

  • 主要采用多继承方式对类的功能进行扩展
  • 我们使用多继承语法来实现Mixin
  • 使用Mixin实现多继承的时候非常小心
    • 首先它必须表示某一单一功能 而不是某个物品
    • 职责必须单一 如果由多个功能 则写多Mixin
    • Mixin不能依赖于子类的实现
    • 子类即使没有继承这个mixin类 也能照样工作 只是缺少了某一个功能
  • 优点:
    • 使用Mixin可以在不对类进行任何修改的情况下 扩充功能
    • 可以方便的组织和维护不同功能组件的划分
    • 可以根据需要任意调整功能类的组合
    • 可以避免创建很多新的类 导致类的继承混乱
class Person():
	name = "liuying"
	age = 18
	def eat(self):
		print("eat...")
		
	def drink(self):
		print("drink...")
		
	def sleep(self):
		print("sleep...")

class Teacher(Person):
	def work(self):
		print("work...")
	
class Student(Person):
	def study(self):
		print("study...")

class Tutor(Teacher, Student):
	pass

t = Tutor()

print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)

print("*"*20)
class TeacherMixin():
	def work(self):
		print("work...")

class studentMixin():
		def study(self):
			print("study...")
	class TutorM(Person, TeacherMixin, StudentMixin):
		pass
	
	tt = TutorM()
	print(TutorM.__mro__)
	print(tt.__dict__)
	print(TutorM.__dict__)	
(<class '__main__.Tutor'>, <class '__main__.Teacher'>, <class 
'__main__.Student'>, <class '__main__.Person'>, <class 
'object'>)
{}
{'__module__': '__main__', '__doc__': None}
********************
(<class '__main__.TutorM'>, <class '__main__.Person'>, <class 			
'__main__.TeacherMixin'>, <class '__main__.StudentMixin'>, 
<class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}

模块

  • 一个模块就是一个包含python代码的文件 后缀名为.py即可 模块就是python文件

为什么用模块

  • 程序太大 编写维护不方便 需要拆分
  • 模块可以增加代码重复利用的方式
  • 当作命名空间使用 避免命名冲突

如何定义模块

  • 模块就是一个普通文件 所以任何代码可以直接书写
  • 不过根据模块的规范 最好在模块中编写以下内容:
    • 1.函数(单一功能)
    • 2.类(相似功能的组合 或者类似业务模块)
    • 3.测试代码

如何使用模块

1.模块直接导入

  • 语法
    • import module_name
    • module_name.function_name
    • module_name.class_name
# p01.py
# 包含一个学生类 
# 一个sayhello函数 
# 一个打印语句
class Student():
	def __init__(self, name = "NoName", age = 18):
	self.name = name
	self.age = age

	def say(self):
		print("my name is {0}".format(self.name))

def sayHello():
	print("Hi, welcome")

print("i am module p01")
import p01

stu = p01.Student("xiaojing",19)

stu.say

p01.sayHello()
# 导入模块的时候 模块代码自动执行一遍
# 所以打印语句执行
i am module p01 
my name is xiaojing
Hi, welcome
  • 若不想执行打印语句 添加main
  • 模块作为引用使用后 自带的程序不会自动执行
class Student():
	def __init__(self, name = "NoName", age = 18):
	self.name = name
	self.age = age

	def say(self):
		print("my name is {0}".format(self.name))

def sayHello():
	print("Hi, welcome")
# 此判断语句建议一直作为程序的入口
if __name__ == '__main__':
	print("i am module p01")

2. 模块名称以数字开头

  • 需借助于importlib包实现导入
# 正常模块的命名应与变量命名一致 不可以数字开头
# 借助于importlib包可以实现导入以数字开头的模块名称
import importlib
# 相当于导入了一个叫01的模块并把导入模块赋值给了heu
heu = importlib.import_module("01")

stu = heu.Student()
stu.say()

3. import 模块 as 别名

- 导入的同时给模块起一个别名
- 其余用法与第一种相同
import p01 as p
 
stu = p.Student("yueyue",18)
stu.say()

4.选择性导入模块中的部分

  • from module_name import func_name,class_name
  • 避免导入冗余
  • 使用的时候可以直接使用导入的内容 不需要前缀
from p01 import Student,sayHello

stu = Student()
stu.say()

sayHello()

导入模块所有内容

  • from module_name import *
from p01 import *

sayHello()

stu = Student("yaona",28)
stu.say()

if __name__ == ’ __main__’ 的使用

  • 可以有效避免模块代码被导入的时候被动执行的问题
  • 建议所有程序的入口都以此代码作为入口

模块的搜索路径和存储

模块的搜索路径

  • 加载模块的时候 系统会在哪些地方寻找此模块

系统默认的模块搜索路径

  • import sys
    sys.path 属性可以获取路径列表
import sys
print(type(sys.path))
print(sys.path)

for p in sys.path:
	print(p)
<class 'list'>
['/Users/zhaoyaxin/PycharmProjects/Python', '/Users/zhaoyaxin/PycharmProjects/Python', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Users/zhaoyaxin/.conda/envs/Python/lib/python37.zip', '/Users/zhaoyaxin/.conda/envs/Python/lib/python3.7', '/Users/zhaoyaxin/.conda/envs/Python/lib/python3.7/lib-dynload', '/Users/zhaoyaxin/.conda/envs/Python/lib/python3.7/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']
/Users/zhaoyaxin/PycharmProjects/Python
/Users/zhaoyaxin/PycharmProjects/Python
/Applications/PyCharm.app/Contents/helpers/pycharm_display
/Users/zhaoyaxin/.conda/envs/Python/lib/python37.zip
/Users/zhaoyaxin/.conda/envs/Python/lib/python3.7
/Users/zhaoyaxin/.conda/envs/Python/lib/python3.7/lib-dynload
/Users/zhaoyaxin/.conda/envs/Python/lib/python3.7/site-packages
/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend

添加搜索路径

  • sys.path.append(dir)

模块的加载顺序

  • 1.搜索内存中已经加载好的模块
  • 2.搜索python的内置模块
  • 3.搜索sys.path路径

  • 包时一种组织管理代码的方式 包里面存放的是模块
  • 用于将模块包含在一起的文件夹就是包
  • 自定义包的结构
    |---- 包
    |----|---- __init__.py 包的标志文件
    |----|---- 模块1
    |----|---- 模块2
    |----|---- 子包(子文件夹)
    |----|----|---- __init__.py 包的标志文件
    |----|----|---- 子包模块1
    |----|----|---- 子包模块2

包的导入操作

import package_name

  • 直接导入一个包 可以使用__init__.py中的内容
  • 使用方式是:
    • package_name.func_name
    • package_name.class_name.func_name()
# __init__.py

def inInit():
	print("i am init package")
# pkg01 是一个文件夹 里面有__init__.py文件
import pkg01

pkg01.inInit():
i am init package

import package_name as P

  • 具体用法跟作用方式 与上述一样
  • 注意此种方法是默认对__init__.py内容的导入

import package.module

  • 导入包中某一个具体的模块
  • 使用方法
    • package.module.func_name
    • package.module.class.func()
    • package.module.class.var
# 包pkg01中含有 p01 和 __init__.py
# p01.py
class Student():
	def __init__(self, name = "NoName", age = 18):
	self.name = name
	self.age = age

	def say(self):
		print("my name is {0}".format(self.name))

def sayHello():
	print("Hi, welcome")

print("i am module p01")
# __init__.py

def inInit():
	print("i am init package")
import pkg01.p01

stu = pkg01.p01.Student()
stu.say()
i am module p01
my name is NoName

from …import 导入

  • from package import module1,module2,module3…

    • 此种导入方法不执行__init__ 的内容
    • from pkg01 import p01
      p01.sayHello()
  • from package import *

    • 导入当前包__init__.py文件中所有的函数和类
    • 使用方法
      • func_name()
      • class_name.func_name()
      • class_name.var
# 注意此种导入方式的导入的具体内容
from pkg01 import *

inInit()
i am init package

from package. module import *

  • 导入包中指定的模块的所有内容
  • 使用方法
    • func_name()
    • class_name.func_name()

其他情况

  • 在开发环境中经常会使用其他模块 可以在当前包中直接导入其他模块中的内容
    • import 完整的包或者模块的路径

__all__的用法

  • 在使用from package import * 的时候 * 可以导入的内容
  • __init__.py中如果文件为空 或者没有‘__all__’ 那么只可以把‘__init__’中的内容导入
  • ‘__init__’如果设置了‘__all__’ 的值,那么按照‘__all__’ 指定的子包或者模块进行导入 则不会导入‘__init__’中的内容
  • ‘__all__’ = [‘module1’,‘module2’,‘package1’,…]
# pkg02文件夹下有 __init__.py 和 p01.py
# __init__.py

__all__ = ['p01']

def inInit():
	print("i am init package")
from pkg02 import * 

stu = p01.Student()
stu.say()
# 因为定义了__all__ 只导入__all__中内容 没导入inInit的内容 所以报错
inInit()
my name is NoName

命名空间

  • 用于区分不同位置不同功能但相同名称的函数或者变量的一个特定前缀
  • 作用是防止命名冲突
setName()
Student.setName()
Dog.setName()
# setName()各不相同

异常使用

定义

  • 广义上的错误分为错误和异常
  • 错误指的是人为可以避免
  • 异常指的是在语法逻辑正确的前提下,出现的问题
  • python里,异常是一个类,可以处理和使用

异常的分类

AssertError 断言语句(assert)失败
AttributeError 尝试访问未知的对象属性
EOFError 用户输入文件末尾标志EOF(Ctrl+d)
FloatingPointError 浮点计算错误
GeneratorExit generator.close()方法被调用的时候
ImportError 导入模块失败的时候
IndexError 索引超出序列的范围
KeyError 字典中查找一个不存在的关键字
KeyboardInterrupt 用户输入中断键(Ctrl+c)
MemoryError 内存溢出(可通过删除对象释放内存)
NameError 尝试访问一个不存在的变量
NotImplementedError 尚未实现的方法
OSError 操作系统产生的异常(例如打开一个不存在的文件)
OverflowError 数值运算超出最大限制
ReferenceError 弱引用(weak reference)试图访问一个已经被垃圾回收机制回收了的对象
RuntimeError 一般的运行时错误
StopIteration 迭代器没有更多的值
SyntaxError Python的语法错误
IndentationError 缩进错误
TabError Tab和空格混合使用
SystemError Python编译器系统错误
SystemExit Python编译器进程被关闭
TypeError 不同类型间的无效操作
UnboundLocalError 访问一个未初始化的本地变量(NameError的子类)
UnicodeError Unicode相关的错误(ValueError的子类)
UnicodeEncodeError Unicode编码时的错误(UnicodeError的子类)
UnicodeDecodeError Unicode解码时的错误(UnicodeError的子类)
UnicodeTranslateError Unicode转换时的错误(UnicodeError的子类)
ValueError 传入无效的参数
ZeroDivisionError 除数为零

异常处理

  • 不能保证程序永远正确运行

  • 但是,必须保证程序在最坏的情况下得到的问题被妥善处理

  • python的异常处理模块全部语法为:
    try:
    尝试实现某个操作,
    如果没出现异常,任务就可以完成
    如果出现异常,将异常从当前代码块扔出去尝试解决异常

    except 异常类型1:
    解决方案1:用于尝试在此处处理异常解决问题

    except 异常类型2:
    解决方案2:用于尝试在此处处理异常解决问题

    except (异常类型1,异常类型2…)
    解决方案:针对多个异常使用相同的处理方式

    excpet:
    解决方案:所有异常的解决方案

    else:
    如果没有出现任何异常,将会执行此处代码

    finally:
    管你有没有异常都要执行的代码

流程

1.执行try下面的语句
2.如果出现异常,则在except语句里查找对应异常病进行处理
3.如果没有出现异常,则执行else语句内容
4.最后,不管是否出现异常,都要执行finally语句

  • 除except(最少一个)以外,else和finally可选

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

用户手动引发异常

  • 某些情况 用户希望自己引发一个异常的时候 可以使用
  • raise 关键字引发异常
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

关于自定义异常

  • 只要是raise异常,则推荐自定义异常

  • 在自定义异常的时候,一般包含以下内容:

    • 自定义发生异常的异常代码
    • 自定义发生异常后的问题提示
    • 自定义发生异常的行数
  • 最终的目的是,一旦发生异常,方便程序员快速定位错误现场

发布了15 篇原创文章 · 获赞 0 · 访问量 333

猜你喜欢

转载自blog.csdn.net/weixin_38279239/article/details/102626163