Python基础常识
python 区分大小写 Andy != andy
print("\r", end="")
end="" 关闭print的自动\n "\r" 返回行首打印
"\r" end=""
可以实现在shell中始终在一行的开头打印
temp = """string"""
实际上是一个可以换行的字符串 可以让变量来保存 如果在类或者函数里就是类和函数的说明文档
input("请输入:")
接收键盘输入的信息 返回字符串
type(对象)
查看数据类型
id(对象)
查看对象的引用地址以10进制
dir(对象)
查看对象的所有属性和方法 包括继承的
代码太长可以在需要换行的地方输入\然后换行或者使用()
TODO(作者)
标签 提示还没开发完成的功能
pass
占位符保证程序结构正确
while True:
循环入口 当while后的值为逻辑真时 进入无限循环
+=
可以合并列表 元组 字符串 针对列表的+=
实际上是调用了extend
方法 所以在函数内需要注意(可变数据类型)
变量保存着程序在自身内存空间中为数据提供的空间的引用
关键字就是python内置的功能 特点是后面不跟括号 直接使用(区别于property)
函数就是封装好的功能 特点是后面跟一个括号
方法就是针对面向对象的函数 对象.函数()
cpython37.pyc
结尾的文件是python解释器编译过的代码 以二进制的形式储存在电脑 目的是加快代码运行速度
代码的结构 shebang
==>import
==>定义的常量
==>定义的函数
==>代码
变量与常量
用来存储数值时变量
变化的量 常量
不变化的量
变量
的命名所有字母小写用下划线连接单词
常量
的命名所有字符大写用下划线连接单词
不要在代码中直接出现数字而应该使用常量
数据类型
数字型 int
float
bool
complex
布尔属于数字型
非数字型 str
list
tuple
dict
数据类型转换
int(str|float)
转换小数向下取整 无法转换带.
字符串类型小数 可以 int(float(str))
float(str|int)
bool(0即假 值为空即假 None即假)
str(num)
tuple(list)
list(tuple)
None
表示空对象 什么都没有
值为空不是None
是空数据的对象
拼接字符串
str * num
str + str
list * num
list + list
tuple *num
tuple + tuple
list + tuple
不能交叉合并
格式化字符串
%s
字符串
%d
十进制数字
%x
十六进制数字
%f
浮点型
%05.2f
保留2位小数并且保证整体宽度为5不足的以0占位 03.00
格式化字符串输出%
使用%%
格式化字符串的参数
本质上就是一个元组 可以用元组替换 print("%s年龄是%d身高是%.2f" % (tuple))
转义字符
\n
换行
\r
回到行首
\t
\v
制表符
\u
转义unicode字符串 \u00b2
分支语句
例1
if xxx: (判断的是逻辑真假)
xxx
else:
xxx
例2
if xxx:
xxx
elif xxx:
xxx
else:
xxx
IF嵌套
例3
if xxx:
xxx
if xxx:
xxx
else:
xxx
else:
xxx
三元表达式
方法一: 为真时的结果 if 判定条件 else 为假时的结果
d = b if a else c #如果a为真,结果是b,否则结果是c
print('方法一输出结果:' + d)
方法二: 判定条件 and 为真时的结果 or 为假时的结果
d = a and b or c #如果a为真,结果是b,否则结果是c
print('方法二输出结果:' + d)
以上两种方法方法等同于if ... else ...
if a:
d = b
else:
d = c
print('if语句的输出结果:' + d)
循环语句
例1
i = 0
while i (判断) xxx: (判断的是逻辑真假)
xxx
i += 1 # 设置循环换出口 在循环体外部设置计数器在内部每次迭代为计数器+1
例2
i = 0
while i (判断) xxx:
if i (判断) m:
xxx
break # 当程序满足某个条件 直接结束本层循环 不执行本层循环体的后续代码
i += 1
例3
i = 0
while i (判断) xxx:
if i (判断) m:
xxx
i += 1
continue # 当程序满足某个条件 重新循环本层循环体的下一个迭代 不执行本层循环体的后续代码
xxx
i += 1
WHILE嵌套
例4
i = 0
while i (判断) xxx:
m = 0
while m (判断) i:
xxx
m += 1
break # 只影响当前的循环体 不影响上一层的循环体
xxx
i += 1
break
continue
只影响当前的循环体 不影响上一层的循环体
迭代遍历
for num in list():
实际上执行list对象的__iter__
方法如果有此方法说明对象是一个可迭代对象如果没有说明无法迭代
__iter__
会返回一个对象如果这个对象有__next__
方法实现for循环
每循环依次取__next__
的返回值给num
可迭代对象如果没有通过iter
返回一个具有next
方法实现的对象 那么无法实现迭代
完整的FOR循环
for i in 集合:
print(i)
if i == x:
break
else:
print(break后不执行)
print(结束遍历)
遍历结束后执行else
后的代码但如果break
则不会执行
函数
函数的定义
def 函数名():
"""注释 define 上要要留两行空行pycharm中cltr + q 可以查看注释"""
函数的封装
文件保存的名字fun_file
是模块的名字
函数名是模块的方法 fun_file.方法()
不主动调用 封装的内容不会执行
函数的调用
import fun_file
用来加载定义的模块
fun_file.函数()
用来调用模块的方法
函数的参数
def 函数名(形参1, 形参2, …):
result = 形参1*形参2
return result
i = 函数名(实参1, 实参2, …)
print(i)
return
把结果返回给外部调用函数的函数名
return
可以结束函数和方法 下方的代码不会被执行
return
也可以终止循环体 并且不管多少层都直接终止
返回时: 可使用元组来返回多个结果 return (结果1, 结果2, 结果3)
可以省略括号
接收时: 返回的是元组 使用多个变量接收 变量1, 变量2, 变量3 = 函数()
(实际是一个元组 拆包)
函数的递归
在函数内部 调用函数自己 叫做函数的递归如果不指定出口会死循环
函数的递归求1到100的累加结果
def sum_num(num):
if num == 1:
return 1
temp = sum_num(num-1)
return num + temp
sum_ = sum_num(100)
print(sum_)
eval()函数
eval()
函数会计算字符串中的表达式
并返回结果
不要滥用eval()
转换input
的内容
enumerate()函数
可以对list
tuple
str
有序合集枚举 无法对dict
枚举
enumerate(seq, start=num)
返回的是一个对象 也可以通过start=num
指定序号起始数字
每个元组内容为 (0, H), (1, e), (2, l)
通常配合遍历使用
list = [1, 2, 3, 4, 5]
for i, num in enumerate(list):
print(i) # 序号
print(num) # 元素
列表 list
定义列表
list = ["张三", "李四", "王五"]
列表的方法
取值 list[下标]
下标也就是索引
从0开始
取索引 list.index("char")
char的索引是几就返回几
list[index] = "char"
把下标index的数据修改为char
list.append("char")
在列表末尾追加一个数据 list.append(list2)
是追加一个元素会显示为[1, 2, 3, [1, 2, 3]]
list.insert(0, "char")
向索引0的前面插入一个char
list.extend(list_2)
把列表2合并到list末尾 是合并一个列表会显示为[1, 2, 3, 1, 2, 3]
del list
删除列表
list.remove("char")
从列表中删除这个数据
list.pop(index)
删除这个索引下的数据
list.clear()
清空列表
list.pop()
删除最后一个索引的数据
len(list)
显示列表长度
list.count("char")
查看某个数据在列表中出现的次数
list.sort()
升序排列
list.sort(reverse=True)
降序排列
list.reverse()
逆序 颠倒排列
元组 tuple
定义元组
tuple = ()
空元组
tuple = (char,)
单元素元组
元组的方法
取值 tuple[index]
取索引 tuple.index("char")
查询char的索引下标
tuple.count("char")
统计char在元组里出现的次数
len(tuple)
查询元组的长度
字典 dict
字典是以 key: value
键值对对应 特点是无序 列表元组有序
定义字典
dict = {"name": "Lee", "age": 18, "height": 1.75}
key
必须唯一 value
不必唯一
字典的方法
取值 dict["key"]
修改 dict["key"] = "value"
增加 dict["key"] = "value"
当key
不存在表示添加键值对
删除键值对 dict.pop("key")
清空 dict.clear()
合并 dict.update(dict2)
删除字典 del dict
len(dict)
有多少组键值
dict.keys()
显示所有键 以列表形式
dict.values()
显示所有值 以列表方式
dict.items()
显示所有键值对 以列表方式
集合 set
在 Python 中,集合分为两类:
-
set
可变集合 可以原地修改 是 unhashable(不可哈希)的 -
frozenset
不可变集合 顾名被“冻结”的集合 不能原地修改 是 hashable(可哈希)的
集合(set)
是一个无序
的不重复
元素的序列并且是不可变类型
集合不支持索引
可以使用大括号{ }
或者set()
函数创建集合
创建一个空集合
必须用 set()
而不是 { } 因为 { } 是用来创建一个空字典
推导式
推导式comprehensions
(又称解析式) 推导式是可以从一个数据序列构建另一个新的数据序列的结构体 共有三种推导式
- 列表(list)推导式
- 字典(dict)推导式
- 集合(set)推导式
推到式的格式: [表达式 for 变量 in 列表]
或者 [表达式 for 变量 in 列表 if 条件]
推导式不仅让代码更简洁执行速度也更快
列表推导式
lst = [i for i in range(10)] # 生成列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
lst = [i*2 for i in range(10) if i >= 5] # 生成当i大于5时i平方的列表[25, 36, 49, 64, 81]
type(lst) # list
元组(生成器)推导式
gen = (i for i in range(10)) # 生成生成器通过next()函数或者gen.__next__()方法取值
type(gen) # generator
集合推导式
lst = [ 9, 1, 1, 3, 5, 5, 7, 7, 7, 0]
seting = {i for i in lst} # 生成一个无序的集合并且去重{0, 1, 3, 5, 7, 9}
type(seting) # set
字符串
定义字符串
string = "abcde"
字符串的方法
取值 string[num]
取索引 string.index("char")
显示char子字符的下标
len(string)
统计字符串的长度
string.count("char")
统计子字符char的出现次数
判断空格和空白字符
string.isspace()
字符串只有空白字符则true
(空字符串则false)
string.isalnum()
字符串有字符且没有空白字符则true
(空字符串则false)
判断字母
string.isalpha()
字符串只包含字母且没有空白字符则true
(空字符串则flase)
判断数字
string.isdecimal()
字符串只包含数字则true
可判断 半角
全角数字
string.isdigit()
字符串只包含数字则true
可判断 半角
全角数字
unicode特殊数字
string.isnumeric()
字符串只包含数字则true
可判断 半角
全角数字
unicode特殊数字
中文数字
这三个方法都不能判断小数点认为小数点是英文字符
判断大小写
string.istitle()
如果字符串的每个单词首字母大写则true
有空格没关系
string.islower()
如果字符串中有哪怕一个是能区分大小写的字符被小写则true
有空格没关系
string.isupper()
如果字符串中有哪怕一个是能区分大小写的字符被大写则true
有空格没关系
查找字符串
string.startswith("char")
如果是以char开头则true
string.endswith("char")
如果是以char结尾则true
string.find (str, start=0, end=len(str))
查询字符串是否包含子字符串可指定范围 有则返回下标 没有返回-1
string.rfind(str)
反向查询
string.index(str)
返回字符串中子字符串的下标 没有则报错
string.rindex(str)
反向查询
替换字符串
string.capitalize()
将字符串首字符大写
string.title()
将字符串中每个单词首字母大写
string.upper()
将字符串中所有小写字符改为大写
string.lower()
将字符串中所有大写字符改为小写
string.swapcase()
翻转字符串中的字母大小写
string.replace(old_str, new_str, num=string.count(str))
返回一个新的字符串 一次性修改字符串内所有子字符串 可以指定修改个数
文本对齐
string.center(num, "char")
居中对齐并且填充宽度 还可以填充字符
string.ljust(num, "char")
向左对齐以字符填充
string.rjust(num, " ")
向右对齐以空格填充
去除空白字符
string.strip("\t\n")
去掉字符串左右的空白字符
string.lstrip("\n")
去掉左边空白字符
string.rstrip(" ")
去掉右边空白字符
\t
\n
也可以被去掉
拆分和链接
new_list = string.split(str)
以str
为点拆分字符串 输出一个列表 如果str
不指定就默认包含\r
\n
\t
和空格
list_lines = string.splitlines()
如果string
有换行使用splitlines()
可以以行为单位返回一个字符串列表
new_string = str.join(seq)
将序列中的每个元素组成一个字符串用str
间隔
new_string = " ".join(seq)
以空格链接序列 seq
可以是列表
元组
字符串
切片
可以对list
tuple
str
有序集合切片 无法对dict
切片
string[开始索引: 结束索引: 步长]
返回一个字符串
如果不指定切片开始结束 string[:]
就是切len(string)
刀
字符串的逆序
string[-1::-1]
从-1向后切再向前移动直到头
公共方法
就是列表
元组
字符串
字典
都可以使用的方法
len()
统计元素个数
del()
删除变量 或者删除变量内部的元素
max()
显示容器中最大的值 字典只针对keys
min()
显示容器中最小的值 字典只针对keys
运算符
+
合并 * num
重复
可以合并 重复 str
list
tuple
字典不可以合并 字典使用update()
方法合并并且会去重
不能交叉合并 list + tuple
逻辑运算符
and
or
not
与 或 非
and
遇到假立即返回假
or
遇到真立即返回真
and自左向右扫描布尔表达式 如果所有表达式为真 则返回最后一个为真的表达式 如果有表达式为假 立即返回假并且不再向后执行
1 and 0 and 3/0 # 立即返回 0 并且不再向后执行除0错误
1 and "0" # 返回最后一个为真的表达式结果
or与and正好相反 自左向右扫描布尔表达式 如果所有表达式为假 则返回最后一个为假的表达式 如果有表达式为真 立即返回真并且不再向后执行
0 or 1 or 3/0 # 立即返回 1 并且不再向后执行除0错误
0 or "" # 返回最后一个为假的表达式结果
利用and or完成三元表达式
表达式 and 表达式 or 表达式
计算的是表达式的逻辑真假 输出的是表达式的结果
a = 25
b = 5
a > b and a or b # 计算and两边 左右都为真返回第二个真(a) 第二次计算 真(a) or b 立即返回真a的值
a < b and a or b # 计算and两边 左边为假立即返回假(a<b) 第二次计算 假(a<b) or b 返回真b的值
数学运算符
+
-
*
/
%
//
**
加 减 乘 除 求余 整除 幂运算
成员运算符
4 in [1,2,3,4]
返回 true
元素在列表内
4 not in (1,2,3)
返回 true
元素不在元组内
可以判断 str
list
tuple
dict
字典只针对keys
比较运算符
比较数据的值
==
!=
>
<
<=
>=
返回 True
False
可以比较 str
list
tuple
不能比较字典
注意字符串的比较符合以下规则 0 < A < a
身份运算符
比较数据的内存地址
is
is not
返回 True
False
a = [1,2,3]
b = [1,2,3]
a == b True
a is b False
变量进阶
变量
引用数据后变量就储存了数据的地址
变量记录数据的地址叫做引用
id()
可以查看变量中引用数据的内存地址
如果变量已经被赋值 再次赋值变量 本质上是修改变量的引用
函数的参数和返回值 都是通过引用传递
可变和不可变数据类型
内存地址不变的情况下的内容的可变和不可变
不可变类型
int
float
bool
complex
str
tuple
可变类型
list
dict
list
dict
的内容可以通过方法
改变 而且数据的地址不会发生改变
从内存地址的角度讲
不可变类型被创建后不可以改变内容
可变类型可以通过方法改变内容而内存地址不改变
hash()
哈希是一种算法 作用是提取数据的唯一特征码
哈希
只能接收不可变类型
的数据
创建字典的键值对时python会对key
进行hash
决定如何在内存中保存字典的数据
所以字典的key
必须是不可变数据类型
字典中字典和key
储存在一个内存空间中而value
储存在其他空间中 value
只是保存了字典的值数据的引用
局部变量
在函数中定义的变量无法在其他位置使用
不同函数内部可以定义相同名字的变量而不相互影响
全局变量
在函数外部定义的变量可以在函数内部使用
在函数内部定义已有全局变量名称的变量只是定义局部变量
global num
在函数内申明num是全局变量 然后对变量的重新赋值会修改全局变量
全局变量的命名一般加上 g_
或者 gl_
可变参数和不可变参数
函数的参数通过引用传递
在函数内部重新赋值形参变量只是定义局部变量 不会影响实参
在函数内部使用形参变量的方法改变变量数据时会修改外部的实参 可变类型数据
使用列表为参数传递给函数再在函数内部使用 +=
本质上属于 extend
方法会改变全局变量中列表的值
函数的缺省值
函数的某个参数相对固定时 可以设置一个缺省值
缺省参数应该放在函数形参的最后一个
如果函数有多个缺省参数 在调用指定缺省参数时需要输入完整缺省参数的形参名
多值参数
参数可以存放多个数据
*args
存放元组参数
**kwargs
存放字典参数
在函数外传入字典实参时候要使用key=value
不能使用key: value
类似 增加键值对的操作 dict["key"] = value
装包
如果需要将多个值传递给多值参数时装包可以简化参数的传递
形参 *tuple
**dict
实参 (1, 2, 3, name="Lee", age=18)
拆包
在函数内部针对形参使用*tuple
**dict
又可以拆包
字典
的拆包*kwargs
只得到key
**kwargs
得到key:value
a, b = (11, 22)
这也是拆包
操作文件的函数
file = open("文件名", "访问方式", encoding="UTF8")
打开文件 区分大小写 默认"r"
模式打开
文件如果存在 返回文件操作对象 文件如果不存在 抛出异常
在访问方式参数上加上b
代表用二进制方式打开文件 理论上open
可以打开任何文件 "rb"
"wb"
文件指针
文件指针标记了从哪个位置开始读取数据
第一次操作文件前指针默认指向文件开始的位置
操作文件的方法
read()
一次性读取所有内容 读取后文件指针会在文件末尾
readline()
一次读取一行内容 读取后文件指针会在下一行的开始
write()
将指定内容写入文件 写入后指针会在文件末尾
close()
关闭文件关闭文件句柄 fd
如果没有关闭文件 会消耗系统资源 并且影响后续对文件的访问
打开文件的参数
"r"
只读方式打开文件 文件指针将会放在文件的开头 如果文件不存在 抛出异常
"w"
只写模式打开文件 文件指针将会放在文件的末尾 如果文件存在会覆盖内容 如果文件不存在会创建文件并写入
"a"
追加只写模式打开文件 文件指针将会放在文件的末尾 如果文件存在会追加内容 如果文件不存在会创建文件并写入
"r+"
读写方式打开文件 文件指针将会放在文件的开头 如果文件不存在 抛出异常
"w+"
读写方式打开文件 文件指针将会放在文件的末尾 如果文件存在会覆盖内容 如果文件不存在会创建文件并写入
"a+"
追加读写方式打开文件 文件指针将会放在文件的末尾 如果文件存在会追加内容 如果文件不存在会创建文件并写入
分行读取文件
readline()
方法一次读取一行文件对象的内容 执行后文件指针会在读取行的末尾
readline
分行读取大文件降低内存压力
file = open("file_path", "r")
while True:
text = file.readline()
if not text:
break
print(text)
file.close()
复制文件
file_read = open("file_path", "r")
file_write = open("file_path_复件", "w")
text = file_read.read()
file_write.write(text)
file_read.close()
file_write.close()
分行复制文件
file_read = open("file_path", "r")
file_write = open("file_path_复件", "w")
while True:
text = file_read.readline()
file_write.write(text)
if not text:
break
file_read.close()
file_write.close()
文件/目录的常用管理操作
导入os
模块 import os
os.system("shell命令")
system方法
可以在python解释器中执行终端命令
文件操作
os.rename("文件名", "新文件名")
重命名文件
os.remove("文件名")
删除文件
目录操作
os.listdir("目录名")
查看目录内容 返回一个列表
os.mkdir("目录名")
创建目录
os.rmdir("目录名")
删除目录
os.getcwd()
获取工作目录
os.chdir("目标目录")
修改工作目录
os.path.isdir("文件路径")
判断是不是文件夹 返回bool
文本编码
python2.x
默认ascii
编码格式
python3.x
默认UTF-8
编码格式
UTF-8
是unicode
编码的一种编码格式
汉字在UTF-8
中占3字节
在*.py
第一行加入 # *-* coding:utf8 *-*
或者 # coding=utf8
指定解释器使用的编码
在遍历或者切片字符串时即使第一行指定了编码格式还是会出错 需要在UTF-8
编码格式的字符串前加上u"hello世界"
字符串的编码
string.encode("utf-8")
返回编码后的字符串 是一个utf8
的bytes
对象
data.decode("gbk")
把某种bytes
数据解码成gbk
编码的字符串
utf8
的bytes
与gbk
的bytes
长度不同
数据结构
链表(linkdelist)
链表
由一系列不必在内存中相连的结构构成 这些对象按线性顺序排序 每个结构含有表元素和指向后继元素的指针 最后一个单元的指针指向NULL 为了方便链表的删除与插入操作 可以为链表添加一个表头
栈(stack)
栈
是一种受限的线性表 仅允许在表的一端进行插入和删除运算 这一端被称为栈顶 另一端称为栈底 向一个栈插入新元素又称作进栈
或压栈
它是把新元素放到栈顶元素的上面 使之成为新的栈顶元素 从一个栈删除元素又称作出栈
或退栈
它是把栈顶元素删除掉 使其相邻的元素成为新的栈顶元素
队列(queue)
是一种特殊的线性表 是一种先进先出
的数据结构 只允许在表的前端进行删除操作
而在表的后端进行插入操作
进行插入操作的端称为队尾
进行删除操作的端称为队头
队列中没有元素时称为空队列
魔法属性
对象.__dict__
可以查看对象的所有属性
对象.__doc__
可以查看doc描述
对象.__class__
查看谁创建了该对象
对象.__moulde__
查看这个对象是由哪个模块创建的
def __new__(cls)
创建对象的内存空间
def __init__(self)
初始化实例对象的属性
new
和 init
一起完成了构造方法 构造方法包括创建内存和初始化属性
def __del__(self):
当对象被销毁时会自动调用此方法
def __call__(self)
当执行实例对象obj()
时 会调用__call__
方法
def __str__(self)
当print
对象时str
方法返回什么就打印什么 此外 "%s" % 对象
默认调用对象的str
方法
class Foo(object):
def __init__(self):
pass
def __str__(self):
return "test"
def __call__(self, num):
print(num)
def __del__(self):
print("death") # 当对象被自动销毁时会自动调用del方法
obj = Foo() # 当创建对象时会调用init方法
obj(10) # 当实例对象使用()时 调用的是call方法
print(obj) # 打印的结果就是str返回的字符串
with和上下文管理器
with as
一般用来简化操作文件时的清理工作
with
后面的代码返回的对象内必须有__enter__
方法和__exit__
方法 exit
方法一般包含"清理"代码(异常处理和退出)
with open("file_path", "r") as f: # 调用open返回的对象的enter方法然后把__enter__的返回值赋值给f
print(f) # 如果有异常会被f的exit方法捕获并且退出(出错也会关闭句柄)
with
后的代码被求值后返回一个对象并且这个对象的__enter__()
方法会被调用然后把enter
的返回值赋值给as
后的变量f
f
对象运行时不管异常还是完成运行都会调用f
的__exit__()
方法进行"清理"
上下文管理器
当一个对象被用作上下文管理器
时他创建的对象必须包含这两个方法:
-
__enter__
方法将在进入代码块前被调用(类似init
但是仅用作上下文管理器) -
__exit__
方法则在离开代码块之后被调用(即使在代码块中遇到了异常)
contextlib模块
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
with my_open("out.txt", "w") as f:
f.write("hello,the simplest context manager")
当装饰器为@contextmanager
标记为上下文管理器时
yield
之前的相当于上文yield
后的f
类似enter
返回的对象
yield
下面的相当于下文 用作执行完write
后的"清理"
isinstance()函数
isinstance(object, Classinfo)
判断obj
是不是Class
的实例 或者obj
是不是Class
的子类的实例 返回bool
判断对象是不是可迭代对象
collections
是python
内建的一个集合模块 包含许多集合类
Iterable
是 collections
模块中的一个类 所有可以迭代的对象
都来自Iterable
类
通过 isinstance(a, Iterable)
判断a
是不是一个可迭代对象
迭代器(iterator)
对于没有索引的数据类型 必须提供一种不依赖索引的迭代方式
在python
中实现了__iter__()
方法的对象是可迭代的
实现了__next__()
方法的对象是迭代器
所以让一个迭代器工作 至少要实现__iter__
方法和__next__
方法
如果一个类的对象想被用于for..in循环迭代
就必须实现一个__iter__()
方法 该方法返回一个迭代器对象
for循环
就会不断调用该迭代器对象的__next__()
方法拿到循环的下一个值 直到遇到StopIteration
错误时退出循环
可迭代对象和迭代器
class A(object): 可迭代对象
def __init__(self):
self.list = list()
def add(self, new):
self.list.append(new)
def __iter__(self):
return B(self.list)
class B(object): 迭代器
def __init__(self, list):
self.list = list
num = 0
def __iter__(self): # 此方法表示是一个可迭代对象 迭代器必须是一个可迭代对象
return None
def __next__(self):
if self.num < len(self.list):
ret = self.list[self.num]
self.num += 1
return ret
else:
raise StopIteration
可迭代对象必须包含__iter__
方法
__iter__
返回值必须返回一个迭代器
才可以实现迭代
包含__iter__
方法和__next__
方法的叫做迭代器
也是可迭代对象
for
循环或者next()
函数取值时会每次取对象的iter
方法所返回的对象里的next
方法实现迭代
简化实现迭代器
class Iterable_A(object):
def __init__(self):
self.list = [11, 22, 33]
self.num = 0
def __iter__(self):
return self
def __next__(self):
if self.num < len(self.list)
ret = self.list[self.num]
self.num += 1
return ret
else:
raise StopIteration
使用迭代器的好处
python2
中 range(x)
生成x
个元素的列表 占用大量
内存空间
xrange(x)
返回一个对象
这个对象保存了生成x个元素列表的方法
使用一个生成一个 节约内存
空间
python3
中已经用xrange
替换了range
生成器(generator)
yield
生成器的特点 让代码暂停
生成器
也是一个迭代器
(特殊的迭代器) 使用一次生成一个对象 节约内存空间
如果一个函数中有yield
语句 那么他会变为一个创建生成器的类
当for
循环这个生成器对象取值或者使用next(obj)
函数取值时 程序执行到yield
会返回yield
后的值并暂停
下次循环时会从yield
后开始执行 直到循环结束退出
obj.send(None)
同样具有next(obj)
的功能并且可以传入生成器一个值
send()
的参数如果不为None
则是把yield xx
这个整体当做一个表达式 并把参数传入表达式 然后可以赋值给yield xx
左边的变量
如果第一次启动生成器时就用send()
并且传入一个值 会报错
第一次执行暂停在yield xx
因此传入的值没有变量保存 所以启动生成器
时用next()
后续才可以使用send(msg)
传入参数
通过next()函数取值生成器
def fib(nums):
a = 0
b = 1
count = 0
while count < nums:
yield a
a, b = b, a+b
count += 1
return "done...."
nums_obj = fib(10)
while True:
try:
ret = next(nums_obj)
print(ret)
except Exception as x:
print(x.value)
break
此时nums_obj
是一个yield
模板创建的生成器对象 使用next()
函数取值生成器
如果要在迭代结束后返回一个值就在创建生成器的函数中使用return
返回内容
并且在next
取值结束时捕获异常
并调用捕获到异常的对象的value
属性就可以显示return
的结果了
next()函数
next()
函数可以取值迭代器
也可以取值生成器
迭代器: next(迭代器) 每次取值时执行迭代器
的__next__
方法并返回给外部的next()
生成器: next(生成器) 每次取值时 yield
把 a
的值返回给外部的 next()
send(msg)函数
send()
函数只可以取值生成器
send(msg)
的过程分为2步
-
msg
被传入函数内的yield a
这个整体
并赋值给yield a
左边的变量
-
yield
把a
的值返回给外部的send()
深拷贝浅拷贝
copy模块
import copy
deepcopy 可以完成深拷贝
b = copy.deepcopy(a)
copy 可以完成浅拷贝
d = copy.copy(c)
[11,22,[33,44]]
浅拷贝
可变数据类型只是拷贝第一层
到新的内存空间而嵌套的数据不拷贝还是仅仅是指向
深拷贝
可变数据类型时是递归拷贝所有数据到一个新的内存空间
(11,22,(33,44))
当拷贝一个完全是由不可变数据类型组成的数据时 浅拷贝和深拷贝都只是拷贝引用
(11,22,[33,44])
浅拷贝
一个不可变数据类型包含可变数据类型的数据时只是拷贝第一层
深拷贝
一个不可变数据类型只要其中包含任何可变数据类型
都会全部递归拷贝到一个新的内存空间
面相对象基础
面相过程
是以过程为中心编程思想 以什么正在发生为主要目标进行编程
面向对象
是把过程中的事物分解成各个对象并赋予对象在解决问题过程中事物的行为
面向对象的三大特点
封装
根据职责将对象的属性和方法封装到抽象的类中
继承
子类拥有父类的所有属性和方法从而实现代码的复用
多态
不同的子类的对象调用相同的父类方法产生不同的执行结果(通过重写父类方法)
面向对象的方法
dir(对象)
可以查看针对对象的所有方法和属性
print(对象)
显示对象是由哪个类创建的以及类的内存地址(前提是对象没有实现__str__
方法)
id(对象)
以十进制方式显示对象的内存地址
内置方法
__doc__
查看类内部的说明文档
__new__
为对象分配内存空间 return
父类的new
方法实现返回对象的引用为了后续传入init
方法self初始化对象属性
__init__
初始化实例对象的属性 用作定义实例属性
__del__
对象被从内存删除时会自动调用此方法
__str__
返回对象的描述信息 print
对象时输出指定字符串 配合return
返回字符串
类
具有相同属性或者方法的事物的统称 不能直接使用
类命名的时候要满足大坨峰命名法
被使用的类应该先开发
对象的属性可以是另一个类创建的对象 新兵的枪属性是由枪类创建的枪对象
python3.x
中所有类的父类都是object
类
新式类:以object
为基类的类
经典类:不以object
为基类的类
定义类
class Human(object):
def __new__(cls):
return super().__new__(cls)
def __init__(self, name):
self.name = name
self.age = 18
def work(self):
print("working")
在对象方法内部使用self.
可以访问和调用对象自己的属性和方法
在初始化方法中设置对象属性 self.name = "tom"
在主程序中临时设置对象属性 实例对象.name = "tom"
对象
由类
创建出来的一个具体存在
哪个类创建出来的对象就拥有哪个类包含的属性和方法
类
可以创建多个对象
多个对象的属性和方法互不干扰作用域为对象自己
创建对象的过程
man = Human("士兵")
实例化Human
__new__(cls)
为对象分配内存空间 返回对象的引用self
__init__(self, 参数....)
获取new
返回的对象引用
接收外部传入的参数 为实例对象初始化属性
单继承
class 类名(父类名):
子类拥有父类所有
的方法和属性
子类可以直接使用父类中已经封装好的属性和方法
子类应该继续根据职责封装子类特有的属性和方法
多继承
class 子类(父类1, 父类2,....):
多个父类有同名属性和方法 子类调用属性和方法依据 MRO方法解决顺序
执行
尽量避免多继承有同名属性和方法的父类
MRO方法解决顺序
print(类().__mro__)
查看类的方法搜索顺序
继承的传递性
子类拥有父类的属性和方法 子类的子类也拥有父类的父类的属性和方法
方法的重写
覆盖
父类的方法无法满足子类时在子类中重新定义同名方法 调用时会调用子类重写的方法不会调用原先父类的方法
扩展
在覆盖的基础上使用 super().方法()
调用父类方法实现 再添加扩展内容
重写new方法
class 类(object):
def __new__(cls): # 定义cls变量保存类的引用
return super(类, 类对象).__new__(cls) # cls 表示要让哪一个对象实现此方法
返回父类的new
方法实现得到当前类的实现对象的引用给cls
变量 变量内容会在new
执行完毕传递给当前类的self
如果返回的不是方法实现
而是一个其他类创建的实例引用
那么会执行其他类的init
方法 不会执行当前类的init
方法
super(类, 对象).init()
对象在类中的MRO列表
的下一个搜索类中init
的方法实现
私有属性和私有方法
通过名字重整
的方式改变设置的私有属性名而达到无法直接访问私有属性或方法的目的
无法在外界直接访问 只能在类范围中访问
self.__age
私有属性
self.__secret()
私有方法
强行访问私有属性demo._Women__age
强行访问私有方法demo._Women__secret()
子类的对象 不能在自己的方法内部 直接访问父类的私有属性或私有方法
子类的对象 可以通过继承自父类的公有方法 间接访问到父类的私有属性或私有方法
x
公有属性或方法
_x
属性或者方法 在使用import *
导入的时候不会被导入
__x
私有属性或方法 无法被继承
__x__
内置属性或方法 可以被继承
x_
属性或方法 用于避免名称与关键字名冲突 比如list_ = [1,2,3]
多态
是调用方法的技巧
不会影响类的内部设计
通过重写
或扩展
父类方法 以不同子类的对象 调用相同的父类方法产生不同的执行结果
实例对象
定义实例对象
obj = 类()
使用类()
创建实例对象的步骤有两步:
- 在内存中为对象分配空间
- 调用
init
方法为对象初始化属性
类创建出来的对象叫做类的实例
类创建实例对象的动作叫做实例化
实例对象的属性叫做实例属性
实例对象调用的方法叫做实例方法
实例对象各自拥有自己的实例属性 保存在各自的内存中
实例方法只有一份 保存在类对象
的内存中
实例对象通过self.
传递引用到类对象
实现自己的方法
在调用属性时把实例对象的引用传递到实例对象
的内部
在调用方法时把实例对象的引用传递到类对象
的内部
类对象
定义类对象
class 类(object):
类在运行时也会被加载到内存中 ==>类对象
类对象
的内存中保存着实例对象的方法
方法只有一份 实例分别传递引用调用方法
除了封装实例对象的属性和方法外 类对象还可以拥有自己的类属性
和类方法
类属性
针对类对象定义的属性 只用来描述类对象的特征
定义类属性
count = 0
使用赋值语句在类中定义类属性
调用类属性
类对象.类属性
-
实例.类属性()
不推荐 容易混淆实例.类属性 = value
会创建一个同名实例属性 而不会修改类属性
python
中属性的获取遵循向上查找机制
如果此类中没有该类属性会向上在父类中查找
实例在调用属性时在init
中找不到同名属性会到类中查找属性 但是访问的实际是类属性
类方法
针对类对象定义的方法 用来实现类对象的行为
要在类中使用类本身 就使用类方法 因为类方法的第一个参数就是类本身
定义类方法
@classmethod
def 方法名(cls):
使用装饰器@classmethod
声明这是一个类方法
类方法的第一个参数是cls
哪一个类调用的方法cls
就是哪一个类的引用
调用类方法
类对象.类方法()
类方法不需要实例化类就可以通过类对象来调用
静态方法
类中封装的方法没有访问实例属性
和类属性的需求
应该定义为静态方法
定义静态方法
@staticmethod
def 方法名():
使用修饰器@staticmethod
声明这是一个静态方法
静态方法的第一个参数不需要定义
方法内部无法
访问实例属性
和类属性
实例方法默认第一个参数是self
类方法默认第一个参数是cls
静态方法默认没有参数
调用静态方法
类.静态方法()
静态方法不需要实例化类就可以通过类来调用该方法
特点是不需要创建对象就可以调用 高效率
定义方法的规则
实例方法
---当方法内部需要访问实例属性和类属性
类方法
---当方法内部只需要访问类属性
静态方法
---当方法内部不需要访问实例属性和类属性
property装饰器
让一个类中的方法经过复杂计算后返回一个值并且在调用时变得简单直观
调用时直接使用对象.方法名
而不使用对象.方法()
调用一个方法像调用一个属性一样 并且property装饰器
让方法在调用时避免了是否要传参的疑问
新式类
中property装饰器
有三种分别get
set
和 del
@property
本身又会创建其他的装饰器@xxx.setter
@xxx.deleter
@property
用于get
返回的值 通过对象.方法名
get
@xxx.setter
用于set
变量的值 通过对象.方法名 = num
set
@xxx.deleter
用于del
设置的变量 通过 del 对象.方法名
del
class Goods(object):
def __init__(self):
self.original_price = 100
self.discount = 0.8
@property
def price(self):
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Goods()
obj.price # get价格
obj.price = 200 # set价格
del obj.price # 删除商品价格
通过类属性设置property
在类中property()
函数设置property属性
返回一个property对象
类属性BARBAR = property(fget=None, fset=None, fdel=None, doc=None)
property
函数有四个参数
第一个参数填入方法名
调用obj.BAR
时自动调用fset
设置的方法
第二个参数填入方法名
调用obj.BAR = xxx
时自动调用fset
设置的方法将xxx
传入第二个参数
第三个参数填入方法名
调用del obj.BAR
时自动调用fdel
方法
第四个参数填入字符串
Foo.BAR.__doc__
返回一个字符串对象
class Foo():
def get_bar(self):
bar = "python"
return bar
def set_bar(self, value):
bar = value
return bar
def del_bar(self):
del bar
BAR = property(get_bar, set_bar, del_bar, "description..")
obj = Foo()
obj.BAR # 调用property参数(将get_bar方法变为了property属性)
obj.BAR = "World" # 调用第二个参数
del obj.BAR # 调用第三个参数
desc = Foo.BAR.__doc__ # 获取第四个参数中设置的字符串
print(desc)
单例模式
让类创建的对象只有一个实例
对象的内存地址每次返回都是相同的
super()类
super()
调用父类方法实现的方式区别于直接使用父类名调用父类方法
通过C3算法
的结果保存在MRO
中 通过MRO顺序
可以避免钻石问题时实现一个父类方法会多次调用多继承的父类方法实现
C3算法
避免了多继承时子类多次调用父类方法实现
super().__init__(参数1,参数2,*args,**kwargs)
避免多继承报错 可以使用不定长参数接收参数
定义单例模式
class 类(object):
i = None # 设置空对象
def __new__(cls): # 实例化类时首先读取这行代码
if cls.i is None:
cls.i = super().__new__(cls)
return cls.i
return cls.i
单次初始化
class 类(object):
init_flag = False # 创建类属性初始设置为False
def __init__(self):
if 类.init_flag is False:
.........
类.init_flag = True # 当init执行后更改类属性值为True
return
创建类属性init_flag
标记是否执行过初始化动作
异常
解释器遇到错误会停止并提示错误信息
提示错误信息的动作叫做抛出raise
异常
错误信息的第一个单词是错误类型
捕获异常基本语法
try:
pass
except:
pass
捕获类型异常
try:
pass
except 错误类型:
pass
捕获未知异常
Exception
是一个错误的基类
except Exception
可以捕获所有预想到的异常以外的异常
except Exception as 变量:
把捕获到的异常赋值给as
后的变量
try:
pass
except Exception as 变量:
print(变量)
捕获异常的完整语法
try
语法中只有except
才会捕获异常 被捕获的异常不会继续传递 finally
不会终止传递异常
上下文管理器
中exit
方法类似于finally
虽然会执行一些代码 但是不会捕获异常
try:
pass # 尝试的代码
except:
pass # 有异常会执行的代码
except 错误类型1:
pass # 有错误类型1会执行的代码
except (错误类型2, 错误类型3):
pass # 有错误类型2 或 错误类型3 会执行的代码
except Exception as 变量: # 以上捕获异常都没有预料到的异常会在捕获未知异常里被捕获
print(变量) # 有未知异常时会执行的代码 打印错误信息
else:
pass # 尝试的代码没有异常会执行的代码
finally:
pass # 无论有没有异常都会执行的代码
异常的传递
当函数
或方法
出现错误 会将异常传递给 函数
或方法
的调用一方 当异常传递到主程序时仍没有处理异常 程序才会终止
为保证函数
或方法
中的代码简洁 一般在主程序中捕获异常
主动抛出raise
异常
针对特有的业务需求主动抛出异常
再由其他专门捕获异常的程序处理异常
- 函数中以条件抛出异常:
def xxx():
if (条件)xxx: # 设置当达到某个条件后就是异常
ex = Exception("自定义异常信息") # 创建异常包含此错误信息的异常
raise ex # 抛出异常
return xxx # 抛出异常后不会执行后续代码 如果没有异常return正常结果
- 捕获异常:
try:
xxx() # 内部抛出了异常 还需要捕获
except Exception as 变量: # 捕获xxx()传递的未知异常
print(变量) # 打印自定义异常信息
模块
导入模块
import 模块1
import 模块2 # (PEP8)
import 模块1, 模块2 # (不推荐使用)
如果变量中存储了模块名 直接使用import导入的是变量名.py 此时需要使用__import__()
来导入
__import__(变量名)
返回一个对象 这个对象就是模块的引用
使用getattr(obj, name[, default])
函数 返回一个对象的属性值
obj -- 对象。
name -- 字符串,对象属性。 name 只是名字 而返回值 是这个属性的引用
default -- 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。
module_ = ___import___(变量名)
method_ = getattr(module_, name)
导入模块时的顺序
1.官方标准模块
2.第三方模块
3.应用程序模块
首先从当前目录下搜索要导入的模块 再从系统目录搜索要导入的内置模块
模块.__file__
内置属性 可以查看模块的完整路径
sys
模块中sys.path
是一个列表其中的顺序表示导入模块时搜索模块的顺序
使用列表的sys.path.insert(0, "./")
来插入一个新模块包路径
使用sys.path.append("/")
来追加一个路径
程序在执行期间对程序导入过的模块的修改不会生效
需要在程序中使用 imp
模块中的reload()
方法来重新加载修改过的模块reload(模块)
导入一个模块的两个步骤 定义一个common
的变量 common
指向一个模块中的文件
from common import A
import common
common.A = 1
A = 1
这两种方式的区别在于
common.A = 1
会修改common
中A
的值为1
A = 1
让一个变量A
指向1
调用模块中的工具
模块.全局变量
模块.函数()
obj = 模块.类()
指定模块的别名
import 模块 as 模块别名
模块别名要满足大驼峰命名法
有重名冲突的导入时使用as
设置模块别名
导入部分工具
from 模块 import 工具
导入模块的指定工具 调用时直接使用导入的工具名
from 模块 import *
导入所有工具 调用时直接使用模块内的工具名
使用import
导入多个同名模块会执行后导入的模块内容
模块测试代码
模块被导入时所有没有缩进的代码都会被执行
模块.__name__
内置属性 可以输出模块名 如果在模块内部调用会输出__main__
在其他文件中执行输出模块名
if __name__ == "__main__":
判断__name__
被执行时是不是输出__main__
来决定是否输出下方测试代码
包(Package)
包含多个模块的特殊目录
包文件夹命名时遵循变量命名规则
import 包
可以导入包中所有的模块
在包目录下创建__init__.py
对外界提供包内模块的列表
__init__.py
from ./ import 模块1
from ./ import 模块2
发布模块
在包的同级目录下创建 setup.py
setup.py
from distutils.core import setup
setup(name="st_message", # 包名
version="1.0", # 版本号
description="发送和接收消息模块", # 描述信息
long_description="完整的发送和接收消息模块", # 完整描述信息
author="lee", # 作者
author_email="lee@lee.com", # 作者邮箱
url="lee.com", # 主页
py_modules=[
"st_message.send_message",
"st_message.receive_message"
])
构建模块
$ python3 setup.py build
会构建出需要打包发布的文件
生成发布压缩包
$ python3 setup.py sdist
会打包构建出的文件 使用不同解释器打包不同版本的发布压缩包
安装模块
$ tar -zxvf st_message.tar.gz
$ sudo python3 setup.py install
卸载模块
$ sudo rm -r /usr/local/lib/python3.7/dist-package/st_message*
安装python3第三方模块
pip3
是用来安装python3第三方模块的工具
$sudo pip3 install pygame # 安装模块
$sudo pip3 uninstall pygame # 卸载模块
$pip3 list # 查看安装的所有包
$pip3 freeze # 查看安装的所有包并显示版本号 用于迁移
$pip3 install django==1.8.2 # 指定包的版本
虚拟环境
安装虚拟环境插件
真实python环境的复制版本 只有Python的环境被复制
$sudo pip3 install virtualenv 安装虚拟环境
$sudo pip3 install virtualenvwrapper 安装虚拟环境扩展包 用于简化虚拟环境的使用
配置插件
编辑.bashrc
环境变量文件
export WORKON_HOME=$HOME/.virtualenvs # 设置虚拟环境文件位置
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 # 设置python位置
export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv # 设置virtualenv位置
source /usr/local/bin/virtualenvwrapper.sh # 加载virtualenvwrapper插件
运行source .bashrc
重新加载.bashrc
文件使设置生效
创建虚拟环境
$mkvirtualenv -p python3 环境名 # 创建python3虚拟环境
删除虚拟环境
$rmvirtualenv 环境名 # 删除指定虚拟环境
查看所有虚拟环境
$lsvirtualenv # 显示所有虚拟环境
切换虚拟环境
$workon 环境名 # 切换虚拟环境
退出虚拟环境
$deactivate # 退出虚拟环境
注意: 在虚拟环境
中不能使用sudo来安装 使用sudo pip
安装 会安装到真实python环境