小狸笔记 - 面向对象编程 Python 速览笔记
参考教程
(强推)Python面向对象编程五步曲-从零到就业【上】_哔哩哔哩_bilibili
(强推)Python面向对象编程五步曲-从零到就业【中】_哔哩哔哩_bilibili
(强推)Python面向对象编程五步曲-从零到就业【下】_哔哩哔哩_bilibili
学前阶段
电脑基础知识
机器语言:直接由 01 组成
汇编语言:由一些助记符代替 01
高级语言:最容易阅读
编译型:整个重新翻译,内容不变不用重翻译
解释性:每次都要一字一句地翻译
Python 安装
常用操作系统
用户通过壳来操作内壳,从而控制硬件
壳(shell)分为有操作界面和无操作界面
Linux:
1、apt 工具安装
如 mkdir 创建文件夹
Apt-get 从某个地方找到软件下载并安装
Python-version 找版本
Sudo apt-get install python 3.6
Sudo 超级管理员权限
Which python 3.6 查看安装位置
Python 使用的默认解释器,进入解释器交互模式
Exit/ 退出模式
Python 版本进入对应的版本 python
2、编译 Python 源码安装
Mac os:直接下载包双击
Windows:进入官网-downloads-安装
用 python 要进入 python 的安装目录
命令提示符会先在当前的目录找,找不到去环境变量找
所以添加环境变量:
系统变量中 Path 中添加 Python 路径
可以新建 PythonRoot 变量,指向 Python 安装目录
再将 PythonRoot 变量添加到 Path 中的值中
- X 可以直接安装时添加环境
Scripts 中包含 pip 等工具
编程方式
1、命令提示符中写 python 进入,exit()退出
2、编写代码文件 test. Py
运行 python 文件. Py
3、IDLE 官方的编写器
4、Pycharm
Pycharm 软件使用
from css import test
从 css 文件夹中导入 test 文件
重构可以同步修改所有文件里的对应文件链接
删除可以安全删除,防止影响
Ctrl+/
注释代码
Ctrl+alt+L
自动格式化代码
Ctrl+鼠标左键
查看代码来源
打断点
打断点的那一行不会运行
Python 运行机制
会先编译成一个中间结果的字节码,再解释执行
.pyc 就是编译好的字节码
.py 发生修改就要重新编译
Import 经常作为工具,所以 import 才会持久化. Pyc
基础阶段
注释
# 注释
中文乱码问题
# encording=utf-8
python2解决中文乱码
变量
a = 1
a,b = 1,2
a = b = 1
命名规范:
只能字母数字下划线
驼峰标识,首字母小写
不可用关键字
关键字列表:
Import keyword
Print (keyword. Kwlist)
变量区分大小写
变量名使用前要赋值
数据类型
数据类型是值拥有的,不是变量有的
查看数据类型
type(a)
类型转换
int(a)
str(b)
静态类型:int a = 10
a 永远是 int 类型
动态类型:后面出现 char a
a 变为 char 类型
Python 是动态类型
Python 不会自动转换类型(强类型)
运算符
算数运算符
+ 数值相加、列表重载、字符串拼接
- 数值相减
* 数值相乘
** 求幂运算
/ 数值相除
// 整除运算(取整)
% 求余运算
= 赋值符号
复合运算符
+= -= *= /= %= **= //=
比较运算符
> < != >= <= == is
比较运算符返回 bool
Is 是用来比对唯一标识(id)
5 < num < 20 链式比较
逻辑运算符
not and or
Bool 非 0 非空当作真
存在短路运算符
Print(1 and 3) 得到 3
Print(1 or 3) 得到 1
Print(0 or flase) 得到 false
输入
Python 2
content = raw_input("请输入内容")
所有输入内容全是字符串
content = input("请输入内容")
所有输入内容当作代码:
abd当作变量,1+1当作2
eval(raw_input("aaa")) = input
Python 3
raw_input -> input("aaa")
input -> eval(input("aaa"))
输出
Python 3
print ("aaa")
#格式化输出
print ("name:%s, age:%c"%(name, age))
print ("name:{0}, age:{1}". format (name, age))
print (f'name:{name}, age:{age}')
#输出到文件
f = open ("test","w")
print ("xxxxx", file = f)
import sys
f = open ("test","w")
print ("xxxxx", file = sys. stdout)
#不自动换行
print ("abc", end="")
#使用分隔符分割
print ("1","2","3", sep="&")
#停顿5秒
from time import sleep
sleep (5)
#立即打印
#字符串会先到缓冲区中 ,如果有换行就立即打印,而 sleep 会阻止立即打印,而 flush 能让字符串无论是否有回车都立即打印
print ("aaa", end="", flush=True)
格式符补充
%[(name)][flags][width][. preception]typecode
#[]可省略
print ("name%d, age%d" % (name, age))
#name
print ("name%(ms) d, age%(es) d" % ({"es": age,"ms": name}))
#flags
print ("%10 d" % age)
# %-d 表示左对齐,默认右对齐
# % d 左侧空格,与负数对其,只能一个
# %0 d 零来补充,如%02 d
#width表示占用的宽度
#. perception
print ("%. 2 f")
#小数点后两位
- G/g 自动转换
B 二进制
O 八进制
X 十六进制
流程控制
If
If age >= 18:
Print ("good")
通过缩进实现代码块
Bool 非 0 非空即为真
If age >= 18:
Print ("good")
Else:
Print ("not")
If a > 0:
...
Else:
If b > 0;
...
Else:
If c > 0:
...
Else:
...
If a > 0:
...
Elif b > 0;
...
Elif c > 0:
...
Else:
...
If not (a > 0):
Exit ()
Elseif -> elif
无 switch…. Case
While
While a > 0:
Print ("aaa")
While a > 0:
Print ("aaa")
Else:
Print ("finish")
没有 do…while…
For
Notice = "abcd"
For i in notice:
Print (i)
Notice = ["aaa","bbb","ccc"]
Notice = range (1,101)
通常用于遍历集合
Notice 取出一个元素放给 i
I 可用人任何新的变量名
Range (1,101) 生成 1-100 列表
For i in notice:
Print (i)
Else:
Print ("finish")
不满足(循环结束后运行 else)
For i in notice:
Print (i)
Break
Else:
Print ("finish")
循环中途退出就不执行 else
Result = ""
For i in notice:
Result = c + result
Print (result)
字符串倒序输出
For i in range (1,6):
Print (i)
循环 5 次:i = 1,2,3,4,5
Break
结束所有循环
Continue
结束本次循环,进入下次循环
Exit
Exit ()
退出程序
Pass
空语句,保持代码完整性
If a > 0:
Pass
Else:
Pass
While a > 0:
Pass
数据类型
数值
Int 和 float 运算,int 变为 float
函数
内建函数
Abs (a) 绝对值
Max (a, b, c) 最大值
Min (a, b, c) 最小值
Round (a) 四舍五入
Round (a[, 2]) 保留 2 为小数
Pow (a, 2) a 的 2 次方
Math 模块函数
Import math
Math. Ceil (a) 向上(大)取整
Math. Floor (a) 向下(小)取整
Math. Sqrt (a) 开平方
Math. Log (a, b) b 的多少次方等于 a
随机函数
Import random
Random. Random () 生成[0,1)之间的随机数
Seq = [1,2,3,4]
Random. Choice (seq) 随机选择一个数
Random. Uniform (x, y) 生成[x, y]之间的随机小数
Random. Randint (x, y) 生成[x, y]之间的随机整数
Random. Randrange (x, y, z) [x, y)之间步长为 z 的随机数,y 默认 None,z 默认 1
三角函数
Import math
Math. Pi = 180 角度/180*pi=弧度
Math. Sin (a)
Math. Cos (a)
Math. Tan (a)
Math. Asin (a) 反正弦
Math. Acos (a) 反余弦
Math. Atan (a) 反正切
Math. Radians (a) 角度 -> 弧度
Math. Degrees (a) 弧度 -> 角度
Bool
True -> 1
False -> 0
字符串
基本方式
'aaa'
"aaa"
'''aaa'''
"""aaa"""
\t (Tab) \' \" \n
Name = "a"\
"b"
末尾加上\ 表示续行
Name = ("a"
"b")
用括号也可以续上
'''aaa
Bbb'''
"""aaa
Bbb"""
三个引号可换行使用
R'a'
R"a"
R'''a'''
R"""a"""
原始字符串,里面是什么就输出什么
Name = '我是 “hello” '
单引号和双引号混合使用可以避免转义
拼接
'str 1' + 'str 2'
'str 1' 'str 2'
可用 + 连接或在同一行放在一块
"我是%s,%d"%("123", 123)
"abc"*3
字符串多倍输出
切片
Name = "H e l l o"
0 1 2 3 4
-5 -4 -3 -2 -1
Name[2] l
Name[-2] l
name[1:3] [1,3) el
Name[起始: 终止: 步长]
Name[0: len (name): 1] len 表示全部长度 (4)
Name[::] 取默认值,全部顺序输出
Name[::-1] 倒序输出
name[0:4:1]
name[4:0:-1]
Name[-1:-4:-1]
使用方式:
Find ("abc","a") 内建函数
"abc". Find ("a") 对象方法
函数
Len (name) 字符个数 (转义符算一个)
👇num = name. Xxx (‘a’)
查找
Find (sub, start=0. End=len (str))
查找索引 (没找到-1)
Rfind (sub, start=0. End=len (str))
查找索引 (从右往左)
Index (sub, start=0, end=len (str))
查找索引 (没找到报错)
Rindex (sub, start=0, end=len (str))
查找索引 (从右往左, 没找到报错)
Count (sub, start=0, end=len (str))
数个数
转换
Replace (old, new[, count])
替换 (count 可选个数, 结果为返回值)
Capitalize ()
字符串首字符大写
Title ()
每个单词首字符大写 (不是字母就分割)
Lower ()
每个字符都小写
Upper ()
每个字符都大写
扩充
Ljust (6,"x")
用 x 填充到 6 个长度, 原字符串靠左
Rjust (6,"x")
用 x 填充到 6 个长度, 原字符串靠右
Center (6,"x")
用 x 填充到 6 个长度, 原字符串靠中 (偏左)
缩减
Lstrip (chars)
从左移除字符集里包含的字符,默认空白
Lstrip (chars)
从右移除字符集里包含的字符,默认空白
空白:\n \t space
分割
Split (sep, maxsplit)
以 seq 识别为分隔符,最大分割 max 次
Maxsplit 省略则全分割
Partition (seq)
从左以 seq 识别为分隔符
返回元组 ('左侧','分隔符','右侧')
如果没有则全部在左侧
Rpartition (seq)
从右以 seq 识别为分隔符
Splitlines (keepends)
按照换行符 (\r \n) 拆分保存到列表
Keepends = True 保留换行符 (默认 x)
Join (iterable)
将可迭代 iterable 用对象连起来
'-'. Iterable (['a','b','c'])
-> 'a-b-c'
判断
Isalpha ()
是否全为字母
Isdigit ()
是否全为数字
Isalnum ()
是否全为数字或字母
Isspace ()
是否全为空白符
包括空格\缩进\换行\回车
Sapce\tab\n \r
Startswith ()
是否以某个前缀开头
(prefix, start=0, end=len (str))
需要判断的字符串, 起始位置, 结束位置
Endswith ()
是否以某个尾缀结尾
"aaa" in "aaabbbccc"
"ddd" not in "aaabbbccc"
列表
(有序、可变)
name = [“a”,“b”,“c”]
name = []
name = ['a','b', True,['c']]
列表生成式
name = range (99)
生成 0-99
name = range (1,99,1)
生成 1-99,步长默认为 1
列表推导式(从一个到另一个)
a = [1,2,3,4,5]
b = []
for i in a
b.append (i ** 2)
b = [i**2 for i in a]
1,4,9,16,25
b = [1 for i in a]
1,1,1,1,1
b = [i for i in a for j in a]
1,2,3,4,5,... (*5)
b = [i for i in a if i%2==0]
2,4
常用操作
增加
list. xxx ()
.append (object)
末尾追加一个 object
.insert (index, object)
在 index 前面插入 object
.extend (iterable)
列表中添加另一个可迭代的序列
list*2
['1','2']
->['1','2','1','2']
list 1+list 2
将 list 2 追加到 list 1 后
(list 2 只能是列表)
. append ()可为字符串等可拆的
删除
del list[2]
删除索引为 2 的元素
del list
删除整个列表
list. pop (2)
删除索引为 2 的元素, 返回索引值
空白默认移除最后一个元素 (-1)
list. remove (object)
从左往右删除内容为 2 的元素
nums = [1,2,2,2,3,4,2]
for i in nums
if i == 2:
nums. remove (2)
-> [1,3,4,2]
每次 remove 之后都会重排这个列表,所以通过索引会漏掉,不能用
修改
name[2] = 3
索引为 2 的元素修改为 3
查询
num = range (10)
num[2]
num[-1]
num. index
(value, start=None, stop=None)
获取元素值为 value 的索引
#
遍历显示所有元素的索引值:
错误:
xs = ['a','a','b']
for i in xs:
print (xs. index (v))
->0,0,2
每次都从头开始遍历,出错
正确 1:
xs = ['a','a','b']
ind = 0
for i in xs:
print (xs. index (v, ind))
ind+=1
->0,1,2
每次都从 ind+1 开始,正确
正确 2:
造一个索引列表
ind = [0,1,2]
ind = range (3)
ind = range (len (xs))
num. count (value)
查找元素为 value 的个数
num[start:stop: step]
[start, stop)
num[::-1]
通过枚举对象遍历:
['a','b','c','d']
->[(1,'a'), (2,'b'), (3,'c'), (4,'d')]
newList (enumerate (oldList))
#
for tupleVal in enumerate (newList):
[(1,'a')]->(1,'a')
转换为元组
print (tupleVal[0])
print (tupleVal[1])
把每个元组中的第 1、2 个元素取出
或:
idx, val = tupleVal
->
for idx, val in enumerate (newList)
for idx, val in enumerate (newList, 3)
索引值从 3 开始
可迭代对象
按照某种顺序逐个访问集合中的每一项
1、能用 for i in ...
2、import collection
isinstance (x, collection. Iterable)
x 是否有 Iterable 特性
迭代器
可以记录遍历位置的对象
1、能用 next ()
2、import collection
isinstance (x, collection. Iterator)
3、迭代器也是可迭代对象
也可用于 for i in ...
迭代器特点:
只有迭代到某个元素才处理
可不存在可被销毁
用于遍历大型或无限集合
提供统一的访问集合的接口
iter (Iterable)
访问简单
next ()
l = [1,2,3,4]
it = iter (l) #创建迭代器it
print (next (it)) #1
print (next (it)) #2
print (next (it)) #3
for v in it:
print (v) #1 2 3 4 5
使用完就不能再重复用,只能重新建
判定
'a' in "abc"
'a' not in "bcd"
#
1 in [1,2,3,4,5]
1 not in [2,3,4,5]
比较
python 2
cmp (a, b)
a>b 1
a=b 0
a<b -1
python 3
"abc" > "aaa"
"abc" == "abc"
#
[1,2,3] == [1,2,3]
[2,4,3] > [2,2,4] #比较第一个不同的
排序
s=[('b', 1), ('a', 2)]
sorted
(itearable, key=None, reverse=False)
可迭代对象, 关键字, 降序=False (默认升)
返回排序好的列表
元组:默认按照元组第一个元素排
get=sorted (s)
get=sorted (s, reverse=True)
按照第二个元素排:
def getKey (x):
return x[1]
get=sorted (s, key=getKey)
不改变原本
对任何可迭代对象适用
list. sort (key=None, reverse=False)
改变原本列表
只能对列表操作
def getKey (x):
return x[1]
s.sorted (key=getKey)
乱序
import random
random. shuffle (s)
直接改变本身
反转
s.reverse ()
改变列表本身
news = [::-1]
不改变本身
元组
有序不可变集合
t = (1) #会被识别为int
t = (1,)
t = (1,2,3)
t = ('a',[1,2], 3)
t = tuple ([1,2,'a']) #列表转换为元组
增加、删除、修改(x)
获取
t[3]
t[-1]
t[1:4] #[1,4)
t[1:4:-1]
t.count (2)
计算 2 的个数
t.index (2)
获取对应的索引,不在会报错
len (t)
长度
max (t)
min (t)
判定
1 in (1,2,3)
1 not in (2,3,4)
比较
cmp ((3,3), (2,3)) # >
cmp (tuple, list) # >
不同类型时会比对类型的英文
> = <
拼接
(1,2,3)*3
(1,2,3)+(4,5)
不同类型不能相加连接
拆包
a, b = (1,2) #a =1, b=2
a, b = (b, a) #交换两个值
字典
无序的键值对的集合
{key: value}
d = {'name': 'xs','age': 12}
print (d['name'])
print (d['age'])
d = dict. fromkeys (seq, value)
将有序的 seq 转换为 key, 分别赋值 value
d = {1:2,3:4}. fromkeys
会将原有的字典覆盖掉 (无意义)
key 不能重复(相同时后值覆盖前值)
key 必须是不可变类型
num = [1,2,3]
id (num)
num. append (4)
id (num)
# 两个 id 相同,因为 list 可变
a=10
id (a)
a=20
id (a)
# 两个 id 不同,因为 a 不是可变的
通过 key 唯一表示对应的索引值
增加
d = {'name': 'sz'}
d['age': 19]
删除
d = {'name': 'sz'}
del d['name']
d.pop ('name'[, default])
弹出键值对
有则返回对应的值
没有则返回 default
无设置 default 则报错
d.popitem ()
< python 3.6
将目前的字典按照 key 进行排序
a 最小,再删除最小的键值对
> python 3.6
删除最后一个 (最后添加的)键值对
返回对应的键值对
d.clear ()
清空字典
del d
删除字典
修改
d['name']='sz'
d.update ('age'=20,'sex'='boy')
有则修改,无则添加
查询
print (d['age'])
无则报错
d.get ('age'[, 666])
无则返回 666,或 None
d.setdefault ('age'[, 666])
无则设定该值为 666 并返回 666
d.keys ()
获取所有的键
d.values ()
获取所有的值
d.items ()
获取所有的键值对
a = d.xxx ()
python 2 中直接获取列表
a 不随 d 改变
python 3 中获取的是 view objects
a 随 d 改变
用 list (d.xxx)转换为列表
for i in d.keys ():
print (d[i])
for k, v in d.items ():
print (k, v)
计算
len (d)
计算键值对的个数
判定
'name' in d
'age' not in d
判定 key
集合
不可随机访问的,不可重复的元素集合
可变集合
s = {1,2,3}
s = set ("abc")
s = set ({'name': 'sz','age': 19})
# 只会用 key 生成集合
s = set (x for x in range (0,10))
s = {x for x in range (0,10)}
# 推导式
不可变集合
不可用{}
s = frozenset ("abc")
s = frozenset ({'name': 'sz'})
# 只会用 key 生成集合
s = frozenset (x for x in range (0,10))
# 推导式
s = {} 会被识别为字典
其中元素为可变时会提示不是可哈希
元素相同时会合并为一个 (可用于列表去重)
单一集合操作
增加:
s.add (4)
#
删除:
s.remove (3) #改变本身 ,返回 error
s.discard (3) #返回None
s.pop () #随机删除 ,返回错误
s.clear () #清空
del s #删除
#
查询:
1、for 集合
for i in s:
print (i)
2、迭代器
its = iter (s)
print (next (its))
3、for 迭代器
its = iter (s)
for v in its:
print (v)
集合间操作
交集:
intersection (Iterable)
print (s 1. intersection (s 2))
以前面的 (s 1)类型为准, s 1 为可变则可变
&
print (s 1 & s 2)
以前面为准
intersection_update (Iterable)
s 1. intersection_update (s 2)
计算完赋值给前面的
前面的 (s 1)必须为可变的
intersection (“abc”)
匹配字符
匹配列表、字符串的字符、字典的键
会先把接收的对象转化为可哈希的集合
所以该对象必须为可哈希的
intersection ([1,2,3]) #√
intersection ([1,2,[3,4]]) #x
并集:
union ()
s 1. union (s 2)
|
s 1 | s 2
是否可变以左侧为主
update ()
s 1. update (s 2)
s 1 和 s 2 合并后给 s 1
差集:
difference ()
s 1 = {1,2,3}
s 2 = {3,4,5}
s 1. difference (s 2)
# s 1 - s 2 = {1,2}
-
s 1 - s 2
difference_update ()
s 1, difference_update (s 2)
结果给 s 1
判定:
s 1. isdisjoint (s 2) # 不相交
s 1. issupperset (s 2) # s 1 包含 s 2
s 1. issubset (s 2) # s 1 包含于 s 2
时间日历
时间戳:从 0 时区的 1970 年 1 月 1 日 8 时 0 分 0 秒到只当日期时间的秒数(浮点数)
time
获取时间戳
import time
time. time ()
获取时间元组
time. localtime ()
-> time. struct_time (
tm_year=2014,
tm_mon=6,
tm_mday=30,
... (时间元组)
)
time. localtime (time. time)
将时间戳转换为时间元组
获取格式化时间
time. ctime (time. time ())
-> Wed Sep 6 07:47:25 2017
将秒转换为格式化时间
time. asctime (time. localtime ())
将时间元组转换为格式化时间
格式化日期字符串 <–> 时间戳
时间元组->格式化日期
time. strftime (格式字符串, 时间元组)
time. strftime (
“%Y-%m-%d %H:%M:%S”, time. localtime ())
-> 2011-11-11 11:11:11
#
常用格式符
%y 两位数的年份表示 ( 00-99 )
%Y 四位数的年份表示 (000-9999 )
%m 月份 ( 01-12 )
%d 月内中的一天 ( 0-31)
%H 24 小时制小时数 ( 0-23 )
%I 12 小时制小时数 ( 01-12 )
%M 分钟数 ( 00=59 )
%S 秒 ( 00-59 )
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天 (001-366)
%p 本地 AM 或P.M 的等价符
%U 一年中的星期数 (00-53)星期天为星期的开始
%w 星期 (0-6),星期天为星期的开始 ...
格式化日期->时间元组
time. strptime (日期字符串, 格式符字符串)
time. strptime ("11-11-11 11:11:11",
“%y-%m-%d %H:%M:%S”)
时间元组->时间戳
time. mktime (时间元组)
获取当前 CPU 时间
统计代码执行耗时
start = time. clock ()
for i in range (0,10000)
print (i)
end = time. clock
print (end-start)
休眠(暂停)
time. sleep (1) #休眠一秒
calendar
import calendar
print (calendar. month (2017,6)) a
datetime
要用里面具体的某个类
import datetime
获取日期
print (datetime. datetime. now ())
print (datetime. datetime. today ())
-> 2017-11-11 13:39:59.534278
单独获取当前日期
t = datetime. datetime. now ()
t.year
t.month
t.day
t.hour
t.minute
t.second
计算 n 天之后的日期
t = datetime. datetime. now ()
t 1 = t + datetime. timedelta (days=7)
计算时间差
first = datetime. datetime (
2017,11,11,12,30,30)
second = datetime. datetime (
2017,11,13,12,30,30)
delta = second - first
print (delta. total_seconds ())
函数
用法
def pFunc ():
print (1)
print (2)
print (3)
...
#
pFunc ()
传参
传一个参数
def test (i)
print (i*2)
#
test (3)
传多个参数
def test (i, j) -> i, j 形参
print (i*j)
test (2,3) -> 2,3 实参
test (j=3, i=2) -> 关键字操作
传元组 (多个元素的和)
def test (t)
result = 0
for v in t:
print (v)
result += v
test ((1,2,3))
test ((1,2,3,4,5))
不定参数
*转换为元组
def test (*t)
result = 0
for v in t:
print (v)
result += v
test (1,2,3)
test (1,2,3,4,5)
#
**转换为字典
def test (**t)
print (t)
test ('name'='sz','age'=18)
装包拆包
装包:把传递的参数,包装成一个集合
拆包:把集合参数,再次分解成独立的个体
#
元组: *args = 1,2,3,4
-> args = (1,2,3,4)
加了 * 表示拆包后
#
装包:
def test (*args): # args 表示多个参数的意思
print (args) -> (1,2,3,4)
拆包:
print (*args) -> 1,2,3,4
字典: **kwargs = "name": "sz","age":18
kwargs = {"name": "sz","age": 18}
缺省参数
def test (a=1, b=0):
print (a, b)
#
test ()
test (1,2)
值传递和引用传递
值传递:
def change (num):
print (num)
#
b = 10
->创建一块内存空间
把 10 放入这块空间 (000110)
b 等于这块空间的地址 (b=000110)
change (b)
->函数获取到 b 对应的值 10
新开辟一个空间放入值 10 (000222)
num 等于这块空间的地址 (num=000222)
对 num 的操作都不影响 b 的值
地址传递:
向 num 传递 b 的地址,使得 num 也指向原来的地址 (000110)
Python 中只有引用 (地址)传递
(根据数据类型是否可变判断)
Python 中当不可变的类型 (数字 10)发生改变之后 (变为 666), 会重新开辟一个新的空间赋值成 666 并把 num 指向新的地址
#
def change (num):
num = 666
b = 10 # b (000110): 10
change (b) # num=b (000110): 10
# 10!=666 且不可变开辟新空间
# num (000333): 666
若传递的为可变的, 则对应变化
def change (num):
num = [4,5,6]
b = [1,2,3] # b (000110): [1,2,3]
change (b) # num=b (000110): [1,2,3]
# (000110):[1,2,3] -> [4,5,6]
# b=num (000110): [4,5,6]
返回
def summ (a, b):
return = a + b
#
summ (1,2)
return 之后就不再执行
只返回一次
若想返回多个数值可包装成数组、列表...
return (a+b, a*b)
c, d = summ (1,2)
使用描述
def test ():
"""帮助文档"""
命令提示符中: help (test)
偏函数
写一个参数比较多的函数时,有些参数大部分情况都是同一个值,则可创建偏函数指定要使用的函数的某个参数
def test 1 (a, b, c, d=1):
print (a+b+c+d)
def test 2 (a, b, c, d=2):
test 1 (a, b, c, d)
调用 test 2 时 d=2, 否则 d=1
import functools
newFunc = functools. partial (test 1, c=5)
functools. partial (函数, 偏参数=偏爱值)
newFunc (1,2)
-> 相当于写了新函数 test 1 (a, b, c=5, d=1)
int ("10010", base=2) #2进制转换
#
import functools
int 2 = functools. partial (int, base=2)
之后调用 int 2 即可默认转换为 2 进制
高阶函数
def test (a, b):
a, b 是形参,也是变量
传递函数,就是指给变量赋值
函数本身也可以作为数据传递给另外一个变量
函数 test 的参数 a 接收的是另外一个函数
则函数 test 是高阶函数
例如:
qwq = ([{"name": 'sz','age': 19},
{"name": 'ac','age': 13},
{"name": 'tr','age': 15}])
#
result = sorted (qwq)
报错,无法字典与字典比较,不知道哪个好
#
def getKey (x):
return x['age']
分别传入字典,取出对应字典 age 的值
#
result = sorted (qwq, key=getKey)
这个就是高级函数
def caculate (num 1, num 2, caculateFunc):
print (caculateFunc (num 1, num 2))
def jiafa (a, b):
return a+b
def jianfa (a, b):
return a-b
#
calculate (6,4, jianfa)
calculate (6,4, jiafa)
返回函数
是指个函数内部,它返回的数据是另外一个函数,把这样的操作称为“返回函数”
def getFunc (flag)
def sum (a, b, c):
return a+b+c
def jian (a, b, c):
return a-b-c
if flag == "+"
return sum
elif flag == "-"
#
res = getFunc ("+") #返回一个函数
result = res (1,2,5) #调用加法这个函数
匿名函数
没有名字的函数
lambda 参数 a,b... : 表达式 (返回值)
只能写一个表达式,不能直接 return
return = (lambda x, y:x+y)(1,2) #返回1 +2
或
newFunc = lambda x, y:x+y
print (newFunc (4,5))
应用:
qwq = ([{"name": 'sz','age': 19},
{"name": 'ac','age': 13},
{"name": 'tr','age': 15}])
#
sorted (qwq, key=lambda x: x['age'])
闭包
在函数嵌套的前提下,内层函数引用了外层函数的变量,外层函数又把内层函数当作返回值进行返回
test () 嵌套了函数 test 2 ()
test 2 () 中使用了外层函数 test ()中的变量 b
或者用了传进来的参数 c
test () 将整个内部函数 test 2 ()返回出去
def test ():
a = 10
def test 2 ():
print (a)
return test 2 ()
newFunc = test ()
newFunc ()
->变成 print (10)
应用场景:外层函数,根据不同的参数生成不同作用功能的函数
例如:根据不同的配置信息生成不同分割线
应用 1:
def line_config (content, length):
def line ():
print ("-"*(length//2)
+ content + "-"*(length//2))
return line
line 1 = line_config ("aaa", 40)
line 2 = line_config ("bbb", 20)
line 1 ()
line 2 ()
应用 2:
a>10 时返回相加函数,否则返回相减函数
def config (a, b, c)
if a>10:
def summ (b, c)
return summ ()
else
def cutt (b, c)
return cutt ()
res 1 = config (11,3,4)
返回 summ (3,4)
res 2 = config (9,7,5)
返回 cutt (7,5)
注 1:闭包中要修改引用的外部变量,要用 nonlocal
声明,否则会当作时闭包内新定义的变量
def test ():
num = 10 #-> 外部 num:10
def test 2 ():
num = 666 #-> 内部新 num:666
print (num)
print (num) #-> 外部 num:10
test 2 () #-> 内部 num:666
print (num) #-> 外部 num:还是 10
内部完全独立于外部
#
def test ():
num = 10 #-> num:10
def test 2 ():
->>> nonlocal num
num = 666 #-> num:666
print (num)
print (num) #-> num:10
test 2 () #-> num:666
print (num) #-> num:变为 666
(nonlocal ≈ global)
注 2:闭包中引用了一个后期会发生变化的变量时要注意
def test ()
a=1
def test 2 ()
print (a)
a=2
return test 2
newFunc = test ()
-> newFunc = test 2
a = 2
newFunc () #-> 执行 print (a)
输出 2,只有当定义的函数被调用的时候才会确定值
之前都是以普通的变量表示名称而存在
def test ()
funcs = []
for i in range (1,4)
def test 2 ()
print (i)
funcs. append (test 2)
return funcs
newFunc = test () #-> newFunc = funcs
-> newFunc=[test 2, test 2, test 2]
=[print (i), print (i), print (i)]
i = 3
newFunc [0]()
newFunc [1]()
newFunc [2]()
结果全部是 3: 执行的时候已经循环完毕, i 始终为 3 了
要跟随改变:
def test ()
funcs = []
for i in range (1,4)
def test 2 (num)
def inner ():
print (num)
return inner
funcs. append (test 2 (i))
-> 每次循环:
调用并运行 test 2 (i)
此时 test 2 = inner,
i 为对应的 i
相当于运行 inner ()
相当于运行 print (变化 i)
将 print (变化 i) 加入列表
return funcs
-> 返回[print (1), print (2), print (3)]
只需要知道:
def test (i)
def inner (i):
return inner
#
test (i) return inner
看两个 def: test (i) return inner (i)
相当于: test = inner
只要调用、执行了 test (i)
就看成执行了 inner (i)
装饰器
在函数名与函数体不改变的前提下,给一个函数附加一些额外代码
# 功能代码
def fss ():
print ("发说说")
def ftp ():
print ("发图片")
# 逻辑代码
if a==1:
fss ()
else:
ftp ()
#
添加登录验证:
1、直接在逻辑代码中添加 (不推荐)
if a==1:
print ("login")
fss ()
else:
print ("login")
ftp ()
逻辑代码很多,所以冗余度大,复用性差
2、直接在功能代码中修改
def fss ():
print ("login")
print ("发说说")
def ftp ():
print ("login")
print ("发图片")
将登录功能提取出来:
def login ():
print ("login")
开放封闭原则:
已经写好的代码不要修改
新增的功能在原先基础上单独扩展
单一原则:
单一的功能函数只执行他对应的功能
不要参杂其他功能
def fss ():
print ("发说说")
def ftp ():
print ("发图片")
#
def login (func)
print ("login")
func ()
#
if a==1:
login (fss)
else:
login (ftp)
但是这样会改变原来的逻辑代码
那怎样不改变逻辑代码呢?
def login (func)
print ("login")
func ()
#
def fss ():
print ("发说说")
def ftp ():
print ("发图片")
#
fss = login (fss)
ftp = login (ftp)
#
这样不行,因为这个时候已经执行了一遍
所以使用闭包:
def login (func)
def inner ():
print ("login")
func ()
return inner
#
fss = login (fss)
= inner () #传入func =fss
= print ("login")
func ()
= print ("login")
fss ()
不使用闭包,代码将在给新函数赋值的时候调用,如果使用闭包,则可以避免调用,安全将函数“改名”
此时称 login 为装饰器:在原有函数的基础上添加一些小功能
语法糖写法:
def login (func)
print ("login")
func ()
def fss ():
print ("发说说")
def ftp ():
print ("发图片")
fss = login (fss)
ftp = login (ftp)
#
变为:
def login (func)
print ("login")
func ()
@login
def fss ():
print ("发说说")
@login
def ftp ():
print ("发图片")
#
注:@login
def fss ()
相当于:
def fss ()
fss = login (fss)
#
装饰器的执行时间:当写 @login 时就已经执行了
总结: https://www.bilibili.com/video/BV1v4411B7Zv/?p=167
注意事项 :
1 、多个执行器可叠加执行
从上至下装饰
从下至上执行
2 、参数传递
def aaa (func):
def inner ():
print ("a")
func ()
return inner
@aaa
def bbb (num)
print (num)
bbb (123)
#
错误,应为
def aaa (func):
def inner (n): <-
print ("a")
func (n) <-
return inner
#
对于多个参数,可用不定长参数
def aaa (func):
def inner (*args,**kwargs):
print (args, kwargs)
func (*args,**kwargs)
return inner
def bbb (num 1, num 2, num 3)
print (num 1, num 2, num 3)
bbb (111,222, num 3=666)
-> (111,222){'num 3'=666}
111,222,666
3 、返回值
def aaa (func):
def inner (n):
print ("a")
res = func (n)
return res
return inner
@aaa
def bbb (num)
print (num)
return (num) <-
bbb (123)
bbb (n)= aaa (bbb)(n)
= inner (n)
= print ("a")
res = bbb (n)
return res
= print ("a")
print (n)
return n
只需要保证闭包 inner ()中的格式和被装饰的函数的格式相同
4 、带参数的装饰器
def aaa (func):
def inner ():
print ("-"*30)
func ()
return inner
@aaa # bbb=aaa (bbb)
def bbb ():
print ("666")
#
如果要传其他字符:
def getaaa (char)
def aaa (func):
def inner ():
print ("-"*30)
func ()
return inner
return aaa
@getaaa ("*")
def bbb ():
print ("666")
相当于用一个函数包着装饰器
def getaaa (char)
装饰器
return 装饰器
@getaaa ("*")
= @aaa 且 char="*"
生成器
生成器是一种特殊的迭代器
特点:
节省内存,用时再取
能记录状态,用 next ()访问下一状态
具有可迭代特性,可 for … in …
l = [i for i in range (1,100) if i % 2==0]
iter (l)
一次性生产很多数,不好
创建方式
表达式生成
l = (i for i in range (1,100) if i % 2==0)
print (next (l))
print (l.__next__())
#
函数生成
def test ():
print ('0')
yield 1
print ('a')
yield 2
print ('b')
yield 3
print ('c')
yield 4
print ('d')
yield 5
print ('e')
包含 yield
g = test () -> 什么都不生成
print (g) -> 什么都没有
print (next (g)) -> 执行到 yield 1
打印 '0' 1 (返回值/状态值)
print (next (g)) -> 执行到 yield 2
打印 '0' 1 'a' 2
每次 next 都会让程序继续执行,直到下一个 yield 后暂停,返回该 yield 值,只有 next 会执行
yield 5 之后再 next 就会报错 (找不到下个 yield)
send ( ) 方法
def test ():
print ("xxx")
res 1 = yield 1
print (res 1)
res 2 = yield 2
g = test ()
print (g.__next__()) -> xxx 1
print (g.__next__()) -> xxx 1 None 2
其中 res 1 = yield 1 的值为 None
相比 send () 可以额外给 yield 传值
指定上一次被挂起的 yield 语句的返回值
例如
第一次: print (g.send (None)) -> xxx 1
第一次不能发非空的值, 只能传 None
第二次: print (g.send ("aaa")) -> xxx 1 aaa 2
关闭生成器
def test ():
print ('0')
yield 1
print ('a')
yield 2
print ('b')
g = test ()
g.close ()
注意:
1、发现 return 立即终止
def test ():
print ('0')
yield 1
return 10
print ('a')
yield 2
print ('b')
-> 0 1 StopIteration (10)
2、用 for in 会运行所有代码
def test ():
print ('0')
yield 1
print ('a')
yield 2
print ('b')
g = test ()
for i in g:
print (g) -> 0 1 a 2 b (没有报错)
for i in g: -> 第二次没有效果 (只能遍历一次)
print (g)
递归函数
函数 A 内部再继续调用函数 A
求 9!
def jiecheng (n)
if n == 1:
return 1
return n * jiecheng (n-1)
jiecheng (9)
函数作用域
命名空间. 函数 ()
def fun 1 ():
global a <-
a = 6
def fun 2 ()
nonlocal a <-
a=77
全局变量通常命名:g_name
文件使用
打开文件:给文件搭建操作管道(文件句柄)
文件管道 = open (“文件”,“模式”)
f = open ("a.txt","r")
定位位置:定位操作指针
关闭文件:关闭管道
f.close ()
(管道可能有读取、读、写)
打开
f = open ("a.txt","r")
相对路径:相对于当前目录的文件 "a.txt"
模式:
r: 只读, 不在会报错, 指针在开头 (默认模式)
w: 只写, 不在会创建, 指针在开头, 覆盖全部
a: 追写, 不在会创建, 指针在结尾
添加 b: 二进制格式操作读写
添加+: 以读写模式打开
r+: 读写, 不在会报错, 指针在开头, 覆盖部分
例 1: 图片转移一半
f = open ("a.jpg","rb")
print (f.read ())
f.close
t = open ("new. jpg","wb")
t.write (f.read ()[0: len (f.read ())//2])
t.close ()
读写
定位:
f.seek (偏移量,[0,1,2])
0: 文件流最前端, 偏移量为 0/正数
1: 当前指针位置
2: 文件流末尾, 偏移量为 0/负数
注: 1/2 只对二进制文件有效 ("rb")
f.tell ()
查看文件指针位置
读:
f.read (a)
读取两个字符,默认留空读完
f.readline (a)
读取一行数据 (带回车)
f.readlines ()
文件按换行符处理, 处理好的每一行组成列表
("\n"算作两个字符)
for... in...
f=open ("a.txt","r")
for i in f: -> 直接对 f 本身遍历
print (i)
g=f.readlines ()
for i in f: -> 对行列表遍历
print (i)
f.readable ()
可读判定, 返回 true
用哪种呢:
文件大:
readline 按行加载
for in 对 f 本身遍历
文件小:
用. read ()或. readlines ()
(都加载到内存中, 提高速度)
写:
f.write ("abc")
写入"abc"数据, 返回这个字符串长度
f.writable ()
可写判断, 返回 true
关闭
f.close ()
关闭文件,可以释放系统资源
清空缓冲区的数据内容到磁盘文件
f.write ()只会写入到缓冲区
f.flush ()
立即将缓冲区的内容写入到文件中
并清空缓冲区
文件操作
import os
重命名:
os. rename ("b.txt","bb. txt") #改文件名
os. rename ("aaa","bbb") #改文件夹名
os. renames ("one/one. txt","two/two. txt")
删除:
os. remove ("xx 2. jpg")
删除文件, 不存在会报错
os. redir ("two")
不能删除非空目录
os. removedirs ("one/ones")
递归删除: 连 one 也删除
创建文件夹
os. mkdir ("s")
创建新文件夹 (只能创建一级目录)
os. mkdir ("s", 0 o 777)
数字模式权限 (默认八进制 777 对应用户可读可写)
获取当前目录:
os. getcwd ()
修改默认目录:
os. chdir ()
获取目录内容的列表:
os. listdir ("a") #获取a文件夹
os. listdir ("./") #获取所在文件夹
os. listdir ("../") #获取父级文件夹
案例 1:
复制文件到新的文件里
import os
os.chdir("files")
重新导航到files文件夹里
source_file = open("d.txt","r",encording="utf-8")
dst_file = open("d-bat.txt","a",encording="utf-8")
需要保持编码一致
一起读取:
content = source_file.read()
det_file.write(content)
分段读取:
while True:
content = source_file.read(1024)
if len(content)==0:
break
print("-----",content)
dst_file.write(content)
结束:
source_file.close()
dst_file.close()
案例 2:
按照不同的文件后缀分到不同文件夹中
import os
import shutil
path ="files"
if not os.path.exists(path):
exit()
os.chdir(path)
file_list=os.listdir("./")
for file_name in file_list:
index = file_name.rfind(".")
if index == -1:
continue
extension = file_name[index + 1:]
if not os.path.exists(extension):
不存在目录
os.mkdir(extension)
shutil.move(file_name,extension)
案例 3:
列举整个文件目录及所有文件并写入文件
def listFiles(dir,file):
file_list = os.listdir(dir)
for file_name in file_list:
if os.path.isdir(file_name):
file.write(file_name+"\n")
listFiles(file_name,file)
else:
file.write("\t",file_name)
file.write("\n")
f = open("list.txt","a")
listFiles("files",f)
核心阶段
类
经典类
新式类
类名:大驼峰,首字母大写
class Money:
pass
变量:可以改变的量值
不同位置不同访问权限
属性:属于某个对象的特征
只能用变量名访问
属性加到类:类属性
属性加到对象:对象属性
判定变量还是属性:
是否存在宿主
实例化一个对象:
Money()
赋值给一个变量来控制它
one = Money()
print(one)
-> object at xxx
one.__class__
-> 有一个属性找到对应的类
Money.__name__
-> Money类的类名:Money(固定的)
xxx.__name__
-> 还是Money
属性
对象的属性
增加
class Person:
pass
p=Person()
p.age = 18
增加一个对象的属性
p.__dict__
-> 查看对应的所有属性和值
查询
print(p.age)
没有这个属性的时候报错
修改
p.age = 18
p.age = 19
存在就修改,不在就添加
修改后会重新开辟一个位置并重新指向
p.name=['a','b']
p.name=['c','s']
修改后会重新开辟一个位置并重新指向
p.name.append('a')
先访问再追加a,不另外开辟空间
(查看id确定有无开辟)
删除
对于变量:
num=10
del num
-> 报错:未使用的时候是相当于无定义
print(num)
类似的,对于属性:
p.age = 18
del p.age
print(p.age)
类的属性
同一个类的不同对象不能互相访问
万物皆对象,类本身也是一个对象
类是描述对象的
纸的说明书本身也是纸
增加
类名.类属性 = 值
或者:
class hello:
age = 18
类属性 = 值
不能通过对象增加
查询
hello.age
也可以通过对象访问属性
也可以修改对象的类:
class a:
sex=10
class b:
age=11
c=a()
c.sex
-> 10
c.__class__=b
c.age
-> 11
通过对象访问的属性优先从自己身上找
没有再去类身上找
修改
hello.age = 22
类名.属性 = 新值
不能通过对象修改:
对象会认为添加一个新的属性
删除
del 类名.属性
不能通过对象删除
属性的内存存储
class Money:
pass
one=Money()
one.__dict__={"nkame":"sz"}
将one的__dict__设置为字典
对象可以修改__dict__属性
实际上属性添加在对象的__dict__属性中
类也有__dict__属性,只不过是不可修改的
限制对象属性
class Person:
__slots__=["age"]
-> 只对象允许添加age属性
pass
p1=Person()
p2=Person()
方法
class Person:
def eat2(self):
print(1)
p=Person()
p.eat2()
对象是由类实例化出来的
此时称:
类 -> 类
对象 -> 实例
类和实例都是 -> 对象
类方法、实例方法、静态方法
实例方法:第一个参数接收实例
类方法:第一个参数接收类
静态方法:第一个参数什么也不接收
class Person:
def aaa(self):
print("实例方法")
@classmethod
def bbb(cls):
print("类方法")
@staticmethod
def ccc():
print("静态方法")
p=Person()
p.aaa
-> 调用p的实例方法
Person.aaa
-> 缺少实例参数self,报错
所有方法都是存在类里面,不是存在对象里
也是存储在类的__dict__字典里面
对应的字典存储方式:{“函数名”:“函数”}
实例方法
class Person:
def aaa(self):
print("实例方法")
调用时自动将实例传递到第一个参数里面
其他调用:
func=Person.aaa
func("hello")
-> 传递“hello”给self
当作本质函数调用
类方法
class Person:
@classmethod
def bbb(cls):
print("类方法")
调用:
调用时自动将类传递到第一个参数里面
Person.bbb()
p=Person()
p.bbb()
-> 都是自动传递类参数
如果时继承的类则传递的是继承的子类
class A(Person):
pass
A.bbb()
-> A类继承了Person类,传递A而不是Person
静态方法
l=[1,4,2,3]
sorted(l)
类方法:
list.sort(l)
实例方法:
l.sort()
如果不想使用类或者实例,则使用静态方法
class Person:
@staticmethod
def ccc():
print("静态方法")
可以使用类或者对象随意访问,不会自动传递第一个参数
不同类型的方法访问变量
实例方法可以访问该实例和对应类的变量
类方法只能访问类的变量,不能访问实例的
静态方法还是可以访问到类的变量的,但是不能访问实例的变量,通常不访问变量
补充
类的补充
类:
创建类对象的类 Type
手动创建类:
def run(self):
print(self)
xxx = type("Dog",(),{"count":0,"run":run})
类名称是Dog,可以通过xxx来找到这个类
元类的查询:
Person.__metaclass__
-> 查询元类
如果找不到就去父类找元类
没有就去模块找元类
如果还没有就通过type创建元类
__metaclass__ = xxx
class Animal:
__metaclass__ = xxx
pass
class Person(Animal):
__metaclass__ = xxx
pass
类的说明文档:
class Person:
"""
类的描述、作用、构造函数,类的属性描述
Attributes:
count:int 代表是人的个数
"""
def run(self,distance,step):
"""
这个方法的作用效果
:param distance:参数的含义,参数的类型int,是否有默认值
:param step:
:return:返回的结果的含义(时间),返回数据的类型int
"""
print("hello")
return distance
help Person
生成项目文档:
内置模块 pydoc
cmd:
cd 项目路径
python -m pydoc 文件
-> 输出 文件.py 的所有注释
python -m pydoc -p 1234
-> 在本地电脑开启1234端口
b
-> 开启端口情况下输入b
可查看所有模块的相关注释文档
q
-> 退出本地服务器
python -m pydoc -w
第三方模块
Sphinx
epydoc
doxygen
属性的补充
私有化属性
只有伪私有的效果
x
-> 公有属性
类内部访问
子类内部访问
模块其他位置访问
跨模块访问:
import形式导入
from 模块 import *形式导入
_y
-> 受保护的属性
-> 公开访问
类内部访问
子类内部访问
-> 强行访问:
模块其他位置访问
-> 添加__all__=["_a"]
跨模块访问
__y
-> 私有属性
除了类的内部访问
-> 添加__all__=["_a"]
其他地方都访问不了
伪私有:
Animal._Animal__x
-> 可以用来访问私有属性
私有属性的应用场景
class Person:
初始化对象的私有值
def __init__(self):
self.__age=18
-> 自动添加属性对象属性age=18
设置数值的方法
def setAge(self,value):
if isinstance(value,int) and 0<value<200:
self.__age=value
else:
print("你输入的数据有问题")
获取数值的方法
def getAge(self,value):
return self.__age
-> 实现通过内置方法设置和访问私有值
其他情况
class_
-> 取系统的变量名,为了做区分
__class__
-> 一般为系统内置的
只读属性
方法 1
部分公开:
私有化禁用读写,再公开读的操作
class Person(object):
-> 首先继承object
def __init__(self):
self.__age= 18
-> 设置为私有属性
@property
def age(self):
return self.__age
-> 提供一个方法暴露给外界
外界相当于只能通过函数来获取数值
p1=Person()
p1.age
-> 直接获取对应的值
p1.age = 19
-> 会报错
使用 property 优化
@property
-> @property主要作用就是可以以使用属性的方式来使用这个方法
可以将一些属性的操作方法关联到某一个属性中
使用函数
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx,"I'm the 'x' property.")
使用装饰器
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x. setter
def x(self,value):
self._x = value
@x.deleter
def x(self):
del self._x
相关概念:
经典类:没有继承object
新式类:继承object
python2默认不继承,python默认继承
class Person(object):
pass
Person.__bases__
-> 查看父类
建议都写上object保证兼容性
property 在新式类的使用方法:
方法1:
class erson(object)
def __init__(self):
self.__age = 18
def get_age(self):
return self.__age
def set_age(self):
self.__age = value
age = property(get_age,set_age)
p=Person()
p.age
-> 18
p.age=90
-> p.age
class Person(object)
def __init__(self)
@property
def age(self):
return self.__age
@age.setter
def age(self,value):
self.__age=value
p=Person()
p.age
-> 18
p.age = 10
-> 可以设置
property 在经典类的使用方法:
只能使用获取方法,其他写入方法、删除方法均无效
方法 2
上面的方法还是可以绕开来访问
p1._Person__age = 999
pq.__dict__["_Person__age"] = 999
class Person:
def __setattr__(self.key,value):
print(key,value)
-> 当使用 实例.属性 = 值
给一个实例增加或修改一个属性都会调用这个方法
在这个方法内部才会真正把这个属性以及对应的数据
存储到__dict__字典中
if key=="age" and key in self.__dict__.keys():
print("是只读属性,不可创建")
else:
self.__dict__[key] = value
(不能用self.key=value,否则会死循环)
-> 通过实例.属性=值时调用这个函数判断,不允许添加或修改age属性
内置特殊属性
类属性:
__dict__:类的属性
__base__:类的所有父类组成的元组
__doc__:类的文档字符串
__name__:类名
__module__:类定义所在的模块
私有方法
def __run():
pass
内置特殊方法
调用操作
class Person:
-
快捷创建:
def __init_(self,n,a):
self.name = n
self.age = a
p1 = Person("sz",18)
-> 直接通过对象创建方法创建数值
-
格式化输出对象:
def __str__(self):
return "姓名%s,年龄%d"%(self.name,self.age)
print(p1)
-> 直接打印对象时会自动找str属性并通过str属性返回值格式化输出
print(str(p1))
-> 直接找到p1对象的str属性并打印字符串本身
-
直接获取对象的本质信息
def __repr__(self):
print("aaaa")
print(repr(p1))
-> 将p1本质信息输出
注意:repr后可通过eval还原
-
使对象具备当作函数来调用
def __call__(self,*args,**kwargs):
print("xxx")
pass
p = Person()
p()
-> 直接调用__call__方法
-
使用偏函数简化输入参数:
使用函数实现:
def createPen(p_color,p_type):
print("颜色%s,类型%s"%(p_color,p_type))
import functools
gangbiFunc = functools.partial(createPen,p_type="钢笔")
gangbiFunc("红色")
-> 直接默认类型是钢笔,免去输入钢笔和输入"p_color="
使用类实现:
class PenFactory:
def __init__(self,p_type):
self.p_type = p_type
def __call__(self,p_color):
print("类型%s,颜色%s"%(self.p_type,p_color))
gangbiF = PenFactory("钢笔")
gangbiF("红色")
索引操作
将实例对象可以以一个字典或以一个列表进行操作
class Person:
def __init__(self):
self.cache={}
def __setitem__(self,key,value):
self.cache[key]=value
def __getitem__(self,item):
return self.cache[item]
def __delitem__(self,key):
del self.cache[key]
p = Person()
p["name"]="sz"
print(p["name"])
del p["name"]
切片操作
class Person:
def _init__(self):
self.items = [1, 2, 3, 4, 5, 6, 7, 8]
def __setitem__(self, key, value):
self.items[key] = value
或者:
if isinistance(key,slice):
self.items[key.start: key.stop: key.step] = value
def __getitem__(self, item) :
print("getitem", item)
def __delitem__(self, key) :
print("delitem", key)
p=Person()
p[0:4:2]=["a","b"]
-> 将对应的第0个和第2个值修改了
["a", 2, "b", 4, 5, 6, 7, 8]
比较操作
class Person:
def __init_(self,age, height):
self.age = age
self.height = height
def __eq__(self,other):
return self.age == other.age
p1 = Person(18,190)
p2 = Person(18,180)
print(p1 == p2)...
-> = 通过__eq__来判定对象之间是否相同或者不相同,是用的哪个比较
def __ne__(self,other):
return self.age != other.age
-> !=
def __gt__(self,other):
pass
-> >
def __ge__(self,other):
pass
-> >=
def __lt__(self,other):
pass
-> <
def __le__(self,other):
pass
-> <=
如果只定义了一个比较没有定义另一边(反向操作)
比如定义了p1>p2,而没有定义p1<p2
则运算p1<p2时仍然会调用p1>p2
参数会自动调换(self和other调换)
import functools
@functools.total_ordering
class Person:
def __lt__(self, other):
pass
def __eq__(self, other):
pass
通过导入参数库,当运算>=时自动通过<=和=来运算(可同时调用参数)
布尔操作
class Person:
def __init__(self):
self.age = 10
def __bool__(self):
return self.age >=18
pass
p = Person()
if p:
print("xx")
-> 根据__bool__的返回值判断p为真还是假
遍历操作
方式1:
class Person:
def __init__(self):
self.result = 1
def __getitem__(self):
self.result += 1
if self.result >= 6:
raise StopIteration("停止便利")
return self.result
pass
p = Person()
for i in p:
print(i)
方式2:
class Person:
def __init__(self):
self.result = 1
def __iter__(self):
self.age = 1
->初始化迭代器
使其可以重复使用
return self
def __next__(self):
self.result += 1
if self.result >= 6:
raise StopIteration("停止便利")
return self.result
pass
p = Person()
for i in p:
print(i)
-> 相当于首先通过 iter(p) 计算出里面的迭代器
本质就是调用这个__iter__来获取
之后会使用 next() 来获取下一个数据
注意:
l=[1,2,3]
变成迭代器:iter(l)
判断是否为迭代器
import collections
print(isinstance(p,collections.Iterator))
-> 是否迭代器:只有当都存在 iter 和 next 的时候才是迭代器
print(isinstance(p,collections.Iterable))
-> 是否可迭代对象:存在 iter 的时候是可迭代的
可迭代一定可以用 for in 访问,用 for in 访问的不一定可迭代对象
描述器
描述一个属性操作的对象
限制输入的内容
方式1:property
class Person:
def _init__(self):
self.__age = 10
def get_age (self) :
return self.__age
def set_age(self, value) :
if value < 0:
value = 0
self.__age = value
p = Person()
p.age = 10
print(p.age)
del p.age
方式2:
class Person:
def _init__(self):
self.__age = 10
def get_age (self) :
return self.__age
def set_age(self, value) :
if value < 0:
value = 0
self.__age = value
class Person:
age = Age()
-
p = Person()
p.age = 10
print(p.age)
del p.age
自动调用Age()中包含的增删改查来实现
不能顺利转换的场景:
一个实例属性的正常访问顺序:
实例对象自身的 dict 字典
对应类对象的,dict 字典
如果有父类, 会再往上层的 dict 字典中检测
如果没找到, 又定义了 getattr 方法, 就会调用这个方法
而在上述的整个过程当中
是如何将描述器的_get__方法给嵌入到查找机制当中?
通过 __getattribute__
这个方法进行实现
如果实现了描述器方法 get 就会直接调用
如果没有,则按照上面的机制去查找
这个方法当有 get 方法时系统会自动使用这个方法
属性调用优先级:
资料描述器:
实现了 get set
非资料描述器:
只实现了 get
优先级:资料描述器>实例属性>非资料描述器
类的存储问题
class Age(obiect):
def __get__(self,instance, owner):
return instance.v
def __set__(self,instance, value) :
instance.v=value
def __delete__(self,instance) :
del instance.v
-> instance是Person对象
Age对象是共享的
只有不同的Person实例才能存储不同的数据
class Person(object):
age = Age()
p = Person()
p.age = 10
print(p.age)
del p.age
装饰器
def check(func):
def inner () :
print("登录验证")
func()
return inner
@check
def fashuoshuo () :
print("发说说")
fashuoshuo = check(fashuoshuo)
fashuoshuo()
-> 使用语法糖来装饰器
使用类来实现:
class check:
def __init__(self, func):
self.f = func
def __call__(self,*args, **kwargs):
print("登录验证")
self.f()
@check
def fashuoshuo():
print("发说说")
fashuoshuo = check(fashuoshuo)
fashuoshuo()
生命周期
监听对象的生命周期
class Person:
def __new__(cls,*args, **kwargs):
print("新建了一个对象,但是,被我拦截了")
用于创建对象时给这个对象分配内存的方法
通过拦截这个方法可以修改对象的创建过程
比如单例设计模式
def __init__(self)
将new方法创建好的对象传递进来
附加一些东西
print("初始化方法")
self.name = "sz"
def del__(self):
print("这个对象被释放了“)
pass
p = Person()
print(p)
案例:计数创建的实例有多少个
class Person:
__personCount = 0
def __init__(self) :
print("计 + 1")
Person.__personCount += 1
def __del__(self):
print("计 - 1")
self.__class__.__personCount -= 1
@staticmethod
def log():
print("当前的人的个数是%d个" % Person.__personCount)
或者:
@classmethod
def log(cls):
print("当前的人的个数是%d个" % cls.__personCount)
-
p = Person()
p2 = Person()
del p
Person.log()
内存管理机制
存储方面
无论是什么均属于对象,不存在基本数据类型
所有对象都会在内存开辟一个空间进行存储
对于整数和短小的字符,Python 会进行缓存,不会创建多个相同的对象
容器对象(列表、元组、字典),存储其他对象仅仅是其他对象的引用,而不是其他对象本身
垃圾回收方面
引用计数器
通过被引用数来确定是否应该把他删除
查看被引用数
import sys
sys.getrefcount(对象)
-> 得出的结果会大1
+1
的情景:
被创建
被引用
被作为参数传入一个函数中 p1=Person()
传入一个函数中会+2 p2=p1
有两个属性引用这个参数
作为一个元素存储在容器中 L=[p1]
-1
的场景:
对象的别名被显式销 del p1
对象的别名被赋予新的对象 p1=123
一个对象离开它的作用域
一个函数执行完毕
对象所在的容器被销毁或从容器中删除
循环引用问题 (内存泄漏)
P -> pet pet 被引用+1
D -> master master 被引用+1
pet -> master master 被引用+1
master -> pet pet 被引用+1
删除 P -> pet 、D -> master
此时:
pet -> master master 被引用=1
master -> pet pet 被引用=1
但是已经没有 P、D 可以引用 pet 和 master
导致这些内存已经没有使用但是无法释放
导致内存泄露
class Person:
pass
class Dog:
pass
p = Person()
d = Dog()
p.pet = d
d.master = p
del p
del d
删除 P,d之后,对应的对象无法释放掉
import objgraph
objgraph.count()
可以查看,垃圾回收期,跟踪的对象个数
objgraph.count("Person")
垃圾回收器所跟踪的这个Person类所创建的对象
此时可以使用这个查看是否还有这些剩下的未清理的
但是仍然无法消除这些
所以 Python 是有两套内存管理机制并存
内存管理机制=引用计数器机制+垃圾回收机制
垃圾回收机制专门用来对付循环应用问题
垃圾回收机制
怎样找到“循环引用”:
1、收集所有的“容器对象”, 通过一个双向链表进行引用
2、针对于每一个“容器对象", 通过一个变量 gc_refs 来记录当前对应的引用计数
3、对于每个"容器对象”,找到它引用的“容器对象", 并将这个"容器对象"的引用计数 -1
4、如果一个"容器对象”的引用计数为 0, 就代表这玩意可以被回收了, 肯定是"循环引用
非容器对象是不能引用其他对象的
垃圾回收机制主要是针对容器对象
对于一些比较长寿的对象
减少这些对象的“检测频率”
设计了一套机制:分代回收
代数越高则对应的检测频率越低
垃圾回收时机
自动回收
import gc
并且达到垃圾回收阈值
手动回收
gc.collect()
-> 收集所有的垃圾
在 Python 2 中只要一个对象实现了 del 方法,则不再自动回收
通过手动破坏循环引用:
强引用:能触发引用数自动+1 的引用
弱引用:引用数保持不变
p=Person()
d=Dog()
p.pet=d
d.master=weakref.ref(p)
import weakref
weakref.WeakValueDictionary("dog":d1)
实例:计算器
class Caculator:
def __say(self,word):
#1 创建一个播报器对象
speaker = win32com.client.Dispatch("SAPI.SpVoice")
#2,通过这个播报器对象,直接,播放相对应的语音字符串就可以
speaker.Speak(word)
def __create_say_zsq(word=""):
def __say_zsq(func):
def inner(self, n):
self.__say(word + str(n)):
return func(self, n)
return inner
return __say_zsq
def __check_num_zsq(func):
def inner(self, n) :
if not isinstance(n, int):
raise TypeError("类型有问题")
return func(self, n)
return inner
@__check_num_zsq
@__create_say_zsq
def __init__(self, num):
self.__result = num
@__check_num_zsq
@__create_say_zsq("加")
def jia(self,n):
self.__result += n
return self
-> 链式编程
@__check_num_zsq
@__create_say_zsq("减")
def jian(self, n):
self.__result -= n
return self
@__check_num_zsq
@__create_say_zsq("乘")
def cheng(self,n):
self.__result *= n
return self
def show(self):
self.__say("计算的结果是:%d" % self.__result)
print("计算的结果是:%d" % self.__result)
@property
def result(self):
return self.__result
c1 = Caculator(2)
cl.jia(6)
cl.jian(4)
c1.cheng(5)
c1.show()
c1.result
cl.jia(6).jian(4).cheng(5).show()
语音播报:
import win32com.client
创建一个播报器对象
speaker = win32com.client.Dispatch("SAPI.SpVoice")
通过这个播报器对象,直接播放相对应的语音字符串就可以
speaker.Speak("我的名宇是sz")
面向对象三大特性
封装
1、
2 、
继承
1、
2、
class Animal:
pass
class xxx:
pass
class Dog(Animal,xxx):
pass
3、
资源的继承
不能继承:
私有的属性
私有的方法
可以继承:
公有属性、方法
受保护的属性、方法
内置方法
资源的使用
Python 2.2 之前:MRO
1、ABC
2、ABDCE
3、ABDC
Python 2.2:MRO 改进(不是广度优先)
1、ABC - Object
2、ABDCE - Object
3、ABCD - Object
Python 2.2 之后:Merge 算法
资源的覆盖
优先级比较高的可以覆盖优先级比较低的
优先级比较高的先被访问,导致其他的无法访问,导致覆盖
可以写一个优先级比较高的来重写
继承的子类创建的对象使用该子类的对象方法和类方法的时候,调用的类(cls)是该子类,调用的对象(self)是该子类的对象
子类调用父类的方法基础上新增内容
比如在父类的初始化__init__方法基础上再新增一些初始化内容
资源的累加
子类比父类增加自己独有的资源
class B:
def __init__(self):
self.b = 2
self.xxx = "123"
class A:
def __init__(self):
B.__init__(self)
-> 调用父类的方法
super (A, self).__init__(self)
-> 调用 B 的对象方法则传 self (对象)
调用 B 的类方法则传 A(类)
super ().__init__()
-> 相当于 super (A, self)
-> 上面两种 super 不要混合使用
self. a = 5
Super 不是上级节点, 而是 MRO 链条的上一个节点
多态
一个类调用时的多种形态
在继承的前提下使用不同的子类调用父类的同一个方法,产生不同的功能
在子类中重写方法
Python 没有真正的多态
补充
抽象类和抽象方法
不能创建实例,不能直接调用
import abc
设置类的元类: abc. ABCMeta
使用装饰器修饰抽象方法:@abc. abstractmethod
class Animal (object, metaclass=abc. ABCMeta):
@abc. abstractmethod
def jiao (self):
pass
@abc. abstractclassmethod
def test (cls):
pass
class Dog (Animal):
def jiao (self):
print ("hello")
def test (self):
print ("yyy")
抽象类不能直接调用
所有的子类必须实现父类中的所有抽象方法
实例
class Animal :
def __init__(self, name, age=1):
self. name = name
self. age = age
def eat (self) :
print ("%s 在吃饭"% self)
def play (self):
print ("%s 在玩"% self)
def sleep (self):
print ("%s 在睡觉"% self)
class Person (Animal) :
def __init__(self, name, pets, age=1):
super (Person, self).__iit__(name, age)
self. pets = pets
def yang-pets (self) :
for pet in self. pets:
pet.eat ()
pet.play ()
pet.sleep ()
def make_pets_work (self) :
for pet in self. pets:
pet.work ()
def __str__(self):
return "名字是{},年龄{}岁的人".format (self. name, self. age)
class Cat (Animal):
def work (self):
print ("%s 在提老鼠"% self)
def __str__(self):
return "名字是 0,年龄小岁的小猫".format (self. name, self. age)
class Dog (Animal) :
def work (self):
print ("%s 在看家”% self)
def __atr__(self):
return "名字是{},年龄{}岁的小狗".format (self. name, self. age)
d = Dog ("小黑", 18)
c = cat ("cat", 19)
p = Person ("sz",[d, c], 18)
p.yang_pets ()
p.make_pets_work ()
代码设计原则
S
O
L
I
D
后续
包含异常处理和 Python 库与包的安装