速通python基础


python的安装过程网上太多了,这里就略过了,简单来说就是进入官网下载对应的平台版本就好了,下载之后加入到环境变量

在这里插入图片描述

然后安装pycharm作为主要编译器,进行后续的python开发工作。(破解过程网上自行搜索)

先进行两点说明:

  • python是鸭子类型,关注对象的行为而非具体类型,是动态类型
  • python使用缩进方式区分代码层级和代码块

一:pip国内镜像的设置

1:国内的镜像源

国内主要的镜像源有如下:

  • 阿里云 http://mirrors.aliyun.com/pypi/simple/
  • 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
  • 豆瓣 http://pypi.douban.com/simple
  • Python官方 https://pypi.python.org/simple/
  • v2ex http://pypi.v2ex.com/simple/
  • 中国科学院 http://pypi.mirrors.opencas.cn/simple/
  • 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
  • 中国科学技术大学 [http://pypi.mirrors.ustc.edu.cn/simple/
  • 华中理工大学:http://pypi.hustunique.com/
  • 山东理工大学:http://pypi.sdutlinux.org/

2:临时配置

临时使用某个镜像源下载:使用pip的 参数 -i 指定镜像源的地址

例如 想在阿里云镜像源下载Pillow包

pip install -i http://mirrors.aliyun.com/pypi/simple Pillow

3:永久配置

windows下,在user目录中创建一个pip文件夹,如下图

在这里插入图片描述

在pip文件夹中新建文件pip.ini,内容如下

防止一个镜像失效,这里设置了多个镜像,你也可以添加上面列出的其它镜像

[global]
timeout=40
index-url=http://mirrors.aliyun.com/pypi/simple/
extra-index-url=
        https://pypi.tuna.tsinghua.edu.cn/simple/
        http://pypi.douban.com/simple/
        http://pypi.mirrors.ustc.edu.cn/simple/
[install]
trusted-host=
        pypi.tuna.tsinghua.edu.cn
        mirrors.aliyun.com
        pypi.douban.com
        pypi.mirrors.ustc.edu.cn

将配置添加到环境变量path中

在这里插入图片描述

终端输入pip config list测试下

在这里插入图片描述

二:Hello Python

1:input和print

先介绍两个最基本的函数,用来进行输入和输出,分别是 input()print()

input() -> 可以指定输入前提示的话语,然后输入的结果可以通过其他的转换函数进行转换(例如int(), float())

print() -> 输出指定的内容,可以使用f或者format()函数进行辅助的格式化操作

# 格式化输出
# print("xxx %d / %f / %s" % (var_name))

price = float(input("请输入苹果的价格: "))
number = int(input("请输入要购买的苹果的个数: "))

money = price * number

print("价格是: %0.2f, 个数是: %d, 需要%0.2f元" % (price, number, money))


# format
print("我叫{},今年{}岁。".format("小蜜",18))
#我叫小蜜,今年18岁。
#花括号的个数决定了,参数的个数。但是花括号的个数可以少于参数。
print("我喜欢{}和{}".format("乒乓球","羽毛球","敲代码"))

2:基本控制语句

条件控制语句 -> if

基本语法就是 if -> elif -> else,直接上例子

score = input("请输入成绩:")
if int(score) >= 90:
    print("优秀")
elif int(score) >= 80:
    print("良好")
elif int(score) >= 70:
    print("一般")
elif int(score) >= 60:
    print("差")
else:
    print("不及格")

循环控制语句 -> for

基本语法一:for index in range([start, ]end[, step])

  • 循环的起始位置(默认是0),结束位置,和步长(默认是1)
  • 包含左不包含右,左开右闭[start, end)
for i in range(9): # 循环9次,等价于 for i in range(0, 9, 1)
    print("the number is: {}".format(i))  # 0, 1, 2, 3, 4, 5, 6, 7, 8
for j in range(2, 20, 3): # 循环2到20之间,步长为3
    print("the number is: {}".format(j))  #  2, 5, 8, 11, 14, 17

基本语法二:可以为字符串,列表,元组,集合,字典等进行遍历操作

这里直接上例子,等学到对应的数据结构的时候就知晓这块了

# 字符串的遍历
str = "hello"
for c in str:
    print("当前的字符是:{}".format(c))
for index in range(len(str)):
    print("当前的字符是:{}".format(str[index]))
# 列表的遍历
list = ["hello", "world", "python"]
for item in list:
    print("当前的列表项是:{}".format(item))
for index in range(len(list)):
    print("当前的列表项是:{}".format(list[index]))
# 元组的遍历
tuple = ("hello", "world", "python")
for item in tuple:
    print("当前的元组项是:{}".format(item))
for index in range(len(tuple)):
    print("当前的元组项是:{}".format(tuple[index]))
# 集合的遍历
set = {"hello", "world", "python"}
for item in set:
    print("当前的集合项是:{}".format(item))
# 字典的遍历
dict = {
    "name": "hello",
    "age": 18,
    "sex": "man"
}
for key in dict:
    print("当前的字典项是:{}".format(dict[key]))
for key in dict.keys():
    print("当前的字典项是:{}".format(dict[key]))
for value in dict.values():
    print("当前的字典项是:{}".format(value))
for key, value in dict.items():
    print("当前的字典项是:{}".format(value))

循环语句 -> while(循环条件)..do(循环内容)

直接上例子:

number = 10
while number > 0:
    print(number)  # 10, 8, 6, 4, 2
    number = number - 2

3:int和float

python中没有int和long的区分,整数是没有小数部分的数字。在Python中,整数具有无限的精度,这意味着你可以表示非常大的或非常小的整数,而不会丢失精度

而浮点数包含小数部分,用于表示实数。在Python中,浮点数通常使用C语言中的double类型来实现。你可以通过查看sys.float_info来获取你的程序运行所在机器上浮点数的精度和内部表示法

int() -> 用于将一个数字或字符串转换为一个纯整数。这个函数接受两个参数,第一个参数为数字或字符串,第二个可选参数为进制数,默认为10,表示十进制

float() -> 用来将一个整数或字符串转换为浮点数。当从字符串转换时,字符串需要表示一个合法的浮点数

a = 123.56
print(int(a))  # 123
b = "123"
print(int(b))  # 123
c = "1A"
print(int(c, 16))  # 26
d = "0x1A"
print(int(d, 16))  # 26

e = 123
print(float(e))  # 123.0
f = "123.56"
print(float(f))  # 123.

三:基本数据结构

1:字符串str

基本的四点要知道

  • 可以通过[index]获取指定位置的字符,可以for循环得到字符
  • len(s) => s的长度
  • s.count(s1) => s1在s中的个数
  • 支持切片

切片?

在Python中,切片(slicing)是用于从序列类型(如列表、元组、字符串等)中提取一部分元素的一种操作。切片操作通过使用冒号(:)分隔的开始索引、结束索引和步长来实现

sequence[start:stop:step]
  • start 是切片开始的位置的索引,如果省略,默认为序列的开头。
  • stop 是切片结束的位置的索引,如果省略,默认为序列的末尾。
  • step 是切片的步长,即每次跳跃的间隔,默认为1。
# string 相关
# 可以通过[index]获取指定位置的字符,可以for循环得到字符
s = "hello world"
print("index 2 is: {}, index -1 is {}".format(s[2], s[-1]))  # index 2 is: l, index -1 is d

# 最常用的函数就是 -> len(s) & s.count(s1)
# len(s) => s的长度
# s.count(s1) => s1在s中的个数
s = "aaabbbaaa"
for i in range(len(s)):
    print(s[i])
print("count a in s is: {}".format(s.count("a")))  # 6

# 支持切片
print(s[1 : 7 : 2])  # abb

字符串方法

大小写相关方法

方法说明
s.capitalize()首字母大写,其他的字母小写
s.upper()全部字母大写
s.lower()全部字母小写
s.title()每一个单词的首字母大写,其他的字母小写,title化
# ----- 大小写相关操作 --------
s = "helLO woRLd"
print(s.capitalize())  # 首字母大写 Hello world
print(s.title())  # 每一个单词的首字母大写 title化 Hello World
print(s.upper())  # 全部大写 HELLO WORLD
print(s.lower())  # 全部小写 hello world

查找相关方法

方法说明
s.find(subString, [start, end])从左到右查找子字符串,起始查找位置是start,默认是0,结束查找位置是end
s.rfind(subString, [start, end])从右到左查找子字符串,起始查找位置是start,默认是0,结束查找位置是end
s.index(subString, [start, end])从左到右查找子字符串,起始查找位置是start,默认是0,结束查找位置是end
s.rindex(subString, [start, end])从右到左查找子字符串,起始查找位置是start,默认是0,结束查找位置是end

⚠️ find找不到返回 -1,index找不到将会异常

# ------ 查找相关find & index ------
# find找不到返回 -1,index找不到将会异常
s = "aaabbbccc"
print(s.find("bbb"))
try:
    print(s.find("ddd"))
except:
    print("这个是异常抛出, find不会触发")

print(s.index("bbb"))
try:
    print(s.index("ddd"))
except:
    print("这个是异常抛出, index会触发")
# 可以通过指定的参数确定查找的范围
s = "aaabbbcccdddeeebbb"
print(s.find("bbb")) # 默认开始位置是 0
print(s.find("bbb", 6)) # 从index为6位置开始往后找,后面的不看
print(s.index("bbb", 6)) # 同理
# 可以使用对应的 rfind(s1, start_index) & rindex(s1, start_index)从后向前找
print(s.rfind("bbb"))
print(s.rindex("bbb"))

性质判断相关方法

方法说明
s.startswith(subString)字符串s是否以指定的子字符串substring开头,如果是,返回true,否则返回false
s.endswith(subString)字符串s是否以指定的子字符串substring结尾,如果是,返回true,否则返回false
s.isalpha()字符串s是否完全是由字母构成,如果是,返回true,否则返回false
s.isdigit()字符串s是否完全是由数字构成,如果是,返回true,否则返回false
s.isalnum()字符串s是否完全是由字母和数字构成,如果是,返回true,否则返回false
s.isspace()字符串s是否完全是由空格构成,如果是,返回true,否则返回false
# ------- 性质判断 ---------
s = "hello python"
if s.startswith("he"):
    print ("s是以he开头的")
else:
    print ("s不是以he开头的")

s = "hello python"
if s.endswith("world"):
    print ("s是以world结尾的")
else:
    print ("s不是以world结尾的")
    
print(s.isalpha()) # 是不是完全由字母构成
print(s.isdigit()) # 是不是完全由数字构成
print(s.isalnum()) # 是不是完全由字母或者数字构成
print(s.isspace()) # 是不是由空格构成的

格式化相关操作

方法说明
s.center(len, fillchar)居中对齐补充到len长度,左右填充的字符是fillchar
s.rjust(len, fillchar)右对齐补充到len长度,左侧填充的字符是fillchar
s.ljust(len, fillchar)左对齐补充到len长度,右侧填充的字符是fillchar
s.zfill(len)使用0进行填充,一般用于数字的格式化
# ----- 格式化字符串 -------
s = "22"
print(s.center(10, "x")) # 通过x将s填充至10位,并且s原本的内容居中
s1 = "222222222222"
print(s1.center(10, "x")) # 通过len(s1) > 10将不会填充,也不会截取(将不做任何的操作)
print(s.rjust(5, "0")) #右对齐,不足的位置补充0 -> 00022
print(s.ljust(5, "0")) #左对齐,不足的位置补充0 -> 22000
print("33".zfill(5))  # 00033 使用0进行填充
print("-33".zfill(5)) # -0033 使用0进行填充
a = 2
b = 3
print("%d * %d = %d" % (a, b, a * b)) # 格式化输出
print("{} * {} = {}".format(a, b, a * b)) # 同上
# f打头的是一个占位符,会被变量对应的值将其替换
print(f"{a} * {b} = {a * b}")
# 其他的格式
a = 3.1415926
print("{:.2f}".format(a)) # 保留两位小数 3.14

在这里插入图片描述

编码和解码相关方法

  • encode("encodingtype")
  • bytes.decode("codingtype")
方法说明
encode(“encodingtype”)使用指定的字符集进行编码,返回对应的byte[]
bytes.decode(“codingtype”)使用指定的字符集进行解码
s = "李小龙"
# 编码成为指定的字节序列
a = s.encode("utf-8")
b = s.encode("gbk")
print("a is {}".format(a))
print("b is {}".format(b))
print(type(a))
print(type(b))
# 字节序列解码为对应的字符串
c = a.decode("utf-8")
d = b.decode("gbk")
print("c is {}".format(c))
print("d is {}".format(d))
print(type(c))
print(type(d))

修剪,拆分/合并,替换相关方法

方法说明
strip() & rstrip() & lstrip()去掉左右两端空格、右边空格,左边空格
replace(old, new, [count])将old字符串替换成为new字符串,替换次数为count个,如果没有指定count,全部替换
split([split_op], [count])使用split_op对字符串进行拆分,拆分操作执行count次,如果没有指定count,全部拆分
join_op.join(list)使用join_op将list各个列表项拼接在一起,返回字符串
# ------- 修剪,合并,替换-------
s = "   hello world   \t\r\n"
print(s.strip()) # strip() -> 去除左右两端的空格,"hello world" -> lstrip() & rstrip()

s = "hello world"
print(s.replace("o", "@")) # 将全部的 o 替换成为 @ -> "hell@ w@rld"
print(s.replace("o", "@", 1)) # 将第一个遇到的 o 替换成为 @ -> "hell@ world"
# 通过指定的内容进行拆分, 如果不指定默认是用空格进行拆分,拆分后自动形成列表,可以指定拆分次数
s = "hello world java python"
l = s.split()
l = s.split(",", 3) # 用 ","进行三次分割
print(l)
print(", ".join(l)) # 使用join()进行合并列表成为字符串

2:列表list

列表是一个大口袋,什么数据类型的数据都可以放在里面,然后可以通过索引获取指定的位置的数据,如果索引越界,将会报错

# 列表可以存储任何类型的元素
list1 = ["hello", "world", (1, 2), {"x": 1, "y": 2, "z": 3}, 12]
print(list1[2][0])  # while print (1, 2)中的第一个元素
print(list1[3]["y"]) # while print {"x": 1, "y": 2, "z": 3} 中 y 对应的值
print(list1[-1]) # 如果是负数将会倒着往前数,最后一个元素是 -1
# 如果访问的位置越界,将会报错
try: 
    print(list1[30])
except:
    print("如果访问的位置, 超出下标位置将会报错")

下面是常见方法

方法属类说明
list.append(list_item)增加单一列表项添加到列表中
list.extend(iter)增加将一个可以迭代的内容追加到后面
list.insert(index, list_item)增加在指定位置添加列表项
list[index] = new_list_item修改直接在指定的index修改对应的列表项
del[index]删除删除指定索引的元素
list.remove(list_item)删除删除第一个出现的指定的数据
list.pop([index])删除删除最后一个内容,如果指定索引,删除指定索引处的内容
list.clear()删除直接清空列表
len(list)统计返回列表的长度
list.count(data)统计统计data在列表项中出现的次数
list.sort()排序从小到大排序
list.sort(reverse=True)排序从大到小排序
list.reverse()排序将当前的列表中的内容倒序排序 [a, c, b] -> [b, c, a]
list.index(index)查询返回指定index的内容
list[index]查询返回指定index的内容
list[start:end:step]切片返回[start, end)范围内,步长为step的切片结果
# 列表相关的操作

# =========== 增加 ==========
list1.append("python") # list.append(data)在列表的尾部进行追加
print(list1)

list1.extend((1, 2, 3, 4)) # list.append(iter)将一个可以迭代的内容追加到后面
print(list1)

list1.insert(2, {"name": "cuihaida", "age": 24}) # list.insert(index, data)在指定位置添加Object
print(list1)

# ============ 修改 ============
list1[-1] = 100 # 直接让指定的位置为指定的内容
print(list1)

# =========== 删除  ============
del(list1[-1]) # del(list[index])
print(list1)

list1.remove(2) # 删除第一个出现的指定的数据 list.remove(data)
print(list1)

list1.pop() # 删除最后一个内容,如果指定索引,删除指定索引处的内容 list.pop([index])
print(list1)

list1.clear() # 清空列表
print(list1)

# =========== 统计 =============
list2 = list(range(10))
list2.extend(list(range(5)))
print(list2) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4]

print(len(list2)) # 统计数组的长度 len(list)

print(list2.count(0), list2.count(7)) # 统计指定数据的长度 list.count(data)

# ====== 排序 ==========
list2.sort()
print(list2) # 从小到大排序

list2.sort(reverse = True)
print(list2) # 从大到小排序

list2.reverse()
print(list2) # 将当前的列表中的内容翻转

# ======== 查询 ==========
print(list2.index(2)) # list.index(index)
print(list2[-1]) # 可以通过[index]查询

# ==== 循环 =========
list1 = ["hello", "world", (1, 2), {"x": 1, "y": 2, "z": 3}, 12]
for data in list1:
    print("data is: {}, data type is {}".format(data, type(data)))


# 切片[start : end : step] step为负数的时候往前数

numbers = list(range(13))
print(numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
print(numbers[ : : 2]) # [0, 2, 4, 6, 8, 10, 12]
print(numbers[1 : : 2]) # [1, 3, 5, 7, 9, 11]
print(numbers[ : : -1]) # 最常见的列表倒转  [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(numbers[len(numbers) : : -2]) # [12, 10, 8, 6, 4, 2, 0]

3:元组tuple

元组的内容不可以被改变

在声明元组的时候,如果元组的值只有一个,要在后面加上一个,

元组只有两个方法:tuple.count(data) & tuple.index(data)

# 元组, 元组中的内容不可以被修改
tuple1 = (1, 2, 3, "hello", "world", [1, 2], {"x": 1, "y": 2}, (1, 2))
for elem in tuple1:
    print("the elem is {}, the type is {}".format(elem, type(elem)))

# 不允许进行修改   
# tuple1[2] = "test"  # TypeError: 'tuple' object does not support item assignment

# 在声明元组的时候,如果元组的值只有一个,要在后面加上一个逗号(,),否则会认为是int类型

a = (1)
b = (1,)
print(type(a)) # <class 'int'>
print(type(b)) # <class 'tuple'>

# 支持的两个方法
# count(data) -> 返回元组中data的个数, 
# index(data) -> 返回元组中data的首次出现的位置
tuple2 = (1, 1, 2, 4, 5, 1, 1)
print(tuple2.count(1))
print(tuple2.index(1), tuple2.index(4))

# list <-> tuple
tuple3 = (1, 2, 3, 4, 5)
list3 = list(tuple3)
print(list3)
print(type(list3))

tuple4 = tuple(list3[::-1])
print(tuple4)
print(type(tuple4))

4:集合set

集合的三大特点:无序性,互异性,确定性

先简单的介绍下集合的基本用法

set1 = {1, 2, 3, 4, 3, 2, 1}
print(set1)  # {1, 2, 3, 4}

# 字符串转集合
set2 = set("hello")
print(set2)  # {'e', 'l', 'h', 'o'}

# 将列表转成集合,可以去掉集合中的重复的元素
list1 = [1, 2, 3, 3, 2, 1, 4]
set3 = set(list1)
print(set3)

# 创建集合的生成器语法
# 在1 - 19中如果是3的倍数或者是5的倍数将方到set中
set4 = (num for num in range(1, 20) if num % 3 == 0 or num % 5 == 0)
print(set4)
for elem in set4:
    print(elem)  # [3, 5, 6, 9, 10, 12, 15, 18]

集合中的运算

成员运算 in

  • in:当前的成员在集合中将返回true,不在将返回false
  • not in:当前的成员不在集合中将返回true,在将返回false
set1 = {1, 2, 3, 4, 5, 7, 8}
print(9 in set1) # False
print(9 not in set1) # True
print(2 in set1) # True
print(2 not in set1) # False

并(|)、交(&)、差(-)、对称差(^)运算

  • & <-> set1.intersection(set2)
  • | <-> set1.union(set2)
  • - <-> set1.difference(set2)
  • ^ <-> set1.symmetric_difference(set2)
set1 = {1, 2, 3, 4, 5, 6, 7}
set2 = {2, 4, 6, 8, 10}

# 交集 -> 二者都有
print(set1 & set2)  # {2, 4, 6}
print(set1.intersection(set2))  # {2, 4, 6}

# 并集 -> 二者所有的内容构成的集合
print(set1 | set2)  # {1, 2, 3, 4, 5, 6, 7, 8, 10}
print(set1.union(set2))  # {1, 2, 3, 4, 5, 6, 7, 8, 10}

# 差集 -> a有b没有
print(set1 - set2)  # {1, 3, 5, 7}
print(set1.difference(set2))  # {1, 3, 5, 7}

# 对称差 -> 并集 - 交集(两者在互相的集合中并没有出现过)
print(set1 ^ set2)  # {1, 3, 5, 7, 8, 10} -> 1, 3, 5, 7只在set1中有,8, 10只在set2中有
print(set1.symmetric_difference(set2))  # {1, 3, 5, 7, 8, 10}
print((set1 | set2) - (set1 & set2))  # {1, 3, 5, 7, 8, 10}

子集(<)和超集(>)运算

如果集合A中的元素在集合B中都存在,同时集合B中的元素在集合A中都存在,此时称A == B

如果集合A中的元素在集合B中都存在,则称A是B的子集,B是A的超集。同时如果A != B, 那么称A是B的真子集

自己是自己的子集

自己是自己的超集

  • set2 > set1 <-> set2.issuperset(set1)
  • set1 < set2 <-> set1.issubset(set2)
set1 = {1, 2, 3}
set2 = {1, 2, 3, 4}

print(set1 == set2)  # False
print(set1.issuperset(set1))  # True
print(set1.issubset(set1))  # True
print(set2.issuperset(set1))  # True
print(set1.issubset(set2))  # True
print(set1 < set2) # set1是不是set2的真子集
print(set1 <= set2) # set1是不是set2的子集
print(set2 > set1) # set2是不是set1的超集

集合中的其他常用方法如下:

方法说明
set.add(data)添加一个元素到集合中
set.update(Otherset)更新集合
set.discard(element)从集合中移除某一个元素,即使这个元素不存在也没事
set.remove(element)从集合中移除某一个元素,如果元素不存在会报错,所以如果使用这个,最好先用in测试下在不在
set.pop()随机删除一个元素,如果原集合为空将会抛出keyError
set.clear()清空集合
set1.isdisjoint(set2)set1和set2是不是有相同的元素,没有返回True, 有相同的返回False
if __name__ == '__main__':
    # 创建一个set
    set1 = set()

    # add(key) -> Add an element to a set.
    set1.add(10)
    set1.add(20)
    print(set1)

    # update(Otherset) -> Update a set with the union of itself and others
    set1.update({1, 2, 3, 4})
    print(set1)

    # discard(element) -> Remove an element from a set if it is a member.
    # element不在集合中也不会报错,此时将不做任何处理
    set1.discard(3)
    print(set1)

    # remove(element) -> Remove an element from a set; it must be a member.
    # element不在集合中将会抛出KeyError异常,所以使用前最好使用成员运算符进行计算
    if 10 in set1:
        set1.remove(10)
    else:
        pass

    # pop() -> Remove and return an arbitrary set element.
    #         Raises KeyError if the set is empty.
    # 随机删除一个元素,如果原集合为空将会抛出keyError
    set1.pop()
    print(set1)

    # clear() -> Remove all elements from this set
    # 清空集合
    set1.clear()
    print(set1)
    
    # set1.isdisjoint(set2) -> set1和set2是不是有相同的元素,没有返回True, 有相同的返回False
    set2 = {1, 3, 5, 7, 9}
    set3 = {2, 4, 6, 8, 10}
    set4 = {1, 2, 3, 4, 5}
    print(set2.isdisjoint(set3))  # True
    print(set2.isdisjoint(set4))  # False

不可变集合frozenset

不可以添加和删除(就想列表和元组之间的关系),其他的使用方法和set几乎相同

if __name__ == '__main__':
    set1 = frozenset({1, 3, 5, 7})
    set2 = frozenset({1, 6})
    print(set1 & set2)
    print(set1 | set2)
    print(set1 - set2)
    print(set1 ^ set2)
    print(set1.isdisjoint(set2))

5:字典dict

在这里插入图片描述

# 字典 dict {} 无序的 key/value

dict1 = {
    "name": "xiaoming",
    "age": 12,
    "gender": "man"
}

# ========== 查 ============
# 字典的取值 dict.[key] & dict.get(key)
print (dict1["name"])
print (dict1["age"])
print (dict1["gender"])
print (dict1.get("name"))

# 所有键,所有值,所有键值对
print(dict1.keys())  # dict_keys(['name', 'age', 'gender'])
print(dict1.values()) # dict_values(['xiaoming', 12, 'man'])
print(dict1.items()) # dict_items([('name', 'xiaoming'), ('age', 12), ('gender', 'man')])
for key in dict1.keys():
    print(key)
for value in dict1.values():
    print(value)
for item in dict1.items():
    print(item[0] + " -> " + str(item[1]))

# ======= 删除 ==========
del dict1["gender"] # del dict[key] 将指定key对应的key/value删除
print(dict1.keys())
dict1["gender"] = "woman" # 添加对应的键值对
print(dict1.keys())
dict1.pop("age") # dict.pop(key) 将指定key对应的key/value删除
print(dict1.keys())
dict1.popitem() # 随机删除一个item
print(dict1.keys())
dict1.clear() # 清空字典
print(dict1.keys())


# 更新和新增
dict2 = {
    "name": "xiaoming",
    "age": 12,
    "gender": "man"
}

dict3 = {
    "name": "xiaoming",
    "height": 176,
    "weight": 70
}
# dict[key] = value 如果key存在则更新value,否则新增key/value
# dict.setdefault(key, value) 如果key存在则不进行操作,否则新增key/value
# dict_1.update(dict_2) # 将dict_2的数据合并到dict_1
dict2["age"] = 17 # key存在,更新
dict3.setdefault("like", "listen music") # 没有这个key, 将会将key / value添加到dict3中
dict2.update(dict3) # 合并
for item in dict2.items():
    print(item[0] + " -> " + str(item[1]))

四:常用模块介绍

1:math模块

可以通过*dir(math)*来查看有哪些方法

模块常量

常量描述
math.e返回欧拉数 (2.7182…)
math.inf返回正无穷大浮点数
math.nan返回一个浮点值 NaN (not a number)
math.piπ 一般指圆周率。 圆周率 PI (3.1415…)
math.tau数学常数 τ = 6.283185…,精确到可用精度。Tau 是一个圆周常数,等于 2π,圆的周长与半径之比。
import math


def math_test():
    print(math.e)  # 2.718281828459045
    print(math.pi)  # 3.141592653589793
    print(math.nan)  # nan
    print(math.inf)  # inf
    print(math.tau)  # 6.283185307179586


if __name__ == '__main__':
    math_test()

模块方法

方法描述
三角函数相关
math.acos(x)返回 x 的反余弦,结果范围在 0 到 pi 之间。
math.acosh(x)返回 x 的反双曲余弦值。
math.asin(x)返回 x 的反正弦值,结果范围在 -pi/2 到 pi/2 之间。
math.asinh(x)返回 x 的反双曲正弦值。
math.atan(x)返回 x 的反正切值,结果范围在 -pi/2 到 pi/2 之间。
math.atan2(y, x)返回给定的 X 及 Y 坐标值的反正切值,结果是在 -pi 和 pi 之间。
math.atanh(x)返回 x 的反双曲正切值。
math.cos(x)返回 x 弧度的余弦值。
math.cosh(x)返回 x 的双曲余弦值。
math.sin(x)返回 x 弧度的正弦值。
math.sinh(x)返回 x 的双曲正弦值。
math.sqrt(x)返回 x 的平方根。
math.tan(x)返回 x 弧度的正切值。
math.tanh(x)返回 x 的双曲正切值。
math.degrees(x)将角度 x 从弧度转换为度数。
math.hypot()从原点到坐标给定点的向量长度。三角形的斜边长
math.radians(x)将角度 x 从度数转换为弧度。
对指定的x进行操作
math.ceil(x)将 x 向上舍入到最接近的整数
math.floor(x)将数字向下舍入到最接近的整数
math.copysign(x, y)返回一个基于 x 的绝对值和 y 的符号的浮点数。
math.erf(x)返回一个数的误差函数
math.erfc(x)返回 x 处的互补误差函数
math.exp(x)返回 e 的 x 次幂, 其中 e是自然对数的基数。
math.expm1(x)返回 Ex - 1, e 的 x 次幂,Ex,其 e是自然对数的基数。
math.fabs(x)返回 x 的绝对值。
math.factorial(x)返回 x 的阶乘。 如果 x 不是整数或为负数时则将引发 ValueError
math.frexp(x)(m, e) 对的形式返回 x 的尾数和指数。
m 是一个浮点数, e 是一个整数,正好是 x == m * 2^e 。
如果 x 为零,则返回 (0.0, 0) ,否则返回 0.5 <= abs(m) < 1
math.gamma(x)返回 x 处的伽马函数值。
math.isfinite(x)判断 x 是否有限,如果 x 既不是无穷大也不是 NaN,则返回 True ,否则返回 False 。
math.isinf(x)判断 x 是否是无穷大,如果 x 是正或负无穷大,则返回 True ,否则返回 False 。
math.isnan()判断数字是否为 NaN,如果 x 是 NaN(不是数字),则返回 True ,否则返回 False 。
math.isqrt()将平方根数向下舍入到最接近的整数
math.ldexp(x, i)返回 x * 2i 。 这基本上是函数 math.frexp() 的反函数。
math.lgamma()返回伽玛函数在 x 绝对值的自然对数。
math.log(x[, base\])使用一个参数,返回 x 的自然对数(底为 e )。
math.log10(x)返回 x 底为 10 的对数。
math.log1p(x)返回 1+x 的自然对数(以 e 为底)。
math.log2(x)返回 x 以 2 为底的对数
math.trunc(x)返回 x 截断整数的部分,即返回整数部分,删除小数部分
x和y操作
math.fmod(x, y)返回 x/y 的余数
math.gcd(x, y)返回给定的整数参数的最大公约数。
math.fmod(x, y)返回 x/y 的余数
math.pow(x, y)将返回 x 的 y 次幂。
math.remainder(x, y)返回 IEEE 754 风格的 x 除于 y 的余数。
可迭代元素
math.fsum(iterable)返回可迭代对象 (元组, 数组, 列表, 等)中的元素总和,是浮点值。
math.prod(iterable)计算可迭代对象中所有元素的积。
排列和组合
math.comb(n, k)返回不重复且无顺序地从 n 项中选择 k 项的方式总数。
math.perm(n, k=None)返回不重复且有顺序地从 n 项中选择 k 项的方式总数。
其他
math.isclose(a,b)检查两个值是否彼此接近,若 a 和 b 的值比较接近则返回 True,否则返回 False。。
math.dist(p, q)返回 p 与 q 两点之间的欧几里得距离,以一个坐标序列(或可迭代对象)的形式给出。

2:JSON模块

python Object -> JSON:json.dumps(python_object)

PythonJSON
dictobject
list, tuplearray
strstring
int, float, int- & float-derived Enumsnumber
Truetrue
Falsefalse
Nonenull

JSON -> python Object:json.loads(json_str)

JSONPython
objectdict
arraylist
stringstr
number (int)int
number (real)float
trueTrue
falseFalse
nullNone
import json
 
# Python 字典类型转换为 JSON 对象
data1 = {
    'no' : 1,
    'name' : 'Runoob',
    'url' : 'http://www.runoob.com'
}
 
json_str = json.dumps(data1)
print ("Python 原始数据:", repr(data1))
print ("JSON 对象:", json_str)
 
# 将 JSON 对象转换为 Python 字典
data2 = json.loads(json_str)
print ("data2['name']: ", data2['name'])
print ("data2['url']: ", data2['url'])


# 写入 JSON 数据
with open('data.json', 'w') as f:
    json.dump(data, f)

# 读取数据
with open('data.json', 'r') as f:
    data = json.load(f)

3:os模块

os就是operating system的缩写,顾名思义,os模块提供的就是各种 Python 程序与操作系统进行交互的接口。

通过使用os模块,一方面可以方便地与操作系统进行交互,另一方面页可以极大增强代码的可移植性。

如果该模块中相关功能出错,会抛出OSError异常或其子类异常。

⚠️

  • 如果是读写文件的话,建议使用内置函数open()
  • 如果是路径相关的操作,建议使用os的子模块os.path
  • 如果要逐行读取多个文件,建议使用fileinput模块;
  • 要创建临时文件或路径,建议使用tempfile模块;
  • 要进行更高级的文件和路径操作则应当使用shutil模块。

当然,使用os模块可以写出操作系统无关的代码并不意味着os无法调用一些特定系统的扩展功能

要切记一点:一旦这样做就会极大损害代码的可移植性。

⚠️ 千万不要为了图调用省事儿而将os模块解包导入,即不要使用from os import 来导入os模块;否则os.open()将会覆盖内置函数open(),从而造成预料之外的错误。

⚠️ os模块中大多数接受路径作为参数的函数也可以接受“文件描述符”作为参数。文件描述符:file descriptor,在 Python 文档中简记为 fd,是一个与某个打开的文件对象绑定的整数,可以理解为该文件在系统中的编号。

3.1:常用功能
功能属性说明
os.name返回当前的操作系统环境:posix(linux & max) & nt(windows)
os.environ返回环境相关的信息,主要是各类环境变量
os.walk(top)需要传入一个路径作为top参数,函数的作用是在以top为根节点的目录树中游走
对树中的每个目录生成一个由(dirpath, dirnames, filenames)三项组成的三元组。
os.listdir()列出(当前)目录下的全部路径(及文件)。
该函数存在一个参数,用以指定要列出子目录的路径,默认为“.”,即“当前路径”。
os.mkdir()新建一个目录
os.remove()用于删除文件,如果指定路径是目录而非文件的话,就会抛出IsADirectoryError异常
os.rename()该函数的作用是将文件或路径重命名
一般调用格式为os.rename(src, dst),即将src指向的文件或路径重命名为dst指定的名称。
os.getcwd()获取当前工作路径
os.chdir(new_dir)切换当前工作路径为指定路径
def get_filelists(file_dir='.'):
    list_directory = os.listdir(file_dir)
    filelists = []
    for directory in list_directory:
        # os.path 模块稍后会讲到
        if(os.path.isfile(directory)):
            filelists.append(directory)
            return filelists
3.2:os.file相关

其实这个模块是os模块根据系统类型从另一个模块导入的,并非直接由os模块实现

  • os.name值为nt,则在os模块中执行import ntpath as path
  • os.name值为posix,则导入posixpath

使用该模块要注意一个很重要的特性:os.path中的函数基本上是纯粹的字符串操作。

换句话说,传入该模块函数的参数甚至不需要是一个有效路径,该模块也不会试图访问这个路径,而仅仅是按照“路径”的通用格式对字符串进行处理。

更进一步地说,os.path模块的功能我们都可以自己使用字符串操作手动实现,该模块的作用是让我们在实现相同功能的时候不必考虑具体的系统,尤其是不需要过多关注文件系统分隔符的问题。

os.path.join()

这是一个十分实用的函数,可以将多个传入路径组合为一个路径。

实际上是将传入的几个字符串用系统的分隔符连接起来,组合成一个新的字符串,所以一般的用法是将第一个参数作为父目录,之后每一个参数即使下一级目录,从而组合成一个新的符合逻辑的路径。

但如果传入路径中存在一个“绝对路径”格式的字符串,且这个字符串不是函数的第一个参数,那么其他在这个参数之前的所有参数都会被丢弃,余下的参数再进行组合。

更准确地说,只有最后一个“绝对路径”及其之后的参数才会体现在返回结果中。

>>> os.path.join("just", "do", "python", "dot", "com")
'just\\do\\python\\dot\\com'
>>> 
>>> os.path.join("just", "do", "d:/", "python", "dot", "com")
'd:/python\\dot\\com'
>>> 
>>> os.path.join("just", "do", "d:/", "python", "dot", "g:/", "com")
'g:/com'

os.path.abspath()

将传入路径规范化,返回一个相应的绝对路径格式的字符串。

也就是说当传入路径符合“绝对路径”的格式时,该函数仅仅将路径分隔符替换为适应当前系统的字符,不做其他任何操作,并将结果返回。

所谓“绝对路径的格式”,其实指的就是一个字母加冒号,之后跟分隔符和字符串序列的格式:

>>> os.path.abspath("a:/just/do/python")
'a:\\just\\do\\python'
>>> # 我的系统中并没有 a 盘

当指定的路径不符合上述格式时,会自动获取当前工作路径,并组合成为一个新的路径字符串。

示例如下:

>>> os.path.abspath("ityouknow")
'D:\\justdopython\\ityouknow'

os.path.basename()

该函数返回传入路径的“基名”,即传入路径的最下级目录。

>>> os.path.basename("/ityouknow/justdopython/IAmBasename")
'IAmBasename'
>>> # 我的系统中同样没有这么一个路径。可见 os.path.basename() 页是单纯进行字符串处理

整这个函数要注意的一点是,返回的“基名”实际上是传入路径最后一个分隔符之后的子字符串

如果最下级目录之后还有一个分隔符,得到的就会是一个空字符串:

>>> os.path.basename("/ityouknow/justdopython/IAmBasename/")
''

os.path.dirname()

与上一个函数正好相反,返回的是最后一个分隔符前的整个字符串:

>>> os.path.dirname("/ityouknow/justdopython/IAmBasename")
'/ityouknow/justdopython'
>>> 
>>> os.path.dirname("/ityouknow/justdopython/IAmBasename/")
'/ityouknow/justdopython/IAmBasename'

os.path.split()

函数os.path.split()的功能就是将传入路径以最后一个分隔符为界,分成两个字符串,并打包成元组的形式返回;

前两个函数os.path.dirname()os.path.basename()的返回值分别是函数os.path.split()返回值的第一个、第二个元素。

就连二者的具体实现都十分真实:

def basename(p):
    """Returns the final component of a pathname"""
    return split(p)[1]


def dirname(p):
    """Returns the directory component of a pathname"""
    return split(p)[0]

通过os.path.join()函数又可以把它们组合起来得到原先的路径。

os.path.exists()

这个函数用于判断路径所指向的位置是否存在。若存在则返回True,不存在则返回False

>>> os.path.exists(".")
True
>>> os.path.exists("./just")
True
>>> os.path.exists("./Inexistence") # 不存在的路径
False

一般的用法是在需要持久化保存某些数据的场景,为避免重复创建某个文件

需要在写入前用该函数检测一下相应文件是否存在,若不存在则新建,若存在则在文件内容之后增加新的内容。

os.path.isabs()

该函数判断传入路径是否是绝对路径,若是则返回True,否则返回False

仅仅是检测格式,同样不对其有效性进行任何核验:

>>> os.path.isabs("a:/justdopython")
True

os.path.isfile() & os.path.isdir()

这两个函数分别判断传入路径是否是文件或路径

此处会核验路径的有效性,如果是无效路径将会持续返回False

4:日期和时间模块

4.1:time模块

时间间隔是以秒为单位的浮点小数。

每个时间戳都以自从 1970/1/1 00:00:00 经过了多长时间来表示。

time.time()

获取当前的时间的时间戳

import time

if __name__ == '__main__':
    get_time = time.time()
    print(get_time)

时间戳单位最适于做日期运算。

但是1970年之前的日期就无法以此表示了。太遥远的日期也不行,UNIX和Windows只支持到2038年。

很多Python函数用一个元组装起来的9组数字处理时间:

序号字段
04位数年2008
11 到 12
21到31
3小时0到23
4分钟0到59
50到61 (60或61 是闰秒)
6一周的第几日0到6 (0是周一)
7一年的第几日1到366 (儒略历)
8夏令时-1, 0, 1, -1是决定是否为夏令时的旗帜

上述也就是struct_time元组。这种结构具有如下属性:

序号属性
0tm_year2008
1tm_mon1 到 12
2tm_mday1 到 31
3tm_hour0 到 23
4tm_min0 到 59
5tm_sec0 到 61 (60或61 是闰秒)
6tm_wday0到6 (0是周一)
7tm_yday一年中的第几天,1 到 366
8tm_isdst是否为夏令时,值有:1(夏令时)、0(不是夏令时)、-1(未知),默认 -1

获取当前的时间

import time

localtime = time.localtime(time.time())
print ("本地时间为 :", localtime)

在这里插入图片描述

格式化时间

方法说明
time.asctime()
time.strftime(format[, t])可以根据需求选取各种格式
  • %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 本地A.M.或P.M.的等价符

  • %U 一年中的星期数(00-53)星期天为星期的开始

  • %w 星期(0-6),星期天为星期的开始

  • %W 一年中的星期数(00-53)星期一为星期的开始

  • %x 本地相应的日期表示

  • %X 本地相应的时间表示

  • %Z 当前时区的名称

  • %% %号本身

import time

local_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(local_time)  # "2016-04-07 10:29:46"

time相关方法

方法说明
time.altzone返回格林威治西部的夏令时地区的偏移秒数
time.asctime([tupletime])接受时间元组并返回一个可读的形式
time.clock()用以浮点数计算的秒数返回当前的CPU时间。用来衡量不同程序的耗时
time.ctime([secs])作用相当于asctime(localtime(secs)),未给参数相当于asctime()
time.gmtime([secs])接收时间戳并返回格林威治天文时间下的时间元组
time.localtime([secs]接收时间戳并返回当地时间下的时间元组
time.mktime(tupletime)接受时间元组并返回时间戳
time.sleep(secs)推迟调用线程的运行,secs指秒数
time.strftime(fmt[,tupletime])接收以时间元组,并返回以可读字符串表示的当地时间,格式由fmt决定
time.strptime(str,fmt=‘%a %b %d %H:%M:%S %Y’)根据fmt的格式把一个时间字符串解析为时间元组
time.time()返回当前时间的时间戳(1970纪元后经过的浮点秒数)
time.tzset()根据环境变量TZ重新初始化时间相关设置
time.perf_counter()返回计时器的精准时间,包含整个系统的睡眠时间
time.process_time()返回当前进程执行 CPU 的时间总和,不包含睡眠时间
time.timezone当地时区(未启动夏令时)距离格林威治的偏移秒数
time.tzname包含一对根据情况的不同而不同的字符串

Python 3.8 已移除 clock() 方法 可以使用 time.perf_counter() 或 time.process_time() 方法替代。

import time

def procedure():
	time.sleep(2.5)

def t():
	t0 = time.perf_counter()
	procedure()
	print(time.perf_counter() - t0)

if __name__ == "__main__":
    t()
4.2:calendar模块
方法说明
calendar.calendar(year,w=2,l=1,c=6)返回一个多行字符串格式的year年年历,3个月一行,间隔距离为c
calendar.firstweekday()返回当前每周起始日期的设置,默认情况下,首次载入caendar模块时返回0,即星期一
calendar.isleap(year)是闰年返回 True,否则为 False
calendar.leapdays(y1,y2)返回在Y1,Y2两年之间的闰年总数
calendar.month(year,month,w=2,l=1)返回一个多行字符串格式的year年month月日历,两行标题,一周一行
calendar.monthcalendar(year,month)返回一个整数的单层嵌套列表。每个子列表装载代表一个星期的整数
calendar.monthrange(year,month)返回两个整数。第一个是该月的星期几,第二个是该月有几天
calendar.setfirstweekday(weekday)设置每周的起始日期码。0(星期一)到6(星期日)
calendar.timegm(tupletime)time.gmtime相反:接受一个时间元组形式,返回该时刻的时间戳
calendar.weekday(year,month,day)返回给定日期的日期码。0(周一)到6(周日)。月份为 1到 12
import calendar

print(calendar.calendar(2025, w=2, l=1, c=6, m=3))  # 输出2025年的日历
print(calendar.firstweekday())  # 0代表周一,1代表周二
print(calendar.isleap(2025))  # 是否为闰年
print(calendar.leapdays(2000, 2025))  # 2000年到2025年之间闰年的天数 -> 7

print(calendar.month(2025, 1, w=2, l=1))  # 输出2025年1月的日历
print(calendar.monthcalendar(2025, 2))  # 返回一个整数的单层嵌套列表。每个子列表装载代表一个星期的整数
print(calendar.monthrange(2025, 3))  # 返回(5,31),5表示周五,31表示3月31天
print(calendar.setfirstweekday(0))  # 设置周一为第一天

print(calendar.weekday(2025, 3, 1))  # 返回3月1日是周几,0代表周一,1代表周二,以此类推

5:正则模块

5.1:两个核心方法

re.match() & re.search()

re.match()尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none

re.match(pattern, string, flags=0)
参数描述
pattern匹配的正则表达式
string要匹配的字符串。
flags标志位,用于控制正则表达式的匹配方式

匹配成功re.match方法返回一个匹配的对象,否则返回None。

我们可以使用group(num)groups() 匹配对象函数来获取匹配表达式。

匹配对象方法描述
group(num=0)匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
groups()返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
import re
 
line = "Cats are smarter than dogs"
# . 表示任意匹配除换行符(\n、\r)之外的任何单个或多个字符
# (.?) 表示"非贪婪"模式,只保存第一个匹配到的子串
matchObj = re.match( r'(.) are (.?) .', line, re.M|re.I)
 
if matchObj:
   print ("matchObj.group() : ", matchObj.group()) #  Cats are smarter than dogs
   print ("matchObj.group(1) : ", matchObj.group(1)) # Cats
   print ("matchObj.group(2) : ", matchObj.group(2)) # smarter
else:
   print ("No match!!")

re.search扫描整个字符串并返回第一个成功的匹配

⚠️ re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None,而 re.search 匹配整个字符串,直到找到一个匹配。举一个简单的例子

import re
 
line = "The Cats are smarter than dogs"
# . 表示任意匹配除换行符(\n、\r)之外的任何单个或多个字符
# (.?) 表示"非贪婪"模式,只保存第一个匹配到的子串
match_obj = re.match( r'Cats are (.?) .', line, re.M|re.I )
search_obj = re.search( r'Cats are (.?) .', line, re.M|re.I )
 
if match_obj:
   print ("matchObj.group() : ", match_obj.group()) 
   print ("matchObj.group(1) : ", match_obj.group(1))
else:
   print ("No match!!") # match将匹配不到,因为字符串的开头不是这个Cats

# z
if search_obj:
    print ("searchObj.group() : ", search_obj.group())  # Cats are smarter than dogs
    print ("searchObj.group(1) : ", search_obj.group(1)) # smarter
else:
    print ("No Search!!")
5.2:检索和替换方法

re.sub -> 匹配之后执行替换,count配置最大替换次数,flags配置匹配模式

re.sub(pattern, repl, string, count=0, flags=0)
  • pattern : 正则中的模式字符串。
  • repl : 替换的字符串,也可为一个函数。
  • string : 要被查找替换的原始字符串。
  • count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。(可选)
  • flags : 编译时用的匹配模式,数字形式。(可选)
import re
 
phone = "2004-959-559 # 这是一个电话号码"
 
# 删除注释
num = re.sub(r'#.$', "", phone)
print ("电话号码 : ", num)
 
# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)


# -------------- repl是一个函数的情况 -------------
import re
 
# 将匹配的数字乘于 2
def double(matched):
    value = int(matched.group('value'))
    return str(value  2)
 
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s))  # A46G8HFD1134

re.compile() -> 编译正则表达式,生成一个正则表达式(Pattern)对象,供 match()search() 这两个函数使用。

re.compile(pattern[, flags])
  • pattern : 一个字符串形式的正则表达式
  • flags 可选,表示匹配模式,比如忽略大小写,多行模式等
>>>import re
>>> pattern = re.compile(r'\d+')                    # 用于匹配至少一个数字
>>> m = pattern.match('one12twothree34four')        # 查找头部,没有匹配
>>> print( m )
None
>>> m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
>>> print( m )
None
>>> m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配
>>> print( m )                                        # 返回一个 Match 对象
<_sre.SRE_Match object at 0x10a42aac0>
>>> m.group(0)   # 可省略 0
'12'
>>> m.start(0)   # 可省略 0
3
>>> m.end(0)     # 可省略 0
5
>>> m.span(0)    # 可省略 0
(3, 5)

在上面,当匹配成功时返回一个 Match 对象,其中:

  • group([group1, …]) 方法用于获得一个或多个分组匹配的字符串
    • 当要获得整个匹配的子串时,可直接使用 group()group(0)
  • start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为0;
  • end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),默认值为0;
  • span([group]) 方法返回 (start(group), end(group))

re.findall(pattern, string) -> 字符串中找到正则表达式所匹配的所有子串,并返回一个列表:

  • 如果有多个匹配模式,则返回元组列表
  • 如果没有找到匹配的,则返回空列表
  • match 和 search 是匹配一次 findall 匹配所有
import re
 
result1 = re.findall(r'\d+','runoob 123 google 456')
 
pattern = re.compile(r'\d+')   # 查找数字
result2 = pattern.findall('runoob 123 google 456')
result3 = pattern.findall('run88oob123google456', 0, 10)
 
print(result1) # ["123", "456"]
print(result2)  # ["123", "456"]
print(result3)  # ["88", "12"]

# 多个匹配的将返回元祖
import re

result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
print(result) # [('width', '20'), ('height', '10')]

re.finditer -> 在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

re.finditer(pattern, string, flags=0)
import re
 
it = re.finditer(r"\d+","12a32bc43jf3") 
for match in it: 
    print (match.group() )

re.split -> 正则切分

re.split(pattern, string[, maxsplit=0, flags=0])
参数描述
pattern匹配的正则表达式
string要匹配的字符串。
maxsplit分割次数,maxsplit=1 分割一次,默认为 0,不限制次数。
flags标志位,用于控制正则表达式的匹配方式
>>>import re
>>> re.split('\W+', 'runoob, runoob, runoob.')
['runoob', 'runoob', 'runoob', '']
>>> re.split('(\W+)', ' runoob, runoob, runoob.') 
['', ' ', 'runoob', ', ', 'runoob', ', ', 'runoob', '.', '']
>>> re.split('\W+', ' runoob, runoob, runoob.', 1) 
['', 'runoob, runoob, runoob.']
 
>>> re.split('a', 'hello world')   # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
['hello world']
3:flags可选标志
修饰符描述
re.I使匹配对大小写不敏感
re.L做本地化识别(locale-aware)匹配
re.M多行匹配,影响 ^ 和 $
re.S使 . 匹配包括换行在内的所有字符
re.U根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

模式如下

模式描述
^匹配字符串的开头
$匹配字符串的末尾。
.匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...]用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’
[^...]不在[]中的字符:[^abc]匹配除了a,b,c之外的字符。
re匹配0个或多个的表达式。
re+匹配1个或多个的表达式。
re?匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{n}匹配n个前面表达式。
re{n,}精确匹配n个前面表达式。
re{n, m}匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
`ab`
(re)匹配括号内的表达式,也表示一个组
(?imx)正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx)正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re)类似 (…), 但是不表示一个组
(?imx: re)在括号中使用i, m, 或 x 可选标志
(?-imx: re)在括号中不使用i, m, 或 x 可选标志
(?#...)注释.
(?= re)前向肯定界定符。
(?! re)前向否定界定符。
(?> re)匹配的独立模式,省去回溯。
\w匹配数字字母下划线
\W匹配非数字字母下划线
\s匹配任意空白字符,等价于 [\t\n\r\f]。
\S匹配任意非空字符
\d匹配任意数字,等价于 [0-9]。
\D匹配任意非数字
\A匹配字符串开始
\Z匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
\z匹配字符串结束
\G匹配最后匹配完成的位置。
\b匹配一个单词边界,也就是指单词和空格间的位置。
\B匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
\n, \t匹配一个换行符。匹配一个制表符, 等
\1...\9匹配第n个分组的内容。
\10匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。

特殊字符

实例描述
.匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用象 ‘[.\n]’ 的模式。
\d匹配一个数字字符。等价于 [0-9]
\D匹配一个非数字字符。等价于 [^0-9]
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\w匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]'。
\W匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]’。

6:多线程模块

6.1:Thread执行线程的对象

构造一个新的线程对象,其构造函数的参数如下

'''
group: 目前始终为 `None`,用于将来实现线程组时使用。
target: 指定线程启动时需要调用的可调用对象(函数或方法)。常用于指定线程要执行的任务。
name: 指定线程的名称。如果为 `None`,则系统自动分配一个以 "Thread-N" 开头的名字。便于调试和管理。
args: 传递给 `target` 函数的位置参数元组。用于传递参数给线程执行的函数。
kwargs: 传递给 `target` 函数的关键字参数字典。用于传递参数给线程执行的函数。
daemon: 指定线程是否为守护线程。守护线程会在主线程结束后自动结束,非守护线程则会阻止主线程的结束。
'''
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
import threading
import time


def run(n):
    print('task', n)
    time.sleep(1)
    print('2s')
    time.sleep(1)
    print('1s')
    time.sleep(1)
    print('0s')
    time.sleep(1)


if __name__ == '__main__':
    # target是要执行的函数名(不是函数)
    # args是函数对应的参数,以元组的形式存在
    t1 = threading.Thread(target=run, args=('t1',))
    t2 = threading.Thread(target=run, args=('t2',))
    t1.start()
    t2.start()

join() 方法用于阻塞当前线程,直到调用 join() 的线程执行完毕。常用于在主线程中等待其他子线程结束后再继续执行

import threading
import time
 
def delayed_print():
    time.sleep(2)
    print("Delayed print")

# 创建一个子线程t,执行的方法是delayed_print
t = threading.Thread(target=delayed_print)
t.start()
 
print("Waiting for the thread to finish...")
t.join() # 阻塞主线程,等待t线程执行完成后再操作
print("Thread has finished execution")
6.2:线程同步

线程同步用于协调多个线程对共享资源的访问,避免数据竞争和不一致性。当多个线程同时访问和修改共享资源时,可能会导致数据不一致,线程同步机制可以确保同一时间只有一个线程能够访问共享资源,从而保证数据的一致性

锁(Lock)是一种用于线程同步的原语,用于确保同一时间只有一个线程能访问某个特定资源。

Lock只有两个状态:locked 和 unlocked。当一个线程获取到锁时,锁的状态变为 locked,其他线程尝试获取该锁时会被阻塞,直到该锁被释放。

import time
from threading import Thread, Lock


def work():
    global n
    lock.acquire() # 增加互斥锁
    temp = n
    time.sleep(0.1)
    n = temp - 1
    lock.release() # 释放互斥锁


if __name__ == '__main__':
    lock = Lock()
    n = 100
    l = []
    for i in range(100): # 创建100个线程,准备测试线程同步
        p = Thread(target=work)
        l.append(p) 
        p.start()
    for p in l:
        p.join()
import threading
 
lock = threading.Lock()
shared_resource = 0
 
def increment():
    global shared_resource
    with lock: # 类似于java的synchronized
        local_copy = shared_resource
        local_copy += 1
        shared_resource = local_copy
        print(f"Resource value: {shared_resource}")
        
threads = []
for i in range(5):
    t = threading.Thread(target=increment)
    threads.append(t)
    t.start()
    
for t in threads:
    t.join()

RLock(可重入锁)允许同一个线程多次获取同一个锁,每次获取锁后必须释放相同次数的锁。

这样可以避免在同一线程内多次获取锁时发生死锁

import threading
 
rlock = threading.RLock()
shared_resource = 0
 
def increment():
    global shared_resource
    with rlock:
        with rlock:
            local_copy = shared_resource
            local_copy += 1
            shared_resource = local_copy
            print(f"Resource value: {shared_resource}")
            
threads = []
for i in range(5):
    t = threading.Thread(target=increment)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

Lock: 一个线程如果多次获取同一个 Lock,会导致死锁。适用于简单的线程同步情况。

RLock: 允许同一个线程多次获取锁,必须释放相同次数的锁。适用于复杂的线程同步情况,需要在同一线程内多次获取锁。

6.3:死锁

死锁是一种情况,当两个或多个线程互相等待对方释放资源时,会导致所有线程都被无限期地阻塞。

简单来说,死锁是线程在等待中无法继续执行的状态

import threading
 
lock1 = threading.Lock()
lock2 = threading.Lock()
 
def task1():
    with lock1:
        print("Task 1 acquired lock 1")
        with lock2:
            print("Task 1 acquired lock 2")

def task2():
    with lock2:
        print("Task 2 acquired lock 2")
        with lock1:
            print("Task 2 acquired lock 1")

thread1 = threading.Thread(target=task1)
thread2 = threading.Thread(target=task2)
 
thread1.start()
thread2.start()
 
thread1.join()
thread2.join()

如果task1拿到lock1后等待 lock2,同时 task2 拿到 lock2 后等待 lock1,就会发生死锁

6.4:信号量同步机制

信号量用于控制对共享资源的访问数量。它维护一个计数器,每次 acquire 时减少计数器,每次 release 时增加计数器。

计数器不能小于零,当计数器为零时,试图 acquire 的线程会被阻塞

import threading
import time
 
semaphore = threading.Semaphore(3)
 
def access_resource():
    with semaphore:
        print(f"Resource accessed by {threading.current_thread().name}")
        time.sleep(1)

threads = []
for i in range(10):
    t = threading.Thread(target=access_resource, name=f"Thread-{i+1}")
    threads.append(t)
    t.start()

for t in threads:
    t.join()
6.5:事件对象

事件对象用于线程间通信。一个线程等待事件发生,另一个线程触发事件。

事件对象有一个内部标志,初始值为 False

当调用 set() 方法时,内部标志变为 True,所有等待该事件的线程被唤醒。

当调用 clear() 方法时,内部标志变为 False

import threading
import time

event = threading.Event()

# 线程通信,类比于java的wait()和notify()
def wait_for_event():
    print(f"{threading.current_thread().name} waiting for event")
    event.wait() # 阻塞线程,直到事件被触发
    print(f"{threading.current_thread().name} event triggered")


def trigger_event():
    time.sleep(2)
    print("Event will be triggered")
    event.set() # 触发事件

# 创建5个线程,执行wait_for_event()函数
threads = [threading.Thread(target=wait_for_event, name=f"Thread-{i + 1}") for i in range(5)]
for t in threads:
    t.start()
# 创建一个线程,执行trigger_event()函数
trigger_thread = threading.Thread(target=trigger_event)
trigger_thread.start()

for t in threads:
    t.join()
trigger_thread.join()

在这里插入图片描述

6.6:Condition:条件变量

条件变量允许线程在满足特定条件时被唤醒。它通常与锁(Lock 或 RLock)一起使用,用于复杂的线程间同步问题

import threading
import time

condition = threading.Condition()  # 条件变量,在满足特定条件时被唤醒
shared_resource = []


def producer():
    """
    生产者函数,负责在共享资源中生产项目。
    该函数会不断检查共享资源中的项目数量,并在数量少于5时生产新的项目。
    """
    # 声明全局变量shared_resource,用于表示共享资源
    global shared_resource

    while True:
        # 使用条件变量condition来同步共享资源的访问
        with condition:
            # 当共享资源中的项目数量少于5时,生产新项目
            if len(shared_resource) < 5:
                # 计算新项目的值,这里简单地使用当前项目数量加1
                item = len(shared_resource) + 1
                # 将新项目添加到共享资源中
                shared_resource.append(item)
                # 打印生产了哪个项目
                print(f"Produced item {item}")
                # 通知所有等待的线程,共享资源的状态已经改变
                condition.notify_all()
        # 每生产一个项目后,暂停1秒,模拟生产时间
        time.sleep(1)


def consumer():
    """
    消费者函数,用于模拟消费行为。

    本函数会不断地尝试从共享资源中消费项目。如果共享资源为空,
    则等待生产者函数通过条件变量通知有新的资源可用。
    """
    global shared_resource  # 定义全局变量shared_resource,用于表示共享资源
    while True:  # 模拟一个持续消费的过程
        with condition:  # 使用条件变量来同步
            while not shared_resource:  # 如果共享资源为空
                condition.wait()  # 消费者等待,释放条件变量并等待生产者的通知
            item = shared_resource.pop(0)  # 从共享资源中取出第一个项目进行消费

            print(f"Consumed item {item}")  # 打印消费的项目
        time.sleep(1)  # 模拟消费时间,每次消费后等待1秒


producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

producer_thread.join()
consumer_thread.join()

五:文件操作

1:文件打开-open()

用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数

如果该文件无法被打开,会抛出 OSError。

⚠️使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

–> 前两个参数最常用

  • file: 必需,文件路径(相对或者绝对路径)。
  • mode: 可选,文件打开模式
  • buffering: 设置缓冲
  • encoding: 一般使用utf8
  • errors: 报错级别
  • newline: 区分换行符
  • closefd: 传入的file参数类型
  • opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

常用的mode模式有:

模式描述
t文本模式 (默认)。
x写模式,新建一个文件,如果该文件已存在则会报错。
b二进制模式。
+打开一个文件进行更新(可读可写)。
U通用换行模式(不推荐)。
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb以二进制格式打开一个文件用于只读。
文件指针将会放在文件的开头。
这是默认模式。
一般用于非文本文件如图片等。
r+打开一个文件用于读写。文件指针将会放在文件的开头。
rb+以二进制格式打开一个文件用于读写。
文件指针将会放在文件的开头。
一般用于非文本文件如图片等。
w打开一个文件只用于写入。
如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。
如果该文件不存在,创建新文件。
wb以二进制格式打开一个文件只用于写入。
如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。
如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+打开一个文件用于读写。
如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。
如果该文件不存在,创建新文件。
wb+以二进制格式打开一个文件用于读写。
如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。
如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a打开一个文件用于追加。
如果该文件已存在,文件指针将会放在文件的结尾。
如果该文件不存在,创建新文件进行写入。
ab以二进制格式打开一个文件用于追加。
如果该文件已存在,文件指针将会放在文件的结尾。
如果该文件不存在,创建新文件进行写入。
a+打开一个文件用于读写。
如果该文件已存在,文件指针将会放在文件的结尾。
文件打开时会是追加模式。
如果该文件不存在,创建新文件用于读写。
ab+以二进制格式打开一个文件用于追加。
如果该文件已存在,文件指针将会放在文件的结尾。
如果该文件不存在,创建新文件用于读写。

2:文件关闭-close()

用于关闭一个已打开的文件。关闭后的文件不能再进行读写操作, 否则会触发ValueError错误。

close()方法允许调用多次。

当file对象被引用到操作另外一个文件时,会自动关闭之前的file对象。

使用close()方法关闭文件是一个好的习惯。

f = open("aa.txt", "wb") # 使用二进制写的方式打开aa.txt
print("文件名为: ", f.name)

# 关闭文件
f.close()

3:刷新缓冲区-flush()

用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。

一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用flush()方法。

f = open("aa.txt", "wb")
print ("文件名为: ", f.name)

# 刷新缓冲区
f.flush()

# 关闭文件
f.close()

4:文件描述符-fileno()

返回一个整型的文件描述符(file descriptor FD 整型),可用于底层操作系统的 I/O 操作。

f = open("aaa.txt", "wb")
print ("文件名为: ", f.name)

fid = f.fileno()
print ("文件描述符为: ", fid)

# 关闭文件
f.close()

5:终端设备-isatty()

检测文件是否连接到一个终端设备,如果是返回 True,否则返回 False

f = open("aaa.txt", "wb")
print ("文件名为: ", f.name)

ret = f.isatty()
print ("返回值 : ", ret)

# 关闭文件
f.close()

6:文件读取-read()

用于从文件读取指定的字符数(文本模式t)或字节数(二进制模式b)

如果未给定参数size或size为负数则读取文件所有内容。

#  ----> 假设aaa.text的文件内容如下
这是第一行
这是第二行
这是第三行
这是第四行
这是第五行
fo = open("aaa.txt", "r+")
print ("文件名为: ", fo.name)

line = fo.read(10)
print ("读取的字符串: %s" % (line))

# 关闭文件
fo.close()

输出结果为:

文件名为:  aaa.txt
读取的字符串: 这是第一行
这是第二

7:行读取-readline()

用于从文件读取整行,包括 “\n” 字符。如果指定了一个非负数的参数,则返回指定大小的字节数,包括 “\n” 字符。

#  ----> 假设aaa.text的文件内容如下
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
fo = open("aaa.txt", "r+")
print ("文件名为: ", fo.name)

line = fo.readline()
print ("读取第一行 %s" % (line))

line = fo.readline(5)
print ("读取的字符串为: %s" % (line))

# 关闭文件
fo.close()

输出结果为:

文件名为:  runoob.txt
读取第一行 1:www.runoob.com

读取的字符串为: 2:www

8:读取所有行-readlines()

用于读取所有行(直到结束符 EOF)并返回列表,该列表可以由 Python 的 for… in … 结构进行处理。

如果碰到结束符EOF则返回空字符串。

#  ----> 假设aaa.text的文件内容如下
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
# 打开文件
fo = open("aaa.txt", "r")
print ("文件名为: ", fo.name)
 
for line in fo.readlines():  #依次读取每行  
    line = line.strip()      #去掉每行头尾空白  
    print ("读取的数据为: %s" % (line))
# 关闭
fo.close()

输出结果为:

文件名为:  aaa.txt
读取的数据为: 1:www.runoob.com
读取的数据为: 2:www.runoob.com
读取的数据为: 3:www.runoob.com
读取的数据为: 4:www.runoob.com
读取的数据为: 5:www.runoob.com

9:移动读取指针-seek()

移动文件读取指针到指定位置。

fileObject.seek(offset[, whence])
  • offset – 开始的偏移量,也就是代表需要移动偏移的字节数,如果是负数表示从倒数第几位开始。
  • whence – 可选,默认值为 0。给 offset 定义一个参数,表示要从哪个位置开始偏移;
    • 0 代表从文件开头开始算起,1 代表从当前位置开始算起,2 代表从文件末尾算起。
>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # 移动到文件的第六个字节
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # 移动到文件倒数第三个字节
13
>>> f.read(1)
b'd'
# 重新设置文件读取指针到开头
f.seek(0, 0)

10:返回当前位置-tell()

返回文件的当前位置,即文件指针当前位置。

#  ----> 假设aaa.text的文件内容如下
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
fo = open("aaa.txt", "r+")
print ("文件名为: ", fo.name)
 
line = fo.readline()
print ("读取的数据为: %s" % (line))
 
# 获取当前文件位置
pos = fo.tell()
print ("当前位置: %d" % (pos)) // 17

# 关闭文件
fo.close()

11:截断-truncate()

用于从文件的首行首字节开始截断,截断文件为 size 个字节,无 size 表示从当前位置截断;

截断之后后面的所有字节被删除

⚠️ -> 其中 Widnows 系统下的换行代表2个字节大小。

#  ----> 假设aaa.text的文件内容如下
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
# 打开文件
fo = open("aaa.txt", "r+")
print ("文件名为: ", fo.name)

# 截取10个字节
fo.truncate(10)

str = fo.read()
print ("读取数据: %s" % (str))

# 关闭文件
fo.close()
文件名为:  runoob.txt
读取数据: 1:www.runo

12:内容写入-write()

用于向文件中写入指定字符串。

在文件关闭前或缓冲区刷新前,字符串内容存储在缓冲区中,这时你在文件中是看不到写入的内容的。

如果文件打开模式带b,那写入文件内容时,str(参数)要用encode方法转为bytes形式, 否则报错。

#  ----> 假设aaa.text的文件内容如下
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com
fo = open("aaa.txt", "r+")
print ("文件名: ", fo.name)

str = "6:www.runoob.com"
# 在文件末尾写入一行
fo.seek(0, 2)
line = fo.write( str )

# 读取文件所有内容
fo.seek(0,0)
for index in range(6):
    line = next(fo)
    print ("文件行号 %d - %s" % (index, line))

# 关闭文件
fo.close()
文件行号 0 - 1:www.runoob.com
文件行号 1 - 2:www.runoob.com
文件行号 2 - 3:www.runoob.com
文件行号 3 - 4:www.runoob.com
文件行号 4 - 5:www.runoob.com
文件行号 5 - 6:www.runoob.com

13:内容写入-writelines()

用于向文件中写入一序列的字符串。

这一序列字符串可以是由迭代对象产生的,如一个字符串列表。

换行需要制定换行符 \n。

fo = open("test.txt", "w")
print ("文件名为: ", fo.name)
seq = ["菜鸟教程 1\n", "菜鸟教程 2"]
fo.writelines( seq )

# 关闭文件
fo.close()
$ cat test.txt 
菜鸟教程 1
菜鸟教程 2

六:面向对象编程

就是类的创建及其相关的操作

1:类的创建和初始化

class Student:
    # 构造函数
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 一般方法
    def study(self):
        print("{} is studying python, age is: {}".format(self.name, self.age))

    def play(self):
        print("{} is playing game, age is: {}".format(self.name, self.age))

    # 魔术方法:用来打印对象的,而不是返回对象的地址    
    def __repr__(self):
        return f"name: {self.name} & age: {self.age}"


if __name__ == '__main__':
    student1 = Student("xiaoMing", 13)
    student2 = Student("xiaoHong", 14)
    student1.study()
    student2.play()
    students = [student1, student2]
    print(students) # [name: xiaoMing & age: 13, name: xiaoHong & age: 14]

2:类属性的可见性

__双下划线开头的属性是私有属性private,_下划线开头的是受保护属性protected

但是python不会保证严格的私有化,依然可以通过一些方法得到私有属性【类似于java的反射】

class Student:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def study(self):
        print("{} is studying python, age is: {}".format(self.__name, self.__age))

    def play(self):
        print("{} is playing game, age is: {}".format(self.__name, self.__age))


if __name__ == '__main__':
    student1 = Student("xiaoMing", 13)
    student2 = Student("xiaoHong", 14)
    student1.study()
    student2.play()
    # print(student1.__name)  # will throws error
    print(student1._Student__name) # xiaoMing

property装饰器

为私有属性提供读取和修改的方法

class Student:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # 获取私有属性
    @property
    def name(self):
        return self.__name

    # 修改私有属性
    @name.setter
    def name(self, name):
        self.__name = name

    # 获取私有属性
    @property
    def age(self):
        return self.__age

    # 修改私有属性
    @age.setter
    def age(self, age):
        self.__age = age


if __name__ == '__main__':
    student1 = Student("xiaoMing", 13)
    student2 = Student("xiaoHong", 14)
    print(student1.name) # xiaoMing 读取私有属性
    print(student1.age) # 13
    student1.name = "xiaoGang" # 修改私有属性
    print(student1.name) # xiaoGang

3:静态方法和类方法

在使用静态方法@staticmethod时,可以不进行实例化,可以直接使用类名.静态方法名

所谓静态,就是不依赖于实例和类,可以看成是一个单独的函数

class Triangle:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

    def get_perimeter(self):
        return self.a + self.b + self.c

    def get_area(self):
        # 海伦公式求面积
        p = self.get_perimeter() / 2
        return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5


if __name__ == '__main__':
    print(Triangle.is_valid(1, 2, 3))  # False

类方法@classmethod的调用和静态方法类似,也是不依赖于类的实例。

但是类方法第一个参数必须是cls,表示是类的一个类方法,依赖于类

class Triangle:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def is_valid(cls, a, b, c):
        return a + b > c and b + c > a and a + c > b

    def get_perimeter(self):
        return self.a + self.b + self.c

    def get_area(self):
        # 海伦公式求面积
        p = self.get_perimeter() / 2
        return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5


if __name__ == '__main__':
    print(Triangle.is_valid(1, 2, 3))  # False

4:动态属性

python 和 JS 一样,都是动态语言,可以动态的进行属性的添加

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_information(self):
        print("{}: {}".format(self.name, self.age))


if __name__ == '__main__':
    student = Student("xiaoMing", 13)
    student.print_information()

    # 动态添加属性
    student.sex = "male"

    print(student.name, student.age, student.sex)

__slot__魔术方法

指定类的属性,不允许进行动态的添加

class Student:

    # 只能由name和age两个属性
    __slots__ = ("name", "age")

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_information(self):
        print("{}: {}".format(self.name, self.age))


if __name__ == '__main__':
    student = Student("xiaoMing", 13)
    student.print_information()

    # 动态添加属性
    student.sex = "male"

    print(student.name, student.age, student.sex)

再动态添加将会抛出异常

5:封装,继承,多态

继承

在这里插入图片描述

class son_class_name(father_class_name):
    pass;
  • 子类可以直接使用父类中的方法和属性
  • 继承具有继承性
  • 子类不可以直接调用父类的私有属性和私有方法, 对于私有属性/方法的访问,可以通过在父类中公共方法中间接实现访问
class Animal:
    def __init__(self, age):
        self.age = age

    def sleep(self):
        print("sleep: {}".format(self.age))

    def eat(self):
        print("eat: {}".format(self.age))


# 继承Animal
class Dog(Animal):
    # 父类的构造方法
    def __init__(self, age):
        # super().父类方法
        super().__init__(age)

    def bark(self):
        print("wang wang ~: {}".format(self.age))

    def get_age(self):
        return self.age


if __name__ == '__main__':
    dog = Dog(13)
    # 直接使用父类的方法
    dog.eat()
    dog.sleep()
    # 自己派生的方法
    dog.bark()
    age = dog.get_age()
    print(age)

mro()

确定搜索顺序

class Animal:
    def __init__(self, age):
        self.age = age

    def sleep(self):
        print("sleep: {}".format(self.age))

    def eat(self):
        print("eat: {}".format(self.age))


# 继承Animal
class Dog(Animal):
    # 父类的构造方法
    def __init__(self, age):
        super().__init__(age)

    def bark(self):
        print("wang wang ~: {}".format(self.age))

    def get_age(self):
        return self.age


if __name__ == '__main__':
    print(Dog.mro()) # [<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>]
    print(Dog.__mro__) # (<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)

多态

不同的子类对象调用相同的父类的方法

class Person:
    def work(self):
        pass


class Programmer(Person):
    def work(self):
        print("code program")


class Engineer(Person):
    def work(self):
        print("design building")


if __name__ == '__main__':
    programmer = Programmer()
    programmer.work()

    engineer = Engineer()
    engineer.work()
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐