Python语言学习笔记
Python语言学习笔记文章目录Python语言学习笔记第一阶段day01一、Python的理解三种语言二、环境:python3.6+ pycharmday02一、变量二、字符串字符串下标和切片切片day03变量如何使用变量数字类型字符串类型切片运算符赋值运算符列表【增删改查】**元祖****字典****set集合**day04流程控制缩进循环结构for循环**random模块****string
Python语言学习笔记
文章目录
第一阶段
Markdown快捷键
快捷键 | 作用 | 快捷键 | 作用 |
---|---|---|---|
Ctrl+1 | 一阶标题 | Ctrl+B | 字体加粗 |
Ctrl+2 | 二阶标题 | Ctrl+I | 字体倾斜 |
Ctrl+3 | 三阶标题 | Ctrl+U | 下划线 |
Ctrl+4 | 四阶标题 | Ctrl+T | 创建表格 |
Ctrl+5 | 五阶标题 | Ctrl+K | 创建超链接 |
Ctrl+6 | 六阶标题 | Ctrl+F | 搜索 |
Ctrl+L | 选中某句话 | Ctrl+E | 选中相同格式的文字 |
Ctrl+D | 选中某个单词 | Ctrl+H | 搜索并替换 |
Ctrl+Home | 返回Typora顶部 | Ctrl+End | 返回Typora底部 |
Alt+Shift+5 | 删除线 | Ctrl+Shift+I | 插入图片 |
Ctrl+Shift+【 | 有序列表 | Ctrl+Shift+】 | 无序列表 |
两个“ `` ” | 小代码块 |
day01
【十进制 < —— >二进制】>>> bin(十进制)
【二进制< —— >十进制 】>>> int(’‘二进制’’ , 2)
一、Python的理解
三种语言
-
低级语言:
第一代计算机语言:机器语言。
完全由0,1组成,执行效率高,能够直接被机器识别出来。开发效率低,可移植性差,难理解,难以阅读
第二代计算机语言:汇编语言。特定的符号代表特定的指令。理解起来相对容易,开发效率相对提高 -
高级语言:
更接近自然语言,开发效率提高,更好理解、更好阅读,可移植性强
【C语言(能够直接操作硬件,对性能要求高,对硬件要求高)、
java(安卓、大数据,大型WEB开发)
python(web网站、爬虫、数据分析、数据挖掘、机器学习、深度学习、自动化、运维)
】
二、环境:python3.6+ pycharm
环境的安装路径不要用中文
规范:不用中文,少用符号,尽量有意义
day02
一、变量
1.有事找百度,万事找谷歌
2.注释:单行——#,多行——''' '''。快捷键:ctrl+/
注意事项:在多行注释中不能嵌套使用
3.变量名 只能是字母开头 + 不能数字开头 + 不能和关键字(33个)冲突 + 尽量不要用中文 【驼峰体:MyPhoto】
4.python 中的数据类型
数字类型:int 整型,float 浮点型
获取数字类型:
number = 111
print(type(number))
查看关键字的命令
import keyword
print(keyword.kwlist)
二、字符串
-
字符串的内容可以包含字母、标点、特殊符号、中文、日文等全世界的所有文字,用单或双引号进行括起来,注意是成对使用
-
注意:不要使用不同的引号包围字符串 单包裹双 双包裹单
-
输入时要求换行,输出时要求不换行
-
name = "床前明月光"\ "疑似地上霜"\ "举头望明月"\ "低头思故乡"\ print(name)
输出时要求换行
name = """ 床前明月光 疑似地上霜 举头望明月 低头思故乡 """ print(name)
-
字符串格式化
name = "小猪"
print('%s在吃饭'%name) #第一种方式
print('{}在吃饭'.format(name))#第二种方式
print(f'{name}在吃饭') #第三种方式
print('%s在%s'%('木易','吃饭'))#第四种方式
print('%s在%s'%(name,do_something))#第四种方式的格式
字符串下标和切片
下标
所谓“下标”
,就是编号,就好比超市中的存储柜的编号,通过这个编号就能找到相应的存储空间
- 编程中的起始位是0
name = 'abcdef'
print(name[0])
print(name[1])
print(name[2])
#字符串的增删改查
string1 = [1,2,3,4]
string2 = ['a','b','c','d']
print(string1 + string2) #字符串的追加
print(string1.__add__(string2))# 将string2以列表的形式插入string1中
print(string1[1]) #打印字符串中的相关位置的值
切片
切片是指对操作的对象截取其中一部分的操作
切片的语法:[起始:结束:步长]
name = 'abcdef'
print(name[0:3]) # 取 下标0~2 的字符
print(name[0:5:1]) # 取下标0~5 的字符,但中间间隔单位长度为1
print(name[0::1]) #默认从下标0到最后,中间间隔单位长度为1
day03
输出
print(我们需要输出的东西)
注释
说明某段代码的作用,意义
方便我们的阅读
方便我们的调试
-
单行注释
- 从井号
#
开始,直到这行结束为止的所有内容都是注释。Python 解释器遇到#
时,会忽略它后面的整行内容。
# 这是单行注释
- 从井号
-
多行注释
- 三对英文的引号可以是单引号或者双引号
-
多行注释指的是一次性注释程序中多行的内容(包含一行)
""" 这是多行注释 """
注意事项:
**不要嵌套**
给一行代码加说明的时候, 放在右边
给一段代码说明,放在上面
变量
菜篮子 :临时存储东西
程序就是用来处理数据的,而变量就是用来存储数据的
变量(Variable)可以看成一个小箱子,专门用来“盛装”程序中的数据。每个变量都拥有独一无二的名字,通过变量的名字就能找到变量中的数据。
从底层看,程序中的数据最终都要放到内存(内存条)中,变量其实就是这块内存的名字。
在编程语言中,将数据放入变量的过程叫做赋值(Assignment)。Python 使用等号=
作为赋值运算符,具体格式如下:
变量名 = 变量值
name = value
name 表示变量名;
value 表示值,也就是要存储的数据。
如何使用变量
直接用变量名就可以了
注意,变量是标识符的一种,它的名字不能随便起,要遵守 Python 标识符命名规范,还要避免和 Python 内置函数以及 Python 保留字重名。
- 变量名只能是 字母、数字或下划线的任意组合
- 变量名的第一个字符不能是数字
- 关键字不能声明为变量名
不建议使用中文
见名知义 - 查看关键字
import keyword
print(keyword.kwlist)
命名风格:
+ 驼峰体
+ 下划线连接
## 数据类型
我们学习变量是为了让计算机能够像人一样去记忆事物的某种状态,而变量的值就是用来存储事物状态的,很明显事物的状态分成不同种类的(比如人的年龄,身高,职位,工资等等),所以变量值也应该有不同的类型
不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)
数字类型
- int整型
- 整数
- float浮点型
- 在编程语言中,小数通常以浮点数的形式存储
- 通常每隔三个数字添加一个下划线,类似于英文数字中的逗号。下划线不会影响数字本身的值
查看数据类型
type(你要看的)
number = 111
print(type(number))
字符串类型
字符串是 Python 中最常用的数据类型。我们可以使用引号( ’ 或 " )来创建字符串。
创建字符串很简单,只要为变量分配一个值即可
若干个字符的集合就是一个字符串(String)。Python 中的字符串必须由双引号" "
或者单引号' '
包围,具体格式为:
"字符串内容"
'字符串内容'
字符串的内容可以包含字母、标点、特殊符号、中文、日文等全世界的所有文字
注意事项:
- 不要使用不同的引号包围字符串 单包裹双 双包裹单
- 对引号进行转义 在引号前面添加反斜杠
\
就可以对引号进行转义
"""
长字符串
"""
字符串拼接
切片
切片是指对操作的对象截取其中一部分的操作
切片的语法:[起始:结束:步长]
name = 'abcdef'
print(name[0:3]) # 取 下标0~2 的字符
格式化字符串的多种方式
name = '木易'
print('%s在吃饭'%name)
#了解即可
print('{}在吃饭'.format(name))
print(f'{name}在吃饭')
name = ' abcdefg '
print(name.strip()) # 去除左右两边的空格
print(name.lstrip()) # 去除左右左边的空格
print(name.rstrip()) # 去除左右右边的空格
# 转换大小写
print(name.upper()) # 全部转大写
name = 'ABCDEFG'
print(name.lower()) # 全部转小写
name = 'hello world'
print(name.title()) # 将每一个单词的第一个字母变为大写,其他变为小写
file = '老杨.jpg'
print(file.startswith('老杨')) # 判断是不是老杨开始的
print(file.endswith('g')) # 判断是不是g开始的
布尔类型 只有两个值 注意大小写
True 真 对的
False 假 错的
输入
input(提示信息)
运算符
算术运算符
print(6+5)
print(6-5)
print(6*5) # 乘法
print(1/1) # 他的结果永远是小数
print(6//5) # 整除 取整数部分 取模
print(6**5) # 幂运算
赋值运算符
- 复合赋值运算符
比较运算符
初学 Python,大家可能对 is 比较陌生,很多人会误将它和 == 的功能混为一谈,但其实 is 与 == 有本质上的区别,完全不是一码事儿。
== 用来比较两个变量的值是否相等,而 is 则用来比对两个变量引用的是否是同一个对象
逻辑运算符
成员运算符
只有in ,not in 两个,用来测试数据中是否包含指定的成员
序列
所谓序列,指的是一块可存放多个值的连续内存空间,这些值按一定顺序排列,可通过每个值所在位置的编号(称为索引)访问它们。
为了更形象的认识序列,可以将它看做是一家旅店,那么店中的每个房间就如同序列存储数据的一个个内存空间,每个房间所特有的房间号就相当于索引值。也就是说,通过房间号(索引)我们可以找到这家旅店(序列)中的每个房间(内存空间)。
在 Python中,序列类型包括字符串、列表、元组、集合和字典,这些序列支持以下几种通用的操作,但比较特殊的是,集合和字典不支持索引、切片、相加和相乘操作。
列表【增删改查】
从形式上看,列表会将所有元素都放在一对中括号[ ]
里面,相邻元素之间用逗号,
分隔
列表中的元素,个数没有限制,只要是 Python 支持的数据类型就可以
列表可以存储整数、小数、字符串、列表元组等任何类型的数据,并且同一个列表中元素的类型也可以不同
- 创建列表
- 使用[]直接创建列表
- 使用 list() 函数创建列表
- 删除列表
- 对于已经创建的列表,如果不再使用,可以使用
del
关键字将其删除 - 实际开发中并不经常使用 del 来删除列表,因为 Python 自带的垃圾回收机制会自动销毁无用的列表,即使开发者不手动删除,Python 也会自动将其回收
- 对于已经创建的列表,如果不再使用,可以使用
列表添加元素
实际开发中,经常需要对 Python列表进行更新,包括向列表中添加元素、修改表中元素以及删除元素
- 列表是序列的一种,所以也可以使用
+
进行连接,这样就相当于在第一个列表的末尾添加了另一个列表 - append() 方法用于在列表的末尾追加元素
- extend() 和 append() 的不同之处在于:extend() 不会把列表或者元祖视为一个整体,而是把它们包含的元素逐个添加到列表中
- append() 和 extend() 方法只能在列表末尾插入元素,如果希望在列表中间某个位置插入元素,那么可以使用 insert() 方法
name = ['c++','java','python']
name.append('php')
print(name)
name = ['c++','java','python']
name.extend('php')
print(name)
name = ['c++','java','python']
name.insert(2,'php')
print(name)
列表删除元素的方式
- 根据目标元素所在位置的索引进行删除,可以使用 del 关键字或者 pop() 方法
- 根据元素本身的值进行删除,可使用列表(list类型)提供的 remove() 方法
- 将列表中所有元素全部删除,可使用列表(list类型)提供的 clear() 方法
name = ['c++','java','python']
del name[2] #对列表中的位置进行删除
name.pop(0) #对指定下标进行删除
name.remove('c++') #对指定元素进行删除
name.clear()#清除列表中 的内容
print(name)
列表修改元素的方式
- 修改单个元素
- 修改一组元素
name = ['c++','java','python']
# del name[0]
# name.insert(0,'c--')
name[0] = 'c--'
name[0:2] = [1,2]
print(name)
列表查找元素的方式
-
count() 方法用来统计某个元素在列表中出现的次数
-
index() 方法用来查找某个元素在列表中出现的位置(也就是索引),如果该元素不存在,则会导致 ValueError 错误,所以在查找之前最好使用 count() 方法判断一下
print(list) count = list.count('P')#统计元素出现的个数 print(count) print(list.index('P')) #查找该元素出现的位置
range()快速初始化数字列表
print(list(range(7)))
元祖
元组(tuple)是 Python 中另一个重要的序列结构,和列表类似,元组也是由一系列按特定顺序排序的元素组成
元组和列表(list)的不同之处在于:
- 列表的元素是可以更改的,包括修改元素值,删除和插入元素,所以列表是可变序列;
- 而元组一旦被创建,它的元素就不可更改了,所以元组是不可变序列
- 元组也可以看做是不可变的列表,通常情况下,元组用于保存无需修改的内容
tuple 元组是一个只读版本的 list 列表,元组要比列表更加轻量级,所以从总体上来说,元组的性能速度要由于列表
- 从形式上看,元组的所有元素都放在一对小括号
( )
中,相邻元素之间用逗号,
分隔 - 从存储内容上看,元组可以存储整数、实数、字符串、列表、元组等任何类型的数据,并且在同一个元组中,元素的类型可以不同
创建元祖
-
使用()直接创建
-
使用tuple()函数创建元祖
tuple = (1,2,3,4,5) print(tuple) print(type(tuple))
字典
Python 字典(dict)是一种无序的、可变的序列,它的元素以“键值对(key-value)”的形式存储。相对地,列表(list)和元组(tuple)都是有序的序列,字典类型是 Python 中唯一的映射类型。“映射”是数学中的术语,简单理解,它指的是元素之间相互对应的关系,即通过一个元素,可以唯一找到另一个元素。字典中,习惯将各元素对应的索引称为键(key),各个键对应的元素称为值(value),键及其关联的值称为“键值对”
man = {'qiaoqiao':'美女','路太长':'18岁'}
print(man)
print(type(man))
print(man['qiaoqiao'])
字典添加键值对
为字典添加新的键值对很简单,直接给不存在的 key 赋值即可
字典修改键值对
Python 字典中键(key)的名字不能被修改,我们只能修改值(value)
字典中各元素的键必须是唯一的,因此,如果新添加元素的键与已存在元素的键相同,那么键所对应的值就会被新的值替换掉,以此达到修改元素值的目的
#字典 键:值 dict
man = {'c':"学过",'c++':'没学过'}
print(man)
print(man.pop('c')) #查找键为“c”的值
print(man['c++']) #同上
print(man.items()) #打印自己的元素
print(type(man))
set集合
Python中的集合,和数学中的集合概念一样,用来保存不重复的元素,即集合中的元素都是唯一的,互不相同。集合是无序的,集合中的元素是唯一的,集合一般用于元组或者列表中的元素去重
从形式上看,和字典类似,Python 集合会将所有元素放在一对大括号 {} 中,相邻元素之间用“,”分隔
添加元素
set 集合中添加元素,可以使用 set 类型提供的 add() 方法实现
删除元素
删除现有 set 集合中的指定元素,可以使用 remove() 方法
如果我们不想在删除失败时令解释器提示 KeyError 错误,还可以使用 discard() 方法,此方法和 remove() 方法的用法完全相同,唯一的区别就是,当删除集合中元素失败时,此方法不会抛出任何错误
访问集合
由于集合中的元素是无序的,因此无法向列表那样使用下标访问元素。Python 中,访问集合元素最常用的方法是使用循环结构,将集合中的数据逐一读取出来
#集合 set
set = {1,2,3,4}
print(set)
print(type(set))
set.add(5) #集合中追加元素
print(set)
day04
流程控制
前面我们看到的代码都是顺序执行的,也就是先执行第1条语句,然后是第2条、第3条……一直到最后一条语句,这称为顺序结构。
但是对于很多情况,顺序结构的代码是远远不够的,比如一个程序限制了只能成年人使用,儿童因为年龄不够,没有权限使用。这时候程序就需要做出判断,看用户是否是成年人,并给出提示。
在 Python中,可以使用 if else 语句对条件进行判断,然后根据不同的结果执行不同的代码,这称为选择结构或者分支结构。
-
单分支
if 表达式: 代码 if 3>2: print(666)
True 1
False 0
缩进
注意:tab不要和空格混合使用
- 双分支
if 表达式:
代码
else:
代码
name = input('请输入用户名')
if name == '木易':
print('登录成功')
else:
print('登录失败')
多分支
如果 表达式:
代码
或者如果 表达式:
代码
或者如果 表达式:
代码
或者:
代码
age = int(input('请输入用户名'))
if age == 18:
print(666)
elif age==28:
print(777)
elif age == 38:
print(88)
else:
print(999)
pass占位符
不起到实际的作用,仅用于占位
a = 3
b = 2
if a>b:
if a==3:
print(666)
if a>b and a == 3:
print(666)
循环结构
while 表达式:
代码
else:
代码
a = 0
while a<5:
print(666)
a+=1
else:
print(777)
while循环
a = 0
while a <10:
a +=1
pass
break
退出当前循环
for循环
for 循环,它常用于遍历字符串、列表、元组、字典、集合等序列类型,逐个获取序列中的各个元素
for 迭代变量 in 字符串|列表|元组|字典|集合:
代码块
迭代变量用于存放从序列类型变量中读取出来的元素,所以一般不会在循环中对迭代变量手动赋值;代码块指的是具有相同缩进格式的多行代码(和 while 一样),由于和循环结构联用,因此代码块又称为循环体
a = [1,2,3,4,5,6]
b = '桥桥是班花'
c = {'name':'木易','age':18}
for i in c:
print(c[i])
for i,j in c.items():
print(j)
for x in range(10):
print(666)
无论是 while 循环还是 for 循环,其后都可以紧跟着一个 else 代码块,它的作用是当循环条件为 False 跳出循环时,程序会最先执行 else 代码块中的代码
跳出循环
在执行 while 循环或者 for 循环时,只要循环条件满足,程序将会一直执行循环体,不停地转圈。但在某些场景,我们可能希望在循环结束前就强制结束循环,Python提供了 2 种强制离开当前循环体的办法
- 使用 continue 语句,可以跳过执行本次循环体中剩余的代码,转而执行下一次的循环
- 只用 break 语句,可以完全终止当前循环
random模块
可以产生指定范围的随机数,字符串等
import random # 导入模块
a = random.choice('ABCDEFG') # 参数也可以是一个列表
s = 'ABCDEFG'
b = random.sample(s,3) # 从数据源中随机抽取3个值
c = random.randint(50,100) # 打印随机数
print(a) #随机打印一个字母
print(b)
print(c)
string 字符串
import string # 导入模块
a = string.ascii_letters # 所有字母
b = string.ascii_uppercase # 大写字母
c = string.ascii_lowercase # 小写字母
d = string.punctuation # 特殊字符
e = string.digits # 数字
print(a)
print(b)
print(c)
print(d)
print(e)
python推导式
#循环体for 推导式
for x in range(10):
print(x)
# 列表推导式,利用range区间、元组、列表、字典、和集合等数据类型,快速生成一个满足要求的列表
# [表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]]
a = [x for x in range(9)]
print(a)
day05
字符和编码
字符串类型、文本文件的内容都是由字符组成的,但凡涉及到字符的存取,都需要考虑字符编码的问题。
人类在与计算机交互时,用的都是人类能读懂的字符,如中文字符、英文字符、日文字符等
而计算机只能识别二进制数,毫无疑问,由人类的字符到计算机中的数字,必须经历一个过程
翻译的过程必须参照一个特定的标准,该标准称之为字符编码表,该表上存放的就是字符与数字一一对应的关系。
字符编码中的编码指的是翻译或者转换的意思,即将人能理解的字符翻译成计算机能识别的数字
字符编码的发展经历了三个重要的阶段
1.一家独大
现代计算机起源于美国,所以最先考虑仅仅是让计算机识别英文字符,于是诞生了ASCII表
阶段二:诸侯割据、天下大乱
为了让计算机能够识别中文和英文,中国人定制了GBK,每个国家都各自的字符,为让计算机能够识别自己国家的字符外加英文字符,各个国家都制定了自己的字符编码表,此时,美国人用的计算机里使用字符编码标准是ASCII、中国人用的计算机里使用字符编码标准是GBK、日本人用的计算机里使用字符编码标准是Shift_JIS
阶段三:分久必合
unicode于1990年开始研发,1994年正式公布,具备两大特点:
#1. 存在所有语言中的所有字符与数字的一一对应关系,即兼容万国字符
#2. 与传统的字符编码的二进制数都有对应关系
文件操作
使用文件的目的:
就是把一些存储存放起来,可以让程序下一次执行的时候直接使用,而不必重新制作一份,省时省力
应用程序运行过程中产生的数据最先都是存放于内存中的,若想永久保存下来,必须要保存于硬盘中。应用程序若想操作硬件必须通过操作系统,而文件就是操作系统提供给应用程序来操作硬盘的虚拟概念,用户或应用程序对文件的操作,就是向操作系统发起调用,然后由操作系统完成对硬盘的具体操作。
在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件
open(文件名,访问模式)
示例如下:
f = open('test.txt', 'w','utf-8')
关闭文件 close()
写数据 write()
读数据 read()
对文件的操作
1. 打开文件
open(文件名,打开的模式,[可选:编码格式])
选择要操作的文件是什么
操作的模式 r w a 文本类型
二进制包含文本类型 rb wb ab 二进制文件 图片 音频 视频
r read 读取,读取的前提是需要有这份文件
- 有这份文件,就更改文件内容
- 没有这份文件,就创建这份文件
- w write 创建写 覆盖写,如果没有文件,会覆盖原有的再创建
- a append 追加写,不会覆盖原有的内容,在后面加入内容,没有就创建
2. 操作文件
- write
- read
- readlines 打印所有行
- readline 打印一行
- 键盘敲烂,月薪过万
3. 关闭文件
# 文件的操作
f =open('作业.txt','r',encoding='utf-8') #打开文件全名,加后缀名,只有文本类型的需要加编码模式
data = f.read() #打开模式:读read,写write,追加写append,覆盖写w
f.close() #关闭文件
print(data)
# 复制
'''f1 = open('2.jpg','rb')
data = f.read()
f1.close()'''
# 将内容写入
f1 = open('2.jpg','wb')
f.write(data)
f.close()
'''练习1
f2 = open('index.txt','w',encoding='utf-8')
f2.write('写入的内容')
f2.close()
'''
'''练习2
需求:输入文件的名字,然后程序自动完成对文件进行备份
读取文件的内容
将内容写成文件
思路
file_name = input('请输入文件的名字')
oldfile = open(file_name,'rb')
data = oldfile.read()
oldfile.close()
newfile = open('备份.txt','wb')
newfile.write(data)
newfile.close()
'''
提取文件的后缀名,
# rfind:返回字符串最后一次出现的位置(从右向左查询)。如果没有匹配项则返回-1
简化写法
第一种方法,可能会忘记关闭文件
f = open('作业.txt','w+',encoding='utf-8')
f.write('1234567')
f.close()
简化后如下:自动关闭
with open('作业.txt','w+',encoding='utf-8') as f:
f.write('123456')
文件夹的操作:
file_name = input('请输入文件名')
f = open(file_name,'r',encoding='utf-8')
data = f.read()
data1 = f.readlines()
data2 = f.readline()
f.close()
f = open('备份','w',encoding='utf-8')
f.write(data)
f.close()
day06
函数
什么是函数
为什么要用函数
以前的代码缺点:冗余,可读性很差,维护性太差
Python 提供了一个功能,即允许我们将常用的代码以固定的格式封装(包装)成一个独立的模块,
只要知道这个模块的名字就可以重复使用它,这个模块就叫做函数(Function)。
比如,在程序中定义了一段代码,这段代码用于实现一个特定的功能。
问题来了,如果下次需要实现同样的功能,难道要把前面定义的代码复制一次?
如果这样做实在太傻了,这意味着每次当程序需要实现该功能时,都要将前面定义的代码复制一次。
正确的做法是,将实现特定功能的代码定义成一个函数,
每次当程序需要实现该功能时,只要执行(调用)该函数即可。
其实,函数的本质就是一段有特定功能、可以重复使用的代码,
这段代码已经被提前编写好了,并且为其起一个“好听”的名字。
在后续编写程序过程中,如果需要同样的功能,直接通过起好的名字就可以调用这段代码
写代码 :不要重复造轮子
同样的代码 考虑封装
用循环 用函数
内置函数:
print input int str type
自定义函数
函数语法
def 函数名(参数1,参数2,...):
"""文档描述"""
函数体
return 值
调用函数:
函数名()
函数的使用分为定义阶段与调用阶段,定义函数时只检测语法,看是否有语法错误SyntaxError
不执行函数体代码,函数名加括号即函数调用,只有调用函数时才会执行函数体代码
调用函数也就是执行函数。
如果把创建的函数理解为一个具有某种用途的工具,那么调用函数就相当于使用该工具。
调用函数很简单的,通过 函数名(参数) 即可完成调用
函数的说明
help(函数名)
print(函数名.doc)
函数的参数
形参和实参
形参:在定义函数时,函数名后面括号中的参数就是形式参数,简称形参,
形参的值是不确定的,只是用来占位
实参:在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数
真正参与运算
实参和形参的区别,就如同剧本选主角,剧本中的角色相当于形参,而演角色的演员就相当于实参
形参和实参的关系:
1.在调用阶段,实参(变量值)会绑定给形参(变量名)
2.这种绑定关系只能在函数体内使用
3.实参和形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系
实参是传入的值,但值可以是以下形式
形式一:
func(1,2)
形式二:
a= 1
b = 2
func(a,b)
形式三:
func(int(‘1’),2)
函数的返回值
函数一旦遇到return 就不往下面执行了
返回值就是函数执行的结果,并不是所有的函数都必须要有返回值,开发中根据需求去定到底是否需要返回值
通常情况下,一个函数只有一个返回值,实际上 Python 也是如此,
只不过 Python 函数能以返回列表或者元组的方式,将要返回的多个值保存到序列中,
从而间接实现返回多个值的目的。
因此,实现 Python 函数返回多个值,有以下 2 种方式:
1. 在函数中,提前将要返回的多个值存储到一个列表或元组中,然后函数返回该列表或元组;
2. 函数直接返回多个值,之间用逗号( , )分隔,Python 会自动将多个值封装到一个元组中,
其返回值仍是一个元组
函数遇到return就返回,不会继续往下运行,默认返回None
如果一个函数没有返回值,则表示返回None
def lao():
print('从前有座道馆')
print('山里有座庙')
print('庙里有个老和尚')
print('老和尚对小和尚说')
print('山下的女人是老虎')
age = int(input('请输入年龄'))
if age<5:
for x in range(10):
lao()
elif 5<age<10:
for x in range(3):
lao()
命名规范:
1. 只能是字母,数字,下划线
2. 不能数字开始
3. 不能和关键字冲突
尽量不要用中文 驼峰法 下划线连接法
顾名思义
def 函数名(): 标识符 命名规范 变量
函数体
如何调用:
函数名()
函数在定义的时候做的事情:检测语法
调用函数:函数名()
函数名 : 要执行的函数里面的代码的内存地址
内存地址()
怎么看内存地址 id() 方便我么
最基本的定义 调用
有一定的编程基础
“”"
# def index():
# """
# 自动生成的
# :return:
# """
# for a in range(5):
# print('*'*(5-a))
# index()
# print(id(index)) # 查看index 的内存地址
# 2434115317544() 不可以
# 文档描述 函数的注释 方便我们知道这个函数到底是干嘛用的
# help(index)
# print(index.__doc__)
# 需求:一个函数 用来打印两个数的相乘的结果
# 需要得到他的结果是什么?
# def index(a,b):
# c = a*b
# print(c)
# return 30
# print(11)
# print(22)
#
# a = index(4,5)
# print(a)
函数 一旦看到个return 返回
函数 工厂:做事情 参数:原材料 返回值 服装成品 return
服装工厂 在使用的时候传给他的 可以返回 也可以不返回
当你自己没有return 返回的时候 Python会默认给你返回 None 空值 空
位置参数
按照顺序来,多一个,少一个都不行 依次进行
位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,
换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致
实参和形参数量必须一致
在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),
否则 解释器会抛出 TypeError 异常,并提示缺少必要的位置参数
关键字参数
可以不用按照顺序来 可以指定顺序 指哪打拿
关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,
不再需要与形参的位置完全一致,只要将参数名写正确即可
默认参数
在调用函数时如果不指定某个参数,解释器会抛出异常。
为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。
这样的话即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值
默认参数
不指定的时候就按照默认的值来进行 指定一个默认值
顺序问题:
位置参数 关键字参数 默认参数
可变参数
*args
*args 表示创建一个名为 args 的空元组,该元组可接受任意多个外界传入的非关键字实参
**kwargs
**kwargs 表示创建一个名为 kwargs 的空字典,该字典可以接收任意多个以关键字参数赋值的实际参数
def index(a,b,c,*d):
print(a,b,c)
index(1,c=9,b=6)
def index(*args,**kwargs):
print(args)
print(kwargs)
list1 =[1,2,3,34,5]
index(a = 1,b =2)
index(list1)
dict1 = {'a':10,'b':20}
index(*dict1)
函数的返回值
函数一旦遇到return 就不往下面执行了
返回值就是函数执行的结果,并不是所有的函数都必须要有返回值,开发中根据需求去定到底是否需要返回值
通常情况下,一个函数只有一个返回值,实际上 Python 也是如此,
只不过 Python 函数能以返回列表或者元组的方式,将要返回的多个值保存到序列中,
从而间接实现返回多个值的目的。
因此,实现 Python 函数返回多个值,有以下 2 种方式:
1. 在函数中,提前将要返回的多个值存储到一个列表或元组中,然后函数返回该列表或元组;
2. 函数直接返回多个值,之间用逗号( , )分隔,Python 会自动将多个值封装到一个元组中,
其返回值仍是一个元组
函数遇到return就返回,不会继续往下运行,默认返回None
如果一个函数没有返回值,则表示返回None
命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。
命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
作用域:
内建名称空间
伴随python解释器的启动/关闭而产生/回收,因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内建函数名
全局名称空间
伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中
局部名称空间
伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中
名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间,而查找一个名字,必须从三个名称空间之一找到,查找顺序为:局部名称空间->全局名称空间->内置名称空间
作用域
就是变量的有效范围,就是变量可以在哪个范围以内使用。
有些变量可以在整段代码的任意位置使用,有些变量只能在函数内部使用,
有些变量只能在 for 循环内部使用。变量的作用域由变量的定义位置决定,
在不同位置定义的变量,它的作用域是不一样的
局部变量
在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,
我们将这样的变量称为局部变量
要知道,当函数被执行时,Python 会为其分配一块临时的存储空间,
所有在函数内部定义的变量,都会存储在这块空间中。
而在函数执行完毕后,这块临时存储空间随即会被释放并回收,
该空间中存储的变量自然也就无法再被使用。函数的参数也属于局部变量,只能在函数内部使用
全局变量
除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,
这样的变量称为全局变量
和局部变量不同,全局变量的默认作用域是整个程序,
即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用
要在函数内修改全局名称空间中名字的值,则需要用到global关键字
注意:
函数名重名,会覆盖
函数名可以当成变量名
def index(a,b=5):
print('a 是 %s'%a)
print('b 是 %s'%b)
print(a*b)
c = a*b
return c
return 1,2,3
return a*b
name = 7
age = 77
a,b,c = index(a=name,b=age)
print(a,b,c)
函数名:要执行的函数里面的代码内存地址
例:
def foo():
print(666)
foo()
查看内存地址
a = 1
print(id(a))
def foo():
print(666)
print(id(foo))
day08
函数的说明
help(函数名)
print(函数名.__doc__)
匿名函数
用来表达一个简单的函数,函数调用的次数很少,基本上就是调用一次
用lambda关键字能创建匿名函数,这种函数省略了用def 声明函数的标准步骤
lambda 参数列表:表达式
1.给它定义一个名字(使用的少)
index = lambda a,b:a+b
print(index(2,3))
2.把这个函数当做参数传给另一个函数使用
调用匿名函数
# 方式一:
res = (lambda x,y:x+y)(1,2)
print(res)
# 方式二:
func = lambda x,y:x+y
res = func(1,2)
print(res)
# 匿名函数用于临时调用一次的场景:更多的是将匿名与其他函数配合使用
高阶函数
1、一个函数作为另一个函数的参数
def index(a):
print('index')
print(a)
def foo(a):
print('foo')
a(1)
foo(index)
2、一个函数作为另一个函数的返回值
def index():
print('index')
def foo():
print('foo')
return index #不加括号,只写函数名
a = foo()
a()
3、 一个函数内部在定义一个函数
闭包
如果在一个内部函数里,对在外部作用域(但不是全局作用域)的变量进行引用
那么内部函数就被认为是闭包
嵌套函数,内部函数调用外部函数的变量
def outer():
x = 1
def inner():
y = x
return inner
修改外部作用域的变量
def outer():
x = 10
def inner():
nonlocal x #相当于一个global x,将局部变量升级为全局变量
x +=1
print(x)
return inner
outer()()
函数的递归调用:
是函数嵌套调用的一种特殊形式
具体是指,在调用一个函数的过程中又直接或者间接的调用到本身
递归函数:就是函数内部自己调用自己
递归最重要的就是找到出口(停止的条件)
递归最大的层级是1000层
import sys
# 查看层级
sys.getrecursionlimit()
# 设置层级
sys.setrecursionlimit()
index = [1,[2,[3,[4]]]]
def foo(list1):
for x in list1:
if type(x) == list:
foo(x)
else:
print(x)
foo(index)
第二阶段
day11
1. 类与对象的概念
类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体。
那么问题来了,先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看
- 在现实世界中:肯定是先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,比如
人类、动物类、植物类等概念。也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在,比如你无法告诉我人类
具体指的是哪一个人。
- 在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的:先定义函数,后调用函数,类也是一样的:在程序中需要先定义类,后调用类。不一样的是:调用
函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
2. 定义类
按照上述步骤,我们来定义一个类(我们站在学校的角度去看,在座的各位都是学生)
-
在现实世界中,先有对象,再有类
对象1:张三 特征: 学校=学校 姓名=张三 性别=男 年龄=18 技能: 学习 吃饭 睡觉 对象2:李四 特征: 学校=学校 姓名=李四 性别=女 年龄=38 技能: 学习 吃饭 睡觉
-
在程序中,务必保证:先定义(类),后使用类(用来产生对象)
#在Python中程序中的类用class关键字定义,而在程序中特征用变量标识,技能用函数标识,因而类中最常见的无非是:变量和函数的定义 class Student: school='学校' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping')
注意:
-
类中可以有任意python代码,这些代码在类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过
Student.__dict
__查看 -
类中定义的名字,都是类的属性,点是访问属性的语法。
-
3. 类的使用
-
引用类的属性
Student.school #查 Student.school='学校教育' #改 Student.x=1 #增 del Student.x #删
-
调用类,或称为实例化,得到程序中的对象
s1=Student() s2=Student() s3=Student() #如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__
-
__*init*__方法
#注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值 class Student: school='学校' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex ...... s1=Student('张三','男',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'张三','男',18) s2=Student('李四','女',38) s3=Student('王五','男',78)
4. 对象的使用
s2.name #查,等同于s2.__dict__['name']
s2.name='王三炮' #改,等同于s2.__dict__['name']='王三炮'
s2.course='python' #增,等同于s2.__dict__['course']='python'
del s2.course #删
#1.先定义类
# 定义类的关键字是class
class Student: #类名首字母要大写
#类体内容 相识的特征和技能
school = 'XX学院' #数据属性
def __init__(self,name,age,sex):
self.Name = name
self.Sex = sex
self.Age = age
def learn(self): #self 参数 函数属性
print('is leaning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleepping')
# 2. 后产生对象
stu1 = Student() #实例化
stu2 = Student() #实例化
stu3 = Student() #实例化
# 调用函数 执行函数体内的代码 返回的事函数体执行的结果
# 类 返回的是对象 实例化三次,产生三个不同的对象
print(stu1)
print(stu2)
print(stu3)
# 增删改查
# 查
print(Student.__dict__) # 看内部的名称空间,字典的形式输出
print(Student.__dict__['school']) # 查看school的值
print(Student.school) #本质就是print(Student.__dict__['school'])
print(666)
# 增
Student.contry = 'China'
print(Student.__dict__)
print(666)
# 删
del Student.contry
print(Student.__dict__)
# 改
Student.school = 'czxy'
print(Student.school)
补充说明
- 站的角度不同,定义出的类是截然不同的;
- 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类等;
- 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类。
5. 属性查找与绑定方法
5.1 属性查找
类有两种属性:数据属性和函数属性
1、类的数据属性是所有对象共享的
# 类的数据属性是所有对象共享的,id都一样
print(id(Student.school))
print(id(s1.school)) # 4377347328
print(id(s2.school)) # 4377347328
print(id(s3.school)) # 4377347328
2、类的函数数据是绑定给对象用的,称为绑定到对象的方法
# 类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样
print(Student.learn) #<function Student.learn at 0x1021329d8>
print(s1.learn) #<bound method Student.learn of <__main__.Student object at 0x1021466d8>>
print(s2.learn) #<bound method Student.learn of <__main__.Student object at 0x102146710>>
print(s3.learn) #<bound method Student.learn of <__main__.Student object at 0x102146748>>
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常
5.2 绑定方法
定义类并实例化出三个对象
class Student:
school='学校'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def learn(self):
print('%s is learning' %self.name) # 新增self.name
def eat(self):
print('%s is eating' % self.name)
def sleep(self):
print('%s is sleeping' % self.name)
s1=OldboyStudent('张三','男',18)
s2=OldboyStudent('李四','女',38)
s3=OldboyStudent('王五','男',78)
类中定义的函数是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数
Student.learn(s1)
Student.learn(s2)
Student.learn(s3)
类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法
强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)
s1.learn() # 等同于Student.learn(s1)
s2.learn() # 等同于Student.learn(s2)
s3.learn() # 等同于Student.learn(s3)
注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。
6. 类即类型
python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
>>> list
<class 'list'>
# 实例化的到3个对象l1,l2,l3
>>> l1=list()
>>> l2=list()
>>> l3=list()
# 三个对象都有绑定方法append,是相同的功能,但内存地址不同
>>> l1.append
<built-in method append of list object at 0x10b482b48>
>>> l2.append
<built-in method append of list object at 0x10b482b88>
>>> l3.append
<built-in method append of list object at 0x10b482bc8>
# 操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3
>>> l1.append(3)
>>> l1
[3]
>>> l2
[]
>>> l3
[]
# 调用类list.append(l3,111)等同于l3.append(111)
>>> list.append(l3,111) #l3.append(111)
>>> l3
[111]
7.需求:
1、编写一个学生类,产生一堆对象
2、有一个计数器,统计实例化了多少对象
class Student:
count = 0
def __init__(self):
self.count += 1
print(Student.count)
day12
1. 面向对象介绍
1.1 编程范式
编程思想又称编程范式,编程范式指的就是编程的套路。
各种编程范式在不同的场景下都各有优劣,谁好谁坏不能一概而论
“功夫的流派没有高低之分,只有习武的人才有高低之分“
"面向"的意思就是说你写程序的时候脑子里面始终在思考的东西,
1.2 面向过程
核心是"过程"二字,过程即流程,指的是解决问题的步骤:先什么、再什么、后干什么。
我们基于面向过程编程设计程序相当于在设计一条流水线,所以说面向过程是一种机械式的思维方式
这种思维方式的基本思想就是把一个大的问题,不断的细化成一个个的子问题。在子问题里面再不断的细化,直到能在一个很小的范围当中把这个问题解决掉,最后把子问题串起来执行
**优点:**复杂的问题流程化,进而简单化
**缺点:**可扩展性差
假设说我们现在需要来做一个用户注册程序,你现在怎么做?
我们现在说的是面向过程编程,这种编程范式告诉你的是
你要设计一条流水线,你要设计流水线解决的问题是完成用户的注册。那你想用户注册应该分成哪些流程
- 输入用户名
- 判断用户的输入的合法性
- 注册(将用户输入的账号密码保存)
def interactive():
"""负责和用户交互的,输入账号密码"""
pass
def check():
"""检测合法性"""
pass
def register():
"""注册"""
pass
输入用户名代码实现
def interactive():
name=input('>>: ').strip()
pwd=input('>>: ').strip()
# 将用户输入的结果返回
return {
'name':name,
'pwd':pwd,
}
这个阶段做完后,下个阶段就是接收上个阶段的值
def check(user_info): # 检测逻辑
if len(user_info['name']) == 0:
print('用户名不能为空')
if len(user_info['pwd']) < 6:
print('密码不能少于6位')
我们在这里判断是判断完了,但是我在这个阶段是不是需要有一个结果,给到下一个阶段的输入,我们现在只是在单纯的打印,我可以给他设置一个
def check(user_info):
is_valid=True # 默认数据有效
if len(user_info['name']) == 0:
print('用户名不能为空') # 用户名为空,数据无效
is_valid=False
if len(user_info['pwd']) < 6:
print('密码不能少于6位')
is_valid=False
return {
'is_valid':is_valid,
'user_info':user_info
}
这个处理完后,下一个阶段收到这个字典以后只需要取出’is_valid’这个key对应的值就能知道,我应不应该将 user_info 序列化到文件去做永久性存储
def register(check_info):
if check_info['is_valid']:
with open('db.json','w',encoding='utf-8') as f:
json.dump(check_info['user_info'],f)
完整代码
# 复杂的问题流程化,进而简单化
import json
def interactive():
name = input('>>: ').strip()
pwd = input('>>: ').strip()
return {
'name': name,
'pwd': pwd,
}
def check(user_info):
is_valid = True
if len(user_info['name']) == 0:
print('用户名不能为空')
is_valid = False
if len(user_info['pwd']) < 6:
print('密码不能少于6位')
is_valid = False
return {
'is_valid': is_valid,
'user_info': user_info
}
def register(check_info):
if check_info['is_valid']:
with open('db.json', 'w', encoding='utf-8') as f:
json.dump(check_info['user_info'], f)
def main():
user_info = interactive()
check_info = check(user_info)
register(check_info)
if __name__ == '__main__':
main()
缺点是:
一套流水线或者流程就是用来解决一个问题,改一个组件,与其相关的组件都需要修改,牵一发而动全身,扩展性极差。比如说我们现在用户注册的时候只输入账号密码,但是我在注册的时候通常还有邮箱地址,这个就涉及到你要扩展功能
import json
import re
def interactive():
name = input('>>: ').strip()
pwd = input('>>: ').strip()
email = input('>> ').strip()
return {
'name': name,
'pwd': pwd,
'email': email
}
def check(user_info):
is_valid = True
if len(user_info['name']) == 0:
print('用户名不能为空')
is_valid = False
if len(user_info['pwd']) < 6:
print('密码不能少于6位')
is_valid = False
if not re.search(r'@.*?\.com$', user_info['email']):
print('邮箱格式不合法')
is_valid = False
return {
'is_valid': is_valid,
'user_info': user_info
}
def register(check_info):
if check_info['is_valid']:
with open('db.json', 'w', encoding='utf-8') as f:
json.dump(check_info['user_info'], f)
def main():
user_info = interactive()
check_info = check(user_info)
register(check_info)
if __name__ == '__main__':
main()
1.2 面向对象编程介绍
面向对象:核心就是对象二字
现在的问题是什么是对象?程序猿没有对象怎么办?自己new一个
天上飞的:飞机、大炮、小鸟……
地上跑的:蛇、牛、羊、猪、……
水里游的:鱼、虾、蟹、……
抽象的:规则、项目、方法……
世界上万事万物都是对象
基于面向对象设计程序就好比在创造一个世界,世界是由一个个对象组成,而你就是这个世界的上帝。
那么对象到底是什么呢?我们从西游记中的任何一个人物对象都不难总结出:对象是特征与技能的结合体。比如孙悟空的特征是:毛脸雷公嘴,技能是:七十二变、火眼金睛等。
与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界而非流程的模拟,是一种“上帝式”的思维方式。
优点是:
解决了面向过程可扩展性低的问题,这个也是我学习面向对象的核心
缺点是:
编程的复杂度远高于面向过程,不了解面向对象而立即上手并基于它设计程序,极容易出现过度设计的问题,而且在一些扩展性要求低的场景使用面向对象会徒增编程难度
应用场景:
当然是应用于需求经常变化的软件中,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
2. 定义类与实例化出对象
2.1 类与对象的概念
我们刚刚说到的是对象的概念,对象指的就是特征与技能的结合体,世界上万事万物都是对象。
类是什么?类即类别、种类。是不是就是分类。比如说 电脑类/笔类/人类 那么问题点是你是怎么分的?我是电脑类吗?不是 我和你都是单独的对象我和你都有特征和技能 电脑也有特征和技能 你不把我和电脑分为一类是因为我的特征和技能和它不一样。所以说类就是一系列对象相似的特征与技能的结合体。
补充说明
- 站的角度不同,定义出的类是截然不同的
- 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类等;
- 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类。
对象就是特征与技能的结合体,类是一系列对象相似的特征与技能的结合体。
具体一些:先解释解释什么是车? 有轮胎, 有方向盘, 有发动机, 会跑的是车. 好. 在解释一个. 什么是人. 有名字, 年龄, 爱好, 会唱歌跳舞思考的是人.那么广义上车,人就是类:但是具体的我的车,你这个人这是一个对象。
那么先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看
-
在现实世界中:肯定是先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,比如人类、动物类、植物类等概念。也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在,比如你无法告诉我人类具体指的是哪一个人。
-
在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的:先定义函数,后调用函数,类也是一样的:在程序中需要先定义类,后调用类。不一样的是:调用 函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
2.2 基本使用
我现在写程序的思路就是,先从现实世界入手。来提炼对象,这一堆对象我在总结相识的特征与技能,总结出现实当中的类,然后到程序当中就很简单了。我只需要学会python里面定义类的语法,按照总结的现实生活当中的类写出来就好了
站在学校教育的角度,大家都是学生
"""
在现实世界中:先有对象,再有类
对象1:张三 对象是技能和特征的结合体,我们看对象1就是总结它的特征和技能
特征:学校='学校教育' 名字='张三' 性别='女' 年龄=18
技能:学习 吃饭 睡觉
对象1:李四
特征:学校='学校教育' 名字='张三' 性别='男' 年龄=28
技能:学习 吃饭 睡觉
总结现实中学校的学生类:总结类就是找对象相似的特征和技能
相似的特征
学校='学校教育'
相似的技能 学习 吃饭 睡觉
"""
现实当中的类我们是总结完了,我们回到程序当中
# 在程序里面我需要 1. 先定义类 2. 产生对象
# 定义类就用到python里面造类的语法 class
class Student: # 1.1 类名的首字母大写
# 1.2 类体内容,类体应该有什么?相似的特征和技能 我们使用变量用来表示相似的特征
school = '学校教育'
# 1.3 相似的技能,技能是不是就是功能,功能在程序里面怎么表示 函数
def learn(self): # 1.4 就现在而言,你来看有什么很特别的地方吗?就是定义一个函数,有一个位置参数。是不是代表我在调用它的时候需要传递一个参数给它
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
"""
class 类名:
类体代码 对象相似的特征和技能
"""
在Python中程序中的类用class关键字定义,而在程序中特征用变量标识,技能用函数标识,因而类中最常见的无非是:变量和函数的定义
我们现在就是在程序里面将程序里面的类定义好了,接下来就是怎么产生程序里面的对象了
# 2. 后产生对象 类名加括号
stu1 = Student()
# 2.1 和我们函数的调用很像,只不过这个调用不是执行类体的代码。 执行这个段代码。Student()
# 我们会得到一个返回值,这个返回值就是我们要的对象这个过程也叫实例化
# 什么意思,类是一个模板,按照模板产生对象。类是一个抽象的概念,不是真实的存在。我们用的目的是造出对象
# 类名加括号调用一次就是实例化一次,实例化一次就是得到一个对象
stu2 = Student()
stu3 = Student()
print(stu1) # <__main__.Student object at 0x10423c160> Student这个类产生的object(对象)后面跟的是内存地址
print(stu2)
print(stu3)
我们现在说的是在现实世界当中,怎么由一个个对象,总结出现实中类。在程序当中按照怎么样的语法,把程序里的类定义出来,对象怎么造出来,那么对于程序里面类怎么使用,对象怎么使用?
3. 如何使用类
class Student:
school = '学校'
def learn(self):
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
我们在说定义类的时候,用到的关键字是class 后面跟的是名字,接下类就是类体代码。这个和我们说的函数的定义很像 函数的定义是 def 跟着函数名,函数体代码
不一样的是
我们定义一个函数,函数体的代码是在我们调用后执行。一旦执行了函数就会产生名称空间。这个名称空间叫局部名称空间。把函数内产生的名字都放到局部名称空间里面去。
我们定义类,在类的定义阶段它内部的代码就会运行。怎么证明?我们现在不行,来打印下。
class Student:
school = '学校教育'
print(school) # 如果说我们在运行的时候打印出了school的值,证明在类的定义阶段它内部的代码就会运行
def learn(self):
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
在代码执行到school = '学校教育'
这一行的时候,是不是涉及到产生新的名字。这一行是干什么?定义变量,定义变量是不是就涉及到变量名的问题。产生了新名字,定义函数 产生了函数名,那么涉及到我产生新名字了,是不是就要找一个地方存着
函数会产生函数的局部名称空间,把函数内部定义的名字放进去,
类内部的名字是不是会产生类的名称空间,把这些名字放进去。
# 查看类的名称空间
print(Student.__dict__)
我们现在说的就是类的用法,说类的用法是干什么事??类的内部产生了这些名字,我要怎么访问这个名字。针对这些名字的增删改查
我们在来看,产生这些名字会放到名称空间里面去,__dict__
看到的就是名称空间。我要看下school的值是什么?怎么访问
print(Student.__dict__['school'])
print(Student.__dict__['learn'])
类内部定义的变量,称为类的数据属性,函数称为类的函数属性
针对属性,python提供了专门的属性的访问语法,我们之前接触过
import time
# 导入模块的时候会产生模块的名称空间,把模块的名称空间是不是存到里面去,
# 我们要访问里面的名字,我们是time.__dict__访问的吗?不是
time.sleep(3) # time模块的一个属性sleep
# 一样的道理 我要你访问Student的属性怎么看?
# 查
Student.school
# 增
Student.county='China'
# 删
del Student.county
# 改
Student.school='学校'
总结:类的用法,操作它的属性。增删改查,第二个点就是实例化对象
4. 如何使用对象
在学习对象的使用之前,我们先回顾下对象是怎么来的?
现实生活先有对象再有类,程序当中先有类再实例化对象
学生一是由Student这个类实例化来的,相当于我用这个模版,实际造一个学生出来了。
这个学生应该有 学校= ‘学校’,技能是 学习 吃饭 睡觉 学生二 学生三也是一样,但是你会发现少了点什么。
这三个人现在完全一摸一样,学生一有和别人一样相似的特征,但是学生一还有名字 性别 年龄 这个是它独有的特征。我们必须要找一种解决手段,能给每个对象定制他们独有的特征。这个就使用到__init__
# __init__方法用来为对象定制对象自己独有的特征
class Student:
school = '学校'
def __init__(self, name, sex, age):
self.Name = name
self.Sex = sex
self.Age = age
def learn(self):
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
我们这样写完后到底有什么用?我们现在写的init也没有什么特殊的地方,它看起来和我们下面定义的函数差不多
stu1 = Student() # 我们现在实例化还是按照之前的老方式实例化
# 运行后报错了 __init__() missing 3 required positional arguments: 'name', 'sex', and 'age'
# init这个方法在执行的时候(加括号执行) 少了三个要求的位置参数 'name', 'sex', and 'age'
我们调用过init吗?没有,我们只是类名加括号,就是调用类,也即是说我们调的是类,没有调用init
很明显我们没有调用__init__ 就是python自动帮我们调用了,在我们实例化的时候就会调用它
我们调用它的时候不是说少了三个位置参数吗?传给他试试
stu1 = Student('张三','女',18)
如果我们现在运行代码没有报错,那就是这样的思路是对的
它到底发生了什么事情,
加上 init
方法后,实例化的步骤
-
先产生一个空对象stu1 (空对象 没有自己独有的特征)
-
触发__init__方法
Student.__init__()
# Student.__init__() 现在这个就是一个函数属性 我们可以打印出来看下 print(Student.__init__) # 你来看他是一个函数?是一个函数我们要调用是不是需要按照函数的传参规则来,有几个参数 传递几个 有4个这个self是对象自己 Student.__init__(stu1,'张三','女',18)
init干什么事?
class Student: school = '学校' # stu1 '张三''女' 18 def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age # stu1.Name='张三' # stu1.Sex='女' # stu1.Age=18 # 给stu1这个对象 添加属性 我在类里面见过吗?Student.county='China' 往类的名称空间里面添加名字 # stu1.Name='王二丫' stu1的名称空间里面添加名字 stu1 = Student('张三','女',18) print(stu1.__dict__) # 查看对象的名称空间
现在我们实现的效果就是 stu1这个对象除了有类相似的特征和技能外还有自己的特有的
只不过是现在你不知道我们怎么通过对象访问到类的数据属性和函数属性
但是我们现在你应该想得到,怎么访问到它自己的,比如说要你看下学生一的名字
#查 print(stu1.__dict__) #print(stu1.Name) #print(stu1.Sex) #print(stu1.Age)
#改 stu1.Name='李四' print(stu1.__dict__) print(stu1.Name)
#删除 del stu1.Name print(stu1.__dict__) #增 stu1.class_name='python开发' print(stu1.__dict__)
我们现在可以通过
__init__
给对象定制对象自己独有的特征,但是我们现在还不知道对象怎么访问它们相似的特征和技能
5. 属性查找与绑定方法
class Student:
school='学校'
def __init__(self,name,sex,age):
self.Name=name
self.Sex=sex
self.Age=age
def learn(self):
print('%s is learning %s' %(self.Name))
def eat(self):
print('%s is sleeping' %(self.Name))
stu1=Student('张三','女',18) # 会触发init方法的执行 定制stu1的私有特征
stu2=Student('李四','男',38)
stu3=Student('王五','男',48)
print(stu1.__dict__)
print(stu2.__dict__)
print(stu3.__dict__)
我们现在创建了三个对象 都有各种独有的特征,单独一个对象都是特征和技能的结合体
那么我们现在三个对象都还应该有的相似的特征与相似的技能的结合体
相似的特征与相似的技能的结合体存在哪里?类里面
# 类有两种属性:数据属性和函数属性
# 1. 类的数据属性是所有对象共享的
# 1.1 类当中定义的变量,就是数据属性。类本身是可以访问到的
print(Student.school)
# 1.2 类当中定义的数据属性应该是所以对象共有的
"""
对象:特征与技能的结合体
类:类是一系列对象相似的特征与相似的技能的结合体
那么在对象里面,也是可以访问到类的数据属性
"""
print(stu1.school)
# 这样不能说明问题,
print(Student.school,id(LuffyStudent.school))
print(stu1.school,id(stu1.school))
print(stu2.school,id(stu2.school))
print(stu3.school,id(stu3.school))
# 值是一样的, id也是一样的 这样证明的是 类的数据属性是所有对象共享的大家值向的是同一片内存地址
# 2.类的函数数据是绑定给对象用的,称为绑定到对象的方法
# 2.1 类本身是可以访问函数属性的
print(Student.learn) # 函数属性,我们打印出来也是一个函数而已 没有特殊的地方我们要使用的话需要几个参数就要传递几个
Student.learn(stu1) # 注意:和最开始定义的学生类 函数属性是有变化的
Student.learn(stu2)
Student.learn(stu3)
# 2.2 理解到上面的代码后我们继续来理解 类中的函数属性是绑定给对象使用的,绑定到不同的对象是不同的绑定方法
# 我们先用大白话理解,你是一个对象,我也是一个对象。你来学习和我去学习 你学习能学到我身上去吗,你学完后只能作用到你的身上
# 再来看代码上的效果,输出来的结果是不一样的
print(stu1.learn)
print(stu2.learn)
print(stu3.learn)
# <bound method Student.learn of <__main__.Student object at 0x103a173c8>>
# bound method 绑定方法 指的是Student.learn这个函数 后面跟的还是内存地址
# 当我们调用的时候
stu1.learn() # 输出的是:张三 is learning
# 那么这个说明了什么?我们是触发到了Student下learn这个功能 不同的是我们使用Student.learn需要传递参数。但是使用对象触发learn的话我们不需要传参数
# 我们没有传 就是自动给我们传了
# 类中的函数属性:是绑定给对象使用的,绑定到不同的对象是不同的绑定方法,对象调用绑定方式时,会把对象本身当作第一个传入,传给self
属性查找
现在我们来想一个问题,对象本身只有独有的属性。相似的特征在类里面,如果我们查找的出现了重名,那么这个时候以谁为准?类里面有个属性是x 对象里也有个属性是x,那么通过对象.x 返回那个
stu1.x='from stu1'
Student.x='from Student class'
print(stu1.__dict__)
print(stu1.x)
# 对象访问属性的时候会先在自己对象里面去找 没有的话去类里去找
6. python中一切皆对象
python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
类型就是数据类型,列表/字典/字符串等等
print(type([1, 2, 3])) # 输出的是<class 'list'>
你再来看看我们自己定义的类
class A:
pass
print(A) # <class '__main__.A'>
类型就是类
l1=[1,2,3] # 等于l=list([1,2,3])
# list是类的话 类加括号就是实例化产生一个对象
# 我们有学过列表的内置方法 append 这个是不是l1这个对象调用自己的绑定方法
l1=[1,2,3] #l=list([1,2,3])
l2=[]
l1.append(4) # l1使用append方法你是不可能加到l2里面去的 这个就是我们说的绑定方法的概念
# l1.append(4) 等于 list.append(l1,4) 但是你不会这样使用 我们使用的是l1.append(4)
# 类的函数数据是绑定给对象用的,称为绑定到对象的方法
7. 继承
继承指的是 类和类之间的关系
小案例
模仿LOL定义两个英雄类
要求:
- 英雄需要有昵称、攻击力、生命值等属性;
- 实例化出两个英雄对象;
- 英雄之间可以互殴,被殴打的一方掉血
我们在程序里面产生对象后,对象怎么交互着完成程序的执行
# 一个人物角色就是一个英雄类 一个是盖伦 一个瑞文
class Garen:
# 1. 你用的盖伦 我用的盖伦 这个是两个对象
# 大家用的盖伦有没有相同的属性?阵营
camp = 'Demacia'
def __init__(self, nickname, life_value, aggressivity): # 2. 盖伦有自己独有特征 别名 生命值 攻击力
self.nickname = nickname
self.life_value = life_value
self.aggressivity = aggressivity
# 每个英雄都有一个普通攻击技能
def attack(self, enemy): # enemy 攻击目标 self自己
enemy.life_value -= self.aggressivity # 攻击目标的生命值减等于我们的攻击力
class Riven:
camp = 'Noxus'
def __init__(self, nickname, life_value, aggressivity):
self.nickname = nickname
self.life_value = life_value
self.aggressivity = aggressivity
def attack(self, enemy):
enemy.life_value -= self.aggressivity
g1=Garen('草丛伦',100,30) # 盖伦对象
r1=Riven('锐雯',80,50) # 瑞文对象
print(r1.life_value)
g1.attack(r1)
print(r1.life_value)
两个类之间有一个代码冗余的问题,怎么解决这个问题。这个就是现在要和说的继承了 。通过继承来解决类与类之间代码重用的问题
什么是继承?
继承指的是类与类之间的关系,继承的功能之一就是用来解决代码重用问题。怎么实现?我们一点点来看
继承本身没有特殊之处。是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: # 定义父类
pass
class ParentClass2: # 定义父类
pass
class SubClass1(ParentClass1): # 单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): # python支持多继承,用逗号分隔开多个继承的类
pass
查看继承
# __bases__ 查看所有继承的父类
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
我们现在是了解 python的继承。我们需要考虑的是python里面怎么找继承关系,我们最终的目标是通过继承来解决代码重用问题。先弄清楚到底怎么在程序里面找出继承关系
我们在说之前说过,怎么得到类的概念。现实生活当中一切皆对象,我们从生活当中的对象总结出来了现实生活当中的类。
将奥巴马和梅西这俩对象,在对象之间总结出来类。奥巴马和梅西把他们抽象成人类,抽象的意思就是抽取比较像的部分
抽取麦兜和猪刚鬣他们之间相识的得到猪类 由对象得到两个类
类和类之间有没有相似的,也有
总结对象之间相似的部分得到类,总结类之间相视的特征得到父类
人 狗的父类都是动物
继承指的是类与类之间的关系,是一种什么“是”什么的关系。多个类之间存在什么是什么的关系 就可以使用继承。
好我们现在在回道之前我们做小案例的那个问题,我们定义了两个英雄类。盖伦和瑞文 ,这个两个他们存在它都是什么的关系?都是英雄,他们都可以继承英雄类
# 他们都是英雄
class Hero:
x=3
def __init__(self,nickname,life_value,aggresivity): # 每个英雄都有特有的特征
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
pass
class Riven(Hero):
pass
通过继承的方式新建类,让新建类继承父类,新建类会‘遗传’父类的所有属性(数据属性和函数属性),实现代码重用
g1=Garen('草丛伦',100,30) # Garen里面没有实现init方法 没有我们实例化的时候我们是用的父类的init方法
r1=Riven('可爱的锐雯雯',80,50)
print(r1.life_value)
g1.attack(r1)
print(r1.life_value)
我们现在是通过继承实现了代码重用
属性查找
我造出一个对象,我要访问对象属性的时候怎么查找?先从本身,本身没有的话去它的类里面去找,类里面没有去它的父类里去找
class Hero:
x=3
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
# x=2
pass
class Riven(Hero):
pass
g1=Garen('盖伦',29,30)
print(g1.nickname,g1.life_value,g1.aggresivity)
# g1.x=1
print(g1.x)
#属性查找小练习
class Foo:
def f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')
self.f1() #b.f1()
class Bar(Foo):
def f1(self):
print('from Bar.f1')
b=Bar()
# print(b.__dict__)
b.f2()
#输出为: from Foo.f2
# from Bar.f1
8. 派生
我们现在在来看我们之前的代码,Garen和Riven类之间还有区别吗?没有。但是实际上这两个类有区别吗?有!一个是德玛西亚的一个是纳克萨斯的
派生出新的东西
class Hero:
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
camp='Demacia' # 盖伦不是一无所有 它有自己的阵营
def attack(self,enemy): # 如果我自己有呢?
print('from Garen Class')
class Riven(Hero):
camp='Noxus'
g=Garen('草丛伦',100,30)
r=Riven('锐雯雯',80,50)
g.attack(r)
子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
9. 继承实现原理
我们说完继承和派生后,我们说到了一个属性查找的问题。针对一个对象要找一个属性,会优先从对象自己这里找。对象自己这里没有的话会去对象所在的类里面去找,如果这个类没有再去父类去找
但是这样的查找顺序,的前提是我所有的类都只继承一个类。也就是说我这个是在单继承的情况下,我的查找顺序就是这样的。但是python是可以多继承的,如果是多继承的话,这样的查找就会很复杂。
如果说我对于一个类,它是继承 a/b/c 三个类 ,现在怎么找?它的父类有三个你先找那个
所以说针对多继承的情况下属性的查找,我们必须要搞明白python继承的实现原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,我们只需要知道c3算法计算出来的列表明白列表里面要按照怎么样的顺序排,我知道这个,我是不是就知道多继承我们要按照怎么样的顺序找属性
python当中这个列表的产生,决定了你属性查找的方式,这个查找的方式分成两种。深度优先和广度优先
本质查找还是按照MRO列表,深度优先和广度优先他们的区别就是排列方式不一样
在python中类是分为两种的,新式类/经典类 这个区分只有python2中有,python3里面没有经典类这么一说都是新式类
# 我们站在python2的调度
# 经典类:没有继承object的类,以及它的子类都称之为经典类
class Foo:
pass
class Bar(Foo):
pass
# 在python2中-》新式类:继承object的类,以及它的子类都称之为新式类
class Foo(object):
pass
class Bar(Foo):
pass
#在python3中-》都是新式类:一个类没有继承object类,默认就继承object
class Foo():
pass
print(Foo.__bases__)
我们现在知道了新式类/经典类的概念。说到新式类/经典类就涉及到他们的区别,我产生的mro列表是不一样的
class A(object):
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
def test(self):
print('from C')
class D(B):
def test(self):
print('from D')
class E(C):
def test(self):
print('from E')
class F(D,E):
# def test(self):
# print('from F')
pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
#新式类继承顺序:F->D->B->E->C->A
10. 在子类中重用调用父类属性
子类里面什么都没有的话,那它里面的东西都是继承父类,我定义了自己新的属性,就以自己为准了。问题是在子类里面派生出新的方法里面我想用父类的代码。我不是想从头到尾的改写。我是想在父类的基础上做一点改动。
# 在子类派生出的新的方法中重用父类的方法,有两种实现方式
# 1. 指名道姓
class Hero:
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
camp='Demacia'
def attack(self,enemy):
# 类在调用自己的函数属性,就是有几个参数我们就要传递几个参数
# 绑定方法才会自动传值,现在是类调函数属性
# self 对象自己
Hero.attack(self,enemy) # 指名道姓
print('from Garen Class')
class Riven(Hero):
camp='Noxus'
g=Garen('草丛伦',100,30)
r=Riven('锐雯',80,50)
print(r.life_value)
g.attack(r)
print(r.life_value)
"""
现在是Garen 现在有没有初始化方法 init方法?没有 他是使用的父类的
也就是说我们实例化的时候传的是Hero下init方法的三个参数
如果说你想给Garen添加独有特征这么办?比如说他的武器装备 我现在再重用父类的行吗?不行,那么是不是就意味着我们需要自己派生出新的来
"""
class Hero:
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
camp='Demacia'
def __init__(self,nickname,life_value,aggresivity,weapon):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
self.weapon=weapon
def attack(self,enemy):
Hero.attack(self,enemy)
print('from Garen Class')
g=Garen('草丛伦',100,30,'金箍棒')
print(g.__dict__)
但是这样写完问题又来了,你确实是派生出自己新的属性来了。但是你又重复写代码了
class Hero:
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
camp='Demacia'
def __init__(self,nickname,life_value,aggresivity,weapon):
# self.nickname=nickname
# self.life_value=life_value
# self.aggresivity=aggresivity
Hero.__init__(self,nickname,life_value,aggresivity) # 指名道姓
self.weapon=weapon
def attack(self,enemy):
Hero.attack(self,enemy)
print('from Garen Class')
g=Garen('草丛伦',100,30,'大宝剑')
print(g.__dict__)
# 方式二:super() (依赖继承)
class Hero:
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
camp='Demacia'
def attack(self,enemy):
# super(Garen,self) 得到一个特殊的对象,可以专门引用父类属性
# super(Garen,self)是一个对象,我们使用attack就是使用绑定方法,不是函数第一个参数我们是不需要传的
super(Garen,self).attack(enemy) #依赖继承
print('from Garen Class')
class Riven(Hero):
camp='Noxus'
g=Garen('草丛伦',100,30)
r=Riven('锐雯雯',80,50)
g.attack(r)
print(r.life_value)
class Hero:
def __init__(self,nickname,life_value,aggresivity):
self.nickname=nickname
self.life_value=life_value
self.aggresivity=aggresivity
def attack(self,enemy):
enemy.life_value-=self.aggresivity
class Garen(Hero):
camp='Demacia'
def __init__(self,nickname,life_value,aggresivity,weapon):
# self.nickname=nickname
# self.life_value=life_value
# self.aggresivity=aggresivity
# super(Garen,self).__init__(nickname,life_value,aggresivity)
super().__init__(nickname,life_value,aggresivity) # 在python3里面的写法
self.weapon=weapon
def attack(self,enemy):
Hero.attack(self,enemy)
print('from Garen Class')
g=Garen('草丛伦',100,30,'大宝剑')
print(g.__dict__)
super为什么是依赖继承,我们说过只要有一个子类它继承了一堆父类python会给我们计算出一个mro列表 super就是在mro列表里面帮我们找属性,所以它是依赖继承的。
class A:
def f1(self):
print('from A')
class B:
def f1(self):
print('from B')
class C(A,B):
pass
print(C.mro())
#[<class '__main__.C'>,
# <class '__main__.A'>,
# <class '__main__.B'>,
# <class 'object'>]
如果说super依赖继承也就是说它会现在A找,再去B找
class A:
def f1(self):
print('from A')
super().f1()
class B:
def f1(self):
print('from B')
class C(A,B):
pass
c=C()
c.f1()
先从对象这里先找f1没
去对象类里面去找 先从c开始找这里就涉及到mro列表了 没有
去a里面去找 有 打印from A
接下来就是super().f1()
super依赖继承 它会延这继承的mro列表往后走 我现在是基于c的继承关系找到这里的 因为我们是由c产生的对象引发的属性查找 所以c的对象是参照c的mro列表
我是先找c,c找完没有找完了 确定没有 去a找
我们现在停在a这个位置 停在a这个位置我们打印了一个a
接下来又涉及到super找f1 先不要管找谁 super就是沿着mro列表往后找 我们现在是他在a的位置
所以说super会去a这个接着往后找 在b里面找打印from B
super不会管你父类 它只会参照一个东西。你找的是哪一个类的mro。到那个位置我就基于那个位置往后走
即使你们之间没有直接的继承关系,没关系。我参照的是mro
你以后使用那种都无所为,你知道各自的特性就好了
11. 组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,
1.继承的方式
通过继承建立了派生类与基类之间的关系,它是一种’是’的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3…
示例:继承与组合
class People:
school='学校'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Teacher(People):
def __init__(self,name,age,sex,level,salary,):
super().__init__(name,age,sex)
self.level=level
self.salary=salary
def teach(self):
print('%s is teaching' %self.name)
class Student(People):
def __init__(self, name, age, sex, class_time,):
super().__init__(name,age,sex)
self.class_time=class_time
def learn(self):
print('%s is learning' % self.name)
class Course:
def __init__(self,course_name,course_price,course_period):
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
def tell_info(self):
print('课程名<%s> 课程价钱<%s> 课程周期<%s>' %(self.course_name,self.course_price,self.course_period))
class Date:
def __init__(self,year,mon,day):
self.year=year
self.mon=mon
self.day=day
def tell_info(self):
print('%s-%s-%s' %(self.year,self.mon,self.day))
teacher1=Teacher('久违',28,'male',10,3000,)
teacher2=Teacher('初雨',28,'male',10,3000,)
python=Course('python',3000,'3mons')
linux=Course('linux',2000,'4mons')
# 老师添加python课程
teacher1.course=python
teacher2.course=python
# 查看老师的课程信息
print(teacher1.course)
print(teacher2.course)
print(teacher1.course.course_name)
print(teacher2.course.course_name)
teacher1.course.tell_info()
# 创建学生对象 添加课程信息
student1=Student('张三',28,'female','08:30:00')
student1.course1=python
student1.course2=linux
student1.course1.tell_info()
student1.course2.tell_info()
# 第二种写法
student1.courses=[]
student1.courses.append(python)
student1.courses.append(linux)
# 添加学生日
student1=Student('张三',28,'female','08:30:00')
d=Date(1988,4,20)
python=Course('python',3000,'3mons')
student1.birh=d
student1.birh.tell_info()
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
12. 抽象类
要大家定义三个类 人狗猪
class People:
# 人有没有走这个方法
def walk(self):
print('people is walking')
class Pig:
# 猪也有走这个方法
def run(self):
print('Pig is running')
class Dog:
def go(self):
print('Pig is going')
定义完这三个类后,我们发现它们都有走这个方法。为什么他们三个都有一个 关系 人狗猪都是动物
只要是动物他们都会走但是他们走的方式不一样
peo1=People()
pig1=Pig()
dog1=Dog()
peo1.walk()
pig1.run()
dog1.go()
我们不看上面的类,你要人走要怎么走。你不知道
你只知道每个类都有走的功能,但是我们调用的方法名不一样。这样就会对使用者造成很大的困扰
我们现在有有一种解决方案,最好把他们三个名字 同一起来
我不管你是什么只要你是动物,你就可以走
peo1.run()
pig1.run()
dog1.run()
但是这个是要你自己定义的 我就不写run 我偏要写walk 你管我?我们最好可以找一个限制
import abc #导入抽象类的模块
class Animal(metaclass=abc.ABCMeta): #只能被继承,不能被实例化
all_type='animal'
@abc.abstractmethod # 定义抽象方法,无需实现功能
def run(self):
'子类必须定义写功能'
pass
@abc.abstractmethod
def eat(self):
pass
# animal=Animal()
class People(Animal):
def run(self):
print('people is running')
def eat(self):
print('people is eating')
class Pig(Animal):
def run(self):
print('people is walking')
def eat(self):
print('people is eating')
class Dog(Animal):
def run(self):
print('people is walking')
def eat(self):
print('people is eating')
peo1=People()
pig1=Pig()
dog1=Dog()
peo1.eat()
pig1.eat()
dog1.eat()
13. 多态与多态性
多态
多态指的是一类事物有多种形态,比如
动物有多种形态:人,狗,猪
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
文件有多种形态:文本文件,可执行文件
import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
@abc.abstractmethod
def click(self):
pass
class Text(File): #文件的形态之一:文本文件
def click(self):
print('open file')
class ExeFile(File): #文件的形态之二:可执行文件
def click(self):
print('execute file')
那么说一类事物有多种形态可以给我们带来什么好处?
多态性
多态性是指在不考虑实例类型的情况下使用实例,怎么理解看下面的代码
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
# 多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
peo1=People()
dog1=Dog()
pig1=Pig()
cat1=Cat()
# 只要你是动物就有talk方法
peo1.talk()
dog1.talk()
pig1.talk()
为什么要用多态性(多态性的好处)
其实大家从上面多态性的例子可以看出,我们并没有增加什么新的知识,也就是说python本身就是支持多态性的,这么做的好处是什么呢?
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
def func(animal):
animal.talk()
func(peo1)
func(pig1)
func(dog1)
func(cat1)
2.增加了程序的可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
class Cat(Animal): #属于动物的另外一种形态:猫
def talk(self):
print('say miao')
def func(animal): #对于使用者来说,自己的代码根本无需改动
animal.talk()
cat1=Cat() #实例出一只猫
func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
say miao
'''
这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
鸭子类型
Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象
也可以创建一个外观和行为像,但与它无任何关系的全新对象,
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
def read(self):
pass
def write(self):
pass
class DiskFile:
def read(self):
pass
def write(self):
pass
disk=Disk()
text=Text()
disk.read()
disk.write()
text.read()
text.write()
例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系
#str,list,tuple都是序列类型
s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))
#我们可以在不考虑三者类型的前提下使用s,l,t
s.__len__()
l.__len__()
t.__len__()
len(s)
len(l)
len(t)
def len(obj):
return obj.__len__()
print(len(l))
print(len(t))
print(len(s))
day15
14. 封装
封装=“隐藏”
封装不是单纯的隐藏
14.1 先看怎么隐藏
class A:
__x = 15 # 双下划线 _A__x
def __init__(self, name):
self.__name = name # self._A__name
def __foo(self): # _A__foo
print('run foo')
这种自动变形的特点
-
在类的外部无法直接使用
-
在类的内部是可以访问的
在定义阶段已经发生了变形
-
子类无法覆盖父类的双下划线开头的属性
# 正常情况看下 class A: def fa(self): print('from A') def test(self): self.fa() # b.fa() class B(A): def fa(self): print('from B') b = B() b.test() # 结果是 from A
# 隐藏后的效果 class A: def __fa(self): # 在·定义阶段变形成_A__fa print('from A') def test(self): self.__fa() # 在·定义阶段变形成b._A__fa class B(A): def __fa(self): # 在·定义阶段变形成_B__fa print('from B') b = B() b.test()
-
变形的过程只在类的定义阶段发生一次
print(B.__dict__) B.__x = 1 print(B.__dict__)
14.2 封装的意义
一:封装的是数据属性 可以明确区分内外
class People:
def __init__(self,name, age):
self.__name = name
self.__age = age
def tell_info(self):
print('<name:%s> <age:%s>' % (self.__name, self.__age))
def set_info(self, name, age):
if not isinstance(name, str): #判断数据类型是不是 str类型
print('名字必须是字符串')
return
if not isinstance(age, int):
print('年龄必须是整数')
return
self.__name = name
self.__age = age
p = People('小明',18)
p.tell_info()
p.set_info(1444,4555)
二:封装的函数属性 隔离复杂度
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('认证')
def __input(self):
print('输入取款金额')
def __take_money(self):
print('取款')
def foo(self):
self.__card()
self.__auth()
self.__input()
self.__take_money()
a = ATM()
a.foo()
15. 特征
# bmi 过轻 18.5 正常 18.5 过重 24-27 肥胖 28-32
# 70 / (1.75*2)
class People():
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height
@property
def bmi(self):
return self.weight / (self.height ** 2)
p1 = People("久违", 75, 1.81)
bmi = p1.bmi
print(bmi)
@property,将一个类的函数定义成特征后,对象再去调用的时候使用 obj.name
问题点
p1 = People("久违", 75, 1.81)
bmi = p1.bmi
print(bmi)
p1.height = 1.82
p1.bmi = 3333
虽然我的访问方式发生了改变
但是我触发的还是 函数属性 如果赋值是会报错的
class People:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height
self.__bmi = None
@property
def bmi(self):
return self.weight / (self.height ** 2)
@bmi.setter
def bmi(self, val):
print('setter', val)
self.__bmi = val
@bmi.deleter
def bmi(self):
print('deleter')
16. 绑定方法与非绑定方法
类中定义的函数分为两大类
-
一. 绑定方法
-
绑定给对象的方法
# 在类内部直接使用def定义的函数 就是绑定给对象的方法 class Foo: def __init__(self, name): self.name = name #绑定给对象去使用 def tell(self): print('名字是%s' % self.name) f = Foo('久违') f1 = Foo('初雨') Foo.tell(f) Foo.tell(f1) print(f.tell) # 绑定给谁,就应该是由谁来调用 谁来调用就会把自己当成第一个参数传入
-
绑定到类的方法 类来调用 把类当成第一个参数自动传入
class Foo: def __init__(self, name): self.name = name def tell(self): print('名字是%s' % self.name) @classmethod def func(cls): print(cls) f = Foo('久违') f1 = Foo('初雨') print(Foo.func) print(f.func)
-
-
二. 非绑定方法
-
不与类和对象绑定 没有自动传值这么一说
@staticmethod def foo(x, y): print(x+y)
-
我们以后在写代码的时候需要考虑情况函数属性里面的逻辑,再使用参数
import settings #在另建一个settings.py,
name = 'xx' age = 18 sex = '男'
import hashlib #哈希模块,把绑定文变为密文
import time #时间的模块
class People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
self.id = self.create_id()
def tell_info(self):
print(self.name, self.age, self.sex)
@classmethod #绑定到类的方法
def from_comf(cls):
obj = cls(
settings.name,
settings.age,
settings.sex
)
return obj
@staticmethod
def create_id():
m = hashlib.md5(str(time.time()).encode('utf-8'))
return m.hexdigest()
day16
17. 反射
反射 通过字符串来映射到一个对象的属性
# 学生选课系统
# 学生 课程 管理员
class Course:
def __init__(self, name, price, period, teacher):
self.name = name
self.price = price
self.period = period
self.teacher = teacher
class Student:
def __init__(self, name):
self.name = name
self.course = []
def show_courses(self):
print('查看可选课程')
def select_course(self):
print('选择课程')
def check_selected_course(self):
print('查看以选课程')
def exit(self):
exit()
class Manger:
operate_list = [
('创建课程', 'create_course'),
('创建学生', 'create_student'),
('查看所有课程', 'show_courses'),
('查看所有的学生', 'show_students'),
('查看所有学生的选课情况', 'show_student_course'),
('退出', 'exit')
]
def __init__(self, name):
self.name = name
def create_course(self):
print('创建课程')
def create_student(self):
print('创建学生')
def show_courses(self):
print('查看所有课程')
def show_students(self):
print('查看所有的学生')
def show_student_course(self):
print('查看所有学生的选课情况')
def exit(self):
exit()
# 学生 登录后就可以选课
# 1. 有学生账号
# 2. 有课程
# 管理员
# 1. 创建学生账号
# 2. 创建课程
# 登录
# 自动识别身份
# 账号 密码存在哪里 以什么形式存储
# 账号|密码|身份
def login():
name = '久违'
pawd = '123'
with open('userinfo', encoding='utf-8')as f:
for line in f:#绑定一些值
usr, pwd, identify = line.strip().split('|')
if usr == name and pawd == pwd:
return {'result': True, 'name': name, 'id': identify}
else:
return {'result': False, 'name': name}
#反射方法:
ret = login()
if ret['result']:#如果'result'为真时
print('登入成功')
if ret['id'] == 'Manger':
obj = Manger(ret['name'])
for id, itme in enumerate(Manger.operate_list,1):#枚举方法
print(id, itme[0])
while True:
func_str = Manger.operate_list[int(input('>>>')) - 1][1]
if hasattr(obj, func_str):#判断obj在不在func_str方法中
getattr(obj, func_str)()
# print(hasattr(obj, 'name')) # 就是一个判断 对象里面是否有 name这个属性
# print(hasattr(obj, 'talk')) # 就是一个判断 对象里面是否有 talk
# print(getattr(obj, 'talkasjkdbhaskdbasjk', '没有没有找不到')) # 拿到属性,,,这个方法,否则返回后边的值--‘没有找不到’
# print(obj.__dict__)
# setattr(obj, 'sex', '男') # 设置属性
# print(obj.__dict__)
delattr(obj, 'age') # 删除属性
print(obj.__dict__) # del obj.age
day17
18. 内置方法
18.1 __del__
当用户使用del删除对象时候会触发
# __del__
f = open('a.txt')
# f.read()
f.close() # 回收操作系统资源
# 1. 首先它是一个赋值操作
# x = 1
# y = 2
# 2. 打开文件 打开文件代表的是我们需要去 操作硬盘
# 应用程序 可以直接操作硬盘吗? 操作系统
# 你产生的对象只是python程序级别没必要使用到__del__
class Open:
def __init__(self, filename):
print('open file....')
self.name = filename
def __del__(self):
print('回收系统资源....')
f = Open('a.txt')
del f
print('程序结束')
# 垃圾回收
18.2 __enter__和__exit__上下文管理协议
抛出出错的地方
上下文管理协议,就是with语法,要一个对象兼容with语法
在这个对象的类里面就需要有__enter__
和__exit__
方法
class Open:
def __init__(self, filepath, mode='r', encoding='utf-8'):
self.filepath = filepath
self.mode = mode
self.encoding = encoding
def __enter__(self):
# print('出现with语句,对象的__enter__会被触发,有返回值 会被as 声明的变量接受')
self.f = open(self.filepath, mode=self.mode, encoding=self.encoding)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
# exc_type 异常类型
# exc_val 异常内容
# exc_tb 异常所在的位置
print('代码第%s行出现错误' % exc_tb.tb_lineno) #输出错误所在的行
self.f.close()
return True # 异常情况
with Open('a.txt', 'w') as f:
print('=====> 执行代码段')
f.write('久违')
print(f.write123213('333'))
# raise AttributeError('出错了') #手动抛出异常
# print(f, f.name)
18.3__str__
声明一个类的对象后,想打印该对象的信息,我们可以通过重写 __str__
方法
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name + ',' + str(self.age)
dog1 = Dog('初雨', 13)
print(dog1)
-
做个小练习
定义一个猫类 Cat:
- 通过
__init__
猫的名字 年龄初始为0 - 定义一个yearpass方法 表示过了一年 在方法中要年龄加1
- 重写
__str__
方法 打印猫的姓名和年龄 - 定义并创建猫对象 然后每过一年就打印一下猫的信息
- 每过几年 给猫添加饰品 3岁添加衣服 5谁添加脚垫 8 岁添加铃铛 10岁剪胡子
# 定义猫类 从猫的本身去看 class Cat: def __init__(self, name): self.name = name self.age = 0 self.outlook = [] def yearPass(self): self.age += 1 def wear(self, something): self.outlook.append(something) def __str__(self): return self.name + ',' + str(self.age) + ',' + str(self.outlook) # 从旁观者的角度去看 jiafei = Cat('加菲') for i in range(20): jiafei.yearPass() if jiafei.age == 3: jiafei.wear('clothes') if jiafei.age == 5: jiafei.wear('shoe') if jiafei.age == 8: jiafei.wear('dingdang') print(jiafei)
18.4 __new__
之前接触过的前后有双下划线的方法:
-
__init__
(初始化对象) -
__str__
(描述对象) -
__del__
(删除销毁对象,拆构方法,析构方法)与__new__
方法相反.触发时机:主动删除对象,对象被系统回收的时候自动触发,当对象同时赋值给另一个对象的时候,只回收当前的这一步操作。
作用:负责回收程序使用过程的信息和变量等
参数:只有一个self参数
-
__new__
(构造方法):构建创造!制作对象,实例化对象的时候触发的,管理和控制对象的生成过程,参数:一个cls接受当前类,其他的参数根据实例化的参数决定
class Dog: def __init__(self): print('init') def __str__(self): return "str" def __del__(self): print('del') def __new__(cls,sex):#在__init__方法之前进行调用使用 print("new") if sex==‘女’: return object.__new__(cls) elss: pass dog1 = Dog() print(dog1) del dog1 print('end') 输出结果 new None end ___new__()
new是创建对象的
调用dog类创建一个实例的时候 1. 先调用__new__ 创建一个对象然后返回对象 2. 传递给__init__方法 初始化对象 3. 放回该对象 obj = __new__(Dog) __init__(obj) return obj
- 通过
18.5 单例
在某个系统里面 一个类的对象只有一个
class Sun:
pass
sun1 = Sun()
sun2 = Sun()
print(id(sun1))
print(id(sun2))
print(id(sun1) == id(sun2)) #False
太阳是唯一的
思路 创建类的时候只有一次
如果你是第一次创建对象 直接去创建
如果不是 返回之前创建的
这个时候你就需要去重写__new__
class Sun:
__instance = None
def __new__(cls):
# 如果这个类没创建对象 就调用父类的__new__去帮我们创建
if cls.__instance == None:
cls.__instance = object.__new__(cls)
return cls.__instance
else:
return cls.__instance
sun1 = Sun()
sun2 = Sun()
print(id(sun1))
print(id(sun2))
print(id(sun1) == id(sun2)) #True
如果给类设置一个熟悉(名字)
你会发现名字会变
# 解决办法
class Sun:
__instance = None
__init_flag = False
def __new__(cls, name):
# 如果这个类没创建对象 就调用父类的__new__去帮我们创建
if cls.__instance == None:
cls.__instance = object.__new__(cls)
return cls.__instance
else:
return cls.__instance
def __init__(self, name):
if Sun.__init_flag == False:
self.name = name
Sun.__init_flag = True
def __str__(self):
return self.name
# Sun('久违', age=18)
sun1 = Sun('张三')
sun2 = Sun('李四')
print(sun1)
print(id(sun1))
print(sun2)
print(id(sun2))
18.6 运算符重载
+ - * /
可以对同一个类的对象进行加减乘除的操作
需要重写 __add__ __sub__ __mul__ __div__
class Point(object):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __str__(self):
return '%s,%s,%s' % (self.x, self.y, self.z)
def get_point(self):
return self.x, self.y, self.z
def __add__(self, other):
pos = other.get_point()
new_x = self.x + pos[0]
new_y = self.y + pos[1]
new_z = self.z + pos[2]
return Point(new_x, new_y, new_z)
def __sub__(self, other):
print('sub')
def __mul__(self, other):
print('mul')
def __ror__(self, other):
print('ror')
p1 = Point(1, 2, 3)
p2 = Point(6, 7, 8)
p3 = p1 + p2
18.7 __next__和__iter
__实现迭代器协议
19. 元类
python一切皆对象
类本身也是一个对象 对象是由一个类实例化来的
class Foo:
pass
f = Foo()
# type函数可以查看类型,也可以用来查看一个对象的类
print(type(Foo))
print(type(f))
我们使用class关键字定义出来的类也是一个对象
元类就是创建类的类 默认是用的class 他的元类是type
两种方式帮助我们创建类
# 方式二 通过type
# 类的三要素 类名 类的父类们
class_name = 'Chinese'
class_bases = (object, )
class_body = """
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is talking' % self.name)
"""
# 我们定义类 类体代码会执行吗?会
# 知识点补充
# 参数1 字符串类型的命令
# 参数2 全局作用域 (字典形式)
# 参数3 局部作用域 (字典形式)
class_dic = {}
exec(class_body, globals(), class_dic)
Chinese1 = type(class_name, class_bases, class_dic)
print(Chinese1)
obj = Chinese1('久违', 18)
print(obj)
print(obj.name)
print(obj.talk)
自定义元类控制类的创建
#__doc__ 文档注释
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):# 类名,类体,类的注释
if not class_name.istitle():#如果类名首字母不是大写的
raise TypeError('类名首字母必须大写') #抛出异常
if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须要有文档说明, 且不能是空')
super().__init__(class_name, class_bases, class_dic)
class Chinese(object, metaclass=Mymeta):
country ='China'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is talking' % self.name)
20. 异常处理
day18
1、集合序列操作,遍历及推导式
# 集合
num = {1,2,3,4,5,6,7,8,9,0}
res = len(num)
print(res)
print(num.__len__()) #打印集合num 的长度
res1 = max(num)
res2 = min(num)
print(res1,res2) # 打印集合num 的最大值和最小值
# 集合的推导式
num1 = {i * 5 for i in num}
print(num1) #所求:所有集合中的数据 *5 所得到的集合
# 带有判断条件的集合推导式
# 要求集合中所有的技术,并且*5组成新的集合
num2 = {i * 5 for i in num if i%2 ==1}
print(num2)
# 多循环的集合推导式
# 要求:集合1和集合2中的每个数据相对应位置进行相加
# set1 = {1,3,5,7,9 }
# set2 = {2,4,6,8,10}
# result = {i + j for i in set1 for j in set2}
# print(result) #这样的情况是不对的,得不到想要的数据
#带有判断条件的多循环推导式
set1 = {'刘备','关羽','张飞','诸葛亮'}
set2 = {'曹操','黄忠','司马懿','赵云'}
result = {i +'--' +j for i in set1 for j in set2 if len(j) ==2 and len(i) ==2}
print(result)
2、21. 面向对象综合项目
题目:开发一套选课系统
需求分析
从“学生选课系统” 这几个字就可以看出来,我们最核心的功能其实只有 选课。
角色
学生、管理员
功能
**登陆 :**管理员和学生都可以登陆,且登陆之后可以自动区分身份
选课 : 学生可以自由的为自己选择课程
创建用户 : 选课系统是面向本校学生的,因此所有的用户都应该由管理员完成
查看选课情况 :每个学生可以查看自己的选课情况,而管理员应该可以查看所有学生的信息
工作流程
**登陆 :**用户输入用户名和密码
**判断身份 :**在登陆成果的时候应该可以直接判断出用户的身份 是学生还是管理员
**学生用户 :**对于学生用户来说,登陆之后有三个功能
- 查看所有课程
- 选择课程
- 查看所选课程
- 退出程序
**管理员用户:管理员用户除了可以做一些查看功能之外,还有很多创建工作**
- 创建课程
- 创建学生学生账号
- 查看所有课程
- 查看所有学生
- 查看所有学生的选课情况
- 退出程序
程序设计
对于复杂的功能,我们首先就应该想到面向对象编程。而要想将面向对象的程序开发好,就应该做好类和对象的分析工作。
# 选课系统简单的划分其实只有两个角色:管理员和学生。
# 仔细思考,你会发现有很多想不通的地方,比如学生选课,课从哪里来?
# 这样一想就会发现,其实课程应该可以由管理员创造出来,那么课程又会有很多属性:价格、周期、课程名、授课老师等等
# 那么课程也应该是一个类。
综上,本程序最基础的分析已经完毕。接下来我们要把所有的类以及其中的属性、方法设计出来
课程
属性:课程名、价格、周期、老师
课程并没有什么动作,所以我们只设计属性不设计方法。其实这里还可以设计很多属性,比如课程的开始时间、结束时间、教室等等,只要你需要,这些都可以记录下来,但是这里我们为了简化代码,就先不设计这些了。
学生
属性:姓名、所选课程
方法:查看可选课程、选择课程、查看所选课程、退出程序
管理员
属性:姓名
方法:创建课程、创建学生学生账号、查看所有课程、查看所有学生、查看所有学生的选课情况、退出程序
import sys
import pickle
import os
class Course:
def __init__(self, name, price, period, teacher):
self.name = name
self.price = price
self.period = period
self.teacher = teacher
class Person:
def show_courses(self):
with open('course_info', 'rb')as f:
count = 0
while True:
try:
count += 1
courser_obj = pickle.load(f)
print(count, courser_obj.name, courser_obj.price, courser_obj.period, courser_obj.teacher)
except EOFError:
break
class Student(Person):
operate_lst = [
('查看可选课程', 'show_courses'),
('选择课程', 'select_course'),
('查看已选课程', 'check_selected_course'),
('退出', 'exit')
]
def __init__(self, name):
self.name = name
self.courses = []
def select_course(self):
self.show_courses()
num = int(input('>>>'))
count = 1
with open('course_info', 'rb')as f:
while True:
try:
course_obj = pickle.load(f)
if count == num:
self.courses.append(course_obj)
print('你选择了%s课程' % course_obj.name)
break
count += 1
except EOFError:
print('没有你选择的课程')
break
def check_selected_course(self):
for course in self.courses:
print(course.name, course.teacher)
def exit(self):
with open('student_info', 'rb')as f1, open('student_info_bak', 'wb')as f2:
while True:
try:
student_obj = pickle.load(f1)
if student_obj.name == self.name:
pickle.dump(self, f2)
else:
pickle.dump(student_obj, f2)
except EOFError:
break
os.remove('student_info')
os.rename('student_info_bak', 'student_info')
exit()
@staticmethod
def init(name):
with open('student_info', 'rb')as f:
while True:
try:
stu_obj = pickle.load(f)
if stu_obj.name == name:
return stu_obj
except EOFError:
print('没有这个学生')
break
class Manager(Person):
operate_lst = [
('创建课程', 'create_course'),
('创建学生', 'create_student'),
('查看所有课程', 'show_courses'),
('查看所有的学生', 'show_students'),
('查看所有学生的选课情况', 'show_student_course'),
('退出', 'exit')
]
def __init__(self, name):
self.name = name
def create_course(self):
# name, price, period, teacher
name = input('请输入课程名字')
price = input('请输入课程价格')
period = input('请输入课程周期')
teacher = input('请输入课程老师')
course_obj = Course(name, price, period, teacher)
with open('course_info', 'ab')as f:
pickle.dump(course_obj, f) #初始化object
print('%s课程创建成功' % course_obj.name)
def create_student(self):
stu_name = input('学生名字:')
stu_pwd = input('学生密码:')
stu_auth = '%s|%s|Student\n' % (stu_name, stu_pwd)
stu_obj = Student(stu_name)
with open('userinfo', 'a', encoding='utf-8')as f:
f.write(stu_auth)
with open('student_info', 'ab')as f:
pickle.dump(stu_obj, f)
print('%s学生创建成功' % stu_obj.name)
def show_courses(self):
with open('course_info', 'rb')as f:
count = 0
while True:
try:
count += 1
courser_obj = pickle.load(f)
print(count, courser_obj.name, courser_obj.price, courser_obj.period, courser_obj.teacher)
except EOFError:
break
def show_students(self):
with open('student_info', 'rb')as f:
count = 0
while True:
try:
count += 1
student_obj = pickle.load(f)
print(count, student_obj.name)
except EOFError:
break
def show_student_course(self):
with open('student_info', 'rb')as f:
while True:
try:
student_obj = pickle.load(f)
course_name = [course.name for course in student_obj.courses]
print(student_obj.name, '所选课程 %s' % '|'.join(course_name))
except EOFError:
break
def exit(self):
exit()
@classmethod
def init(cls, name):
return cls(name)
def login():
name = input('username : ')
pawd = input('password : ')
with open('userinfo', encoding='utf-8') as f:
for line in f:
usr, pwd, identify = line.strip().split('|')
if usr == name and pawd == pwd:
return {'result': True, 'name': name, 'id': identify}
else:
return {'result': False, 'name': name}
ret = login()
if ret['result']:
print('登入成功')
# 当文件中有这个类名
if hasattr(sys.modules[__name__], ret['id']):
cls = getattr(sys.modules[__name__], ret['id'])
obj = cls.init(ret['name']) # 实例化
while True:
for id, item in enumerate(cls.operate_lst, 1):
print(id, item[0])
func_str = cls.operate_lst[int(input('>>>')) - 1][1]
if hasattr(obj, func_str):
getattr(obj, func_str)()
else:
print('登入失败')

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)