python基础语法

01-Python基础

环境准备

Python代码是怎么运行的

Python解释器、Pycharm代码编辑器、Python虚拟环境、操作系统的关系

  • Python解释器负责执行Python代码

  • Python虚拟环境提供了一个隔离的环境,其中包含了特定的Python解释器和相关的库。

  • PyCharm代码编辑器提供了一个方便编写、调试和管理Python代码的界面,并可以配置使用特定的Python解释器Python虚拟环境

安装Python解释器

  1. 官网下载符合自己电脑操作系统的版本 https://www.python.org

  2. 和其他软件一样正常安装

安装Pycharm代码编辑器

  1. 官网下载符合自己电脑操作系统的版本

    • https://www.jetbrains.com/pycharm/

    • 下载带有 Professional字样的版本 (专业版)

  2. 和其他软件一样正常安装

  3. 购物APP购买正版激活码

创建Python虚拟环境

  • python虚拟环境本质:在不同文件夹(路径、目录)之内的完整的python解释器和自己安装的第三方包

  • 终端通过python命令创建,打开命令行终端,依次输入以下命令

# 这里以win10\11为例,使用的终端为powershell
# 在【当前目录】下创建python虚拟环境【假设当前目录叫做py_env】
python -m venv ./
# 使用创建的虚拟环境
./Scripts/python.exe # 进入python交互终端
./Scripts/python.exe /xxx/xxx/xxx.py # 运行某个python代码

  • pycharm编辑器创建,在pycharm中按下列顺序点击或设置:2021.2

    • File

    • Settings

    • Project:xxxx

    • Python Interpreter

    • Show All...

    • 点击 + 号

    • Virtualenv Environment

    • New environment 创建新的虚拟环境

    • Location 选择python虚拟环境的文件夹,将会把python解释器复制进去

    • Base interpreter 选择要复制的Python解释器(需要提前安装好Python)

Pycharm常用快捷键

快速注释: ctrl + /
代码快速格式化对齐: ctrl + alt + l
全选: ctrl + a
复制: ctrl + c
粘贴: ctrl + v
剪切: ctrl + x
右移: 选中要移动的代码  tab
左移: 选中要移动的代码  shift + tab 

安装python三方包

选择相应的python解释器(找到虚拟环境中的python ),再进行安装三方工具包!

python虚拟环境路径/Sc.../python.exe -m pip install pyautogui # win
python虚拟环境路径/Sc.../python -m pip install pyautogui # linux
  • 进行requests包安装

./pip install requests # 默认最新版本
pip install requests==2.25.1 # 指定版本
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple # 使用国内的安装源

注释

  • 对代码进行解释说明,不会被程序运行

# 单行注释
​
"""
多行注释
"""
​
# todo 在pycharm中高亮显示注释
""" todo 在pycharm中高亮显示注释"""

变量及类型

变量&字面量&标识符&关键字

  • 变量:用来存放数据,有四种最基本的类型:字符串、整数、浮点数、布尔值

  • 关键字:编程语言中自带的、本身就有的,不允许玩家自定义的名字

  • 字面量:(四种最基本的类型)变量的值

  • 标识符:变量的名字

    • 见名知意,不能和关键字重复,如果和关键字重复可以使用单下划线开头,以示区别

    • 字母下划线数字组成,不能以数字为开头,区分大小写

    • 小驼峰命名法 myName3

    • 大驼峰命名法 MyName2

    • 蛇形命名法 my_name_1

my_str = '字符串'
# my_str = '字符串' 就是变量
# my_str 就是标识符,变量的名字
# '字符串'就是字面量,变量的值
​
my_str1 = "字符串1"
my_str2 = '"' # 是双引号的字符串
my_str3 = "'" # 是单引号的字符串
my_str4 = '' # 空字符串
my_str5 = '\n' # 换行符
my_str6 = '11' # 长得像数字的字符串
print(type(my_str6), my_str6) # <class 'str'> 11
​
my_int = 11 # 整数
my_float = 3.1415926 # 浮点数,即小数
​
# 布尔类型
my_bool_true = True
my_bool_false = False
​
# None 啥也没有!
n = None
  • 数据类型转换

# 转str
print(str(5), type(str(5))) # 5 <class 'str'>
print(str(5.0), type(str(5.0))) # 5.0 <class 'str'>
print(str(True), type(str(True))) # True <class 'str'>
print(str(False), type(str(False))) # False <class 'str'>
​
# 转float
print(float('5'), type(float('5'))) # 5.0 <class 'float'>
print(float(5), type(float(5))) # 5.0 <class 'float'>
print(float(True), type(float(True))) # 1.0 <class 'float'>
print(float(False), type(float(False))) # 0.0 <class 'float'>
round(3.145, 2) # 3.15
​
# 转 int
print(int('5'), type(int('5'))) # 5 <class 'int'>
print(int(5.0), type(int(5.0))) # 5 <class 'int'>
print(int(True), type(int(True))) # 1 <class 'int'>
print(int(False), type(int(False))) # 0 <class 'int'>
​
# todo True本质是整数 1 ,False本质是整数 0

输入输出

  • 输入

# 从终端接收玩家的输入,全部都为字符串类型
my_str = input('请输入数字:')
print(type(my_str), my_str) # 其实是字符串
  • 输出

print('我是学号 %d 的 %s,年龄为 %d,身高为 %f 米' % (1, '刘海柱', 18, 1.78))
print('我是学号 %6d 的 %s,年龄为 %d,身高为 %.f 米' % (1, '刘海柱', 18, 1.78))
print('我是学号 %06d 的 %s,年龄为 %d,身高为 %.5f 米' % (1, '刘海柱', 18, 1.78))
# todo %d默认整数, %6d 整个数字占6位, %06d  整个数字占6位、且用0占位
# todo f默认6位小数,%.3f 保留3位小数,%.f 四舍五入保留整数

# 三种格式化输出对比
print('%s, %d, %.3f' % ('刘海柱', 18, 1.81))
print('{}, {}, {}'.format('刘海柱', 18, 1.81))
name = '刘海柱'; age = 18; high = 1.81
print(f'{name}, {age}, {high:.1f}')

print('幸运号码是', 5, '!') # 输出多个结果
print('不换行,', end='') # 输出不换行
print('这里开始手动换行:\na\nb\nc')

计算符

# + - * / 加减乘除
# // 除法取整数结果
print(5//3) # 1
# % 除法取余数
print(5%3) # 2
# ** 指数运算
print(2**3) # 8,2的3次方

# 复合赋值运算符:运算并结果赋予变量
a = 0
a += 1 # 等同于 a = a + 1 ;其他运算符以此类推
a -= 5 # 等同于 a = a - 5 ;其他运算符以此类推

02-程序控制结构

条件表达式

  • 条件表达式可以直接放在if或while后边,作为程序控制(if判断或while循环)的条件

  • 通过 比较运算符 或 逻辑运算符 返回 True或False的代码片段(非函数),称之为条件表达式

# 比较运算符
print(5 == 5) # 等于 返回True,否则False
print(5 != 6) # 不等于 返回True,否则False
print(5 >= 6) # 大于等于 返回True,否则False

# 逻辑运算符
# and 并列条件:全True返回True,否则False
print(True and True)
print(True and False)
# or 任意条件:一True或俩True返回True,否则False
print(True or False)
print(True or True)
print(False or False)
# not 真假反意:True返回False,False返回True
print(not True)
print(not False)

# 比较运算符和逻辑运算符混合使用:先比较后逻辑,and在最后
print(5 == 5 and 5 >= 6)
# 下边两个结果一致,可以手动使用小括号来标明、约束运算顺序
print(5 == 5 or 5 >= 6 and 5 != 6)
print(((5 == 5) or (5 >= 6)) and (5 != 6))

判断

  • 石头剪刀布

"""
设置两个玩家 player computer
player:从控制台输⼊要出的拳 石头(1)/剪刀(2)/布(3)
computer:电脑 随机 出拳
player和computer比较胜负
"""
# 导包声明,使用Python内置的random包
import random

# 随机返回1、2、3其中一个数字
computer_int = random.randint(1,3)

player_str = input('\n 1:石头 \n 2:剪刀 \n 3:布 \n玩家请输入:')
# todo: ret = int(str) # 数字样子的字符串转换为数字
player_int = int(player_str)

# 相减
ret = computer_int - player_int
""" 1:石头; 2:剪刀; 3:布
电脑-玩家 = 共5种结果
0:平局
-1、2:电脑胜
1、-2:玩家胜
"""

# 方式1
if ret == 0:
    print('平局!')
elif ret == -1 or ret == 2:
    print('电脑胜!')
else: # ret == 1 or ret == -2
    print('玩家胜!')

# 方式2
if ret == 0:
    result = '平局!'
else:
    # 如果 -1或2 就是电脑胜;除此以外(1或-2;0已经在外层if中判断了)就是玩家胜
    result = '电脑胜!' if ret == -1 or ret == 2 else '玩家胜!'

print(result)
  • 三元运算【难点拓展】

# 求a和b的最大值:如果a大于b就返回a,否则返回b
_max = a if a > b else b  
# 等同于下边的代码
if a > b:
    _max = a
else:
    _max = b

while循环

  • while循环嵌套

""" 打印下边的图形
*
**
***
****
*****
"""
i = 1
# 外层循环 控制行数
while i <= 5:
    j = 1 
    # 内层循环 控制单行中打印星号的次数
    while j <= i:
        print('*', end='') # end='' 不换行
        j += 1
    print() # 强制换行
    i += 1
  • break完全终止循环,continue仅结束本次循环

# 打印1-10,但跳过6
i = 1
while True:
    # 判断是否到6
    if i == 6:
        print('跳过6')
        i += 1 # 在本次循环结束之前 i 要自增 1
        continue # 提前结束本次循环,继续下一次循环
    print(i)
    i += 1
    # 判断 i 超过10
    if i > 10:
        break # 结束全部循环
print('while循环之后的代码')

for循环

# 遍历字符串中每个字符
for i in '12哈a……%@':
    print(i)

# 任意输入字符串,找到第一个数字字符并输出
my_str = input('任意输入字符串:')
for i in my_str:
    # 判断每一个字符是否在 '1234567890'中
    if i in '1234567890':
        print(i)
        break

03-容器数据类型

列表list

names_list = ['职业法师刘海柱', '基尼太美蔡徐坤', '声具泪下周淑怡']

# 添加元素
names_list.append('雷电法王杨永信')

# 删除指定的元素
names_list.remove('雷电法王杨永信')

# 根据下标索引获取元素
print(names_list[0])

# 根据下标索引修改元素
names_list[1] = '雷电法王杨永信'

# 通过下标索引删除元素
int_list = [1, 2, 3, 4, 5]
del int_list[0]
print(int_list)

# 列表元素的个数
print(len([1, 2, 3, 4, 5]))
# 求最大元素
print(max([1, 2, 3, 4, 5]))
# 求最小元素
print(min([1, 2, 3, 4, 5]))
# 求元素和
print(sum([1, 2, 3, 4, 5])) # 只有全数字类型的元素构成的列表才能使用

# 列表中的元素重新排序
int_list = [3, 2, 1, 4, 5]
int_list.sort()
print(int_list)
int_list.sort(reverse=True) # 反转倒序
print(int_list)

# 判断元素是否在列表中
print('基尼太美蔡徐坤' in names_list)
print('基尼太美蔡徐坤' not in names_list)

# 遍历
for name in names_list:
    print(name)

# 返回下标
for index, name in enumerate(names_list):
    print(index, name)

# 列表切片,(左闭右开)左包右闭,步长减一,负数倒序
# 列表[起始索引:结束索引:步长] 返回列表
# [5, 4, 3, 2, 1]
print(int_list)
print(int_list[2:3])
print(int_list[::-2])
print(int_list[2::2])
print(int_list[2::-2])

# 列表推导式
new_list = [i*2 for i in int_list[::-1]]
print(new_list)
print([index for index, value in enumerate(names_list)])

元组tuple

# 元组只能读,不能增删改!不能改变顺序!
names_tuple = ('职业法师刘海柱',)
names_tuple = ('职业法师刘海柱', '基尼太美蔡徐坤', '声具泪下周淑怡')

# 根据下标索引获取元素
print(names_tuple[0])

# 元素的个数
print(len((1, 2, 3, 4, 5)))
# 求最大元素
print(max((1, 2, 3, 4, 5)))
# 求最小元素
print(min((1, 2, 3, 4, 5)))
# 求元素和
print(sum((1, 2, 3, 4, 5))) # 只有全数字类型的元素构成的元组才能使用

# 判断元素是否在列表中
print('基尼太美蔡徐坤' in names_tuple)
print('基尼太美蔡徐坤' not in names_tuple)

# 遍历
for name in names_tuple:
    print(name)

# 返回下标
for index, name in enumerate(names_tuple):
    print(index, name)

# 切片,左包右闭,步长减一,负数倒序
# 元组[起始索引:结束索引:步长] 返回列表
int_tuple = (5, 4, 3, 2, 1)
print(int_tuple)
print(int_tuple[2:3])
print(int_tuple[::-2])
print(int_tuple[2::2])

# 推导式
new_generator = (i for i in int_tuple[::-1])
print(new_generator) # 返回可迭代对象
# 放到列表中方便查看
new_list = [i for i in int_tuple[::-1]]
print(new_list)

集合set

hash + 指纹字符串(md5\sha1\sha256)+ set

# 集合能够去重、不能切片
my_set = set() 
my_set = {1, 2, 3} # 集合
# 向集合的右边队尾添加元素
my_set.add(1)
my_set.add(2)
my_set.add(2)
my_set.add('字符串')
print(my_set)

# 删除指定的元素
my_set.remove(元素)
print(my_set)
# 从集合最左边取出一个元素
print(my_set.pop()) # 2
print(my_set) # {'字符串'}

my_set = {1, 2, 3} # 集合
print(len(my_set)) # 元素个数
print(max(my_set)) # 最大值
print(min(my_set)) # 最小值

# 遍历每个元素
for v in my_set:
    print(v)
# 遍历“下标索引”和元素
for index, v in enumerate(my_set):
    print(index, v)

# 集合、列表、元组 转换
a_set = {1, 2, 3} # 集合
b_list = list(a_set) # 列表
c_tuple = tuple(b_list) # 元组
d_set = set(c_tuple) # 集合
print(a_set, b_list, c_tuple, d_set) 
# {1, 2, 3} [1, 2, 3] (1, 2, 3) {1, 2, 3}

字典dict

my_dict = {'name': 'smart', 'age': 18, 'city': '上海'}

# 通过健修改、添加值
my_dict['name'] = '刘海柱'

# 根据字典的 key 获取对应的值
print(my_dict['name'])
print(my_dict.get('name')) # 没有就None
print(my_dict.get('name', value)) # 返回value

# 通过key取出value
print(my_dict.pop('city')) # 被取出的在原字典中就没了

my_dict_2 = {'a':0, 'b':1, 'c':2}
print(len(my_dict_2))
print(max(my_dict_2))
print(min(my_dict_2))
# 通过key删除kv键值对
del my_dict_2['a']
print(my_dict_2)

# 遍历key
for key in my_dict:
# for key in my_dict.keys():
    print(key)
# 遍历value
for value in my_dict.values():
    print(value)
# 遍历key,value
for (k,v) in my_dict.items():
    print(k,v)
# 遍历下标索引,key,value
for i, (k, v) in enumerate(my_dict.items()):
    print(i, k, v)

# 判断某个value是否在字典中
print('刘海柱' in my_dict.values())
print('刘海柱' not in my_dict.values())
# 判断某个key是否在字典中
print('name' in my_dict.keys())
print('name' not in my_dict.keys())

字符串str

name_1 = '职业法师刘海柱'
name_2 = "雷电法王杨永信"

# \n 换行
print(name_1 + '\n' + name_2)

# 切片,左要右不要,步长减一,负数倒序
# 字符串[起始索引:结束索引:步长] 返回字符串
print(name_2[3])
print(name_2[2:3])
print(name_2[::-2])
print(name_2[2::2])

# 查找字符是否在字符串中,返回下标,不存在就返回-1
print(name_1.find('海'))
print(name_1.find('海', 1, -1)) # 从下标1开始到最后一个字符

# 字符替换:原来的、新的、替换次数(默认不写则全部替换)
print(name_2.replace('王', '神', 5)) # 返回新的字符串

# 指定分割字符串并丢弃,返回列表
ret_list = name_2.split('杨')
print(ret_list)

# 用字符串拼接列表中的每一个元素
print('杨'.join(ret_list))

my_int = 1234 # int
# str函数的用法
your_str = str(my_int) # str
print(type(your_str), your_str)
# eval函数的用法
her_int = eval(your_str) # int
print(type(her_int), her_int)

# 字符串长度
print(len(name_2))

# 遍历字符串中每个字符
for v in name_2:
    print(v)
# 遍历字符下标索引和值
for i,v in enumerate(name_2):
    print(i, v)

# 判断字符是否在字符串中
print('王' in name_2)
print('王' not in name_2) 

04-函数

  • 函数的作用:代码复用,区分代码逻辑功能

  • 内置函数:Python自带可直接使用,如 type input print random.randint len sum max min format等

  • 函数中的变量

    • 变量作用域:指的是变量的作用范围,即变量在哪里可用,在哪里不可用)

    • 局部变量:是函数内部定义的变量(包括函数的形式参数),其作用域只在函数内部有效,不能影响函数外部,除非使用关键字global 来声明全局变量。不同函数的局部变量可以同名,互不影响。

  • 函数的参数

    • 实参:在调用时给形参传入的具体值

      • 位置参数(位置实参):函数调用时,按形参的位置,从左到右,依次传递实参,缺一不可,不需要指定形参名称

      • 关键字参数(关键字实参):函数调用时,按照形参名=值的形式来传递实参,不必按照位置传递

    • 形参:声明函数(写函数)时的参数

      • 普通形参 my_str

      • 元祖不定长形参 *args 接收多个普通参数、参数名和值任意,在函数中以元组呈现

      • 缺省形参(默认)my_int=666

      • 字典不定长形参 **kwargs 接收多个普通参数、参数名和值任意,在函数中以字典呈现

    • 定义函数时、调用函数时,=左边的参数名都是形参,=右边的都是实参,有具体内容的都是实参

函数示例

# 石头剪刀布
# 导包声明,使用Python内置的random包
import random


def caiQuanGame(nums, is_player_bool=True):
    """ 猜拳一次
    1:石头; 2:剪刀; 3:布
    电脑-玩家 = 共5种结果
        0:平局
        -1、2:电脑胜
        1、-2:玩家胜
    :param: is_player: 是否由电脑帮玩家猜拳, 默认True, 由玩家输入
            nums: 游戏次数,在本函数中没用,传出函数
    :return: (nums, result_str) :result_str: 平局! or 电脑胜! or 玩家胜!
    """
    # 随机返回1、2、3其中一个数字
    computer_int = random.randint(1,3)

    # if is_player_bool == 1:
    # if is_player_bool == True:
    # if is_player_bool is True:
    if is_player_bool:
        player_str = input('\n 1:石头 \n 2:剪刀 \n 3:布 \n玩家请输入:')
        # todo: ret = int(str) # 数字样子的字符串转换为数字
        player_int = int(player_str)
    else:
        player_int = random.randint(1, 3)

    # 相减
    ret = computer_int - player_int

    if ret == 0:
        result_str = '平局!'
    else:
        # 如果 -1或2 就是电脑胜;除此以外(1或-2;0已经在外层if中判断了)就是玩家胜
        result_str = '电脑胜!' if ret == -1 or ret == 2 else '玩家胜!'
    return nums, result_str


def main():
    """程序主逻辑
    :return: None 没有return关键字的函数 默认返回None
    """
    nums = int(input('输入游戏次数:'))
    is_player = input('是否自动进行游戏?输入 "是" 或 "否":')
    is_player_bool = True if is_player == '是' else False

    i = 1
    while True:
        # todo 调用猜拳函数
        result = caiQuanGame(is_player_bool=is_player_bool, nums=nums)
        # 输出 caiQuanGame 函数的 第二个返回值 result_str
        print(result[1])  #  return (nums, result_str)

        i += 1
        if i > nums:
            break
    print('游戏结束')


# 执行程序的主逻辑
main()

局部与全局变量

num = 10 # 声明全局变量

# 全局变量
def func1():
    print('func1:', num) # 访问全局变量num
func1()

def func2():
    # 这里相当于在函数内部定义了一个局部变量,也叫num,它的值为:10,它和全局变量没有关系
    num = 20 # 声明局部变量
    print('func2:', num) # 使用局部变量
func2()

def func3():
    # 需求:在 func3 函数调用时,修改全局变量 num,把它的值改为:10
    # global一个关键字,这个关键字的作用是告诉 python 解释器,接下来使用的 num 是全局变量的 num
    # 下面这句代码叫
    global num # 声明 全局变量
    # 因为是全局变量,当func3函数被调用时,执行到这里时,全局的num都会修改
    num = 30 # 修改 num的值
    print('func3:此时刚刚修改了全局变量num的值')
    print('func3:', num)
func3()

print(num)
func1()
func2() 

不定长参数

# 元组不定长参数,用1个 * 来声明,传入时是个元组
def func4(*args):
    print(type(args), args)
    return 111
ret = func4(1,3,5)

# 字典不定长参数,用2个 ** 来声明,传入时是个字典
def func5(**kwargs):
    print(type(kwargs), kwargs)
func5(
    name='彦祖',
    age=18,
    gender='男'
)

# 函数不同形式的参数有顺序要求:普通参数,元组不定长参数,缺省参数,字典不定长参数
def func6(my_int, *args, name='德华', **kwargs):
    print(my_int)
    print(args)
    print(name)
    print(kwargs)
func6(1, 2, 3, 4, name='柏芝', age=16, gender='女')

05-语法进阶

组包与拆包

# 组包:变量赋值时,= 右边有多个数据时,会自动包装为元组赋值给 = 左边的变量。
a = 1, 2, 3
print(type(a), a) # <class 'tuple'> (1, 2, 3)

# 拆包:多个变量 = 容器数据,当变量数量等于容器长度时,容器中的每个元素会依次赋值给 = 号左边的变量。
# 注意:变量的个数必须和数据的个数相同。
a, b, c = (1, 2, 3)
print(a, b, c)

# 先组包、再拆包
a, b, c = 1, 2, 3
print(a, b, c)

# 示例1:交换变量
a, b = 10, 20
print(f'交换之前:a={a},b={b}') #交换之前:a=10,b=20
a, b = b, a
print(f'交换之后:a={a},b={b}') #交换之后:a=20,b=10

# 示例2:函数多个返回值
def func():
    return 1, 2, 3
a, b, c = func()
print(a, b, c)

# 示例3:遍历字典 k v
my_dict = {'name': '彦祖', 'age': 18}
for index, (k, v) in enumerate(my_dict.items()):
    print(index, k, v)

可变与不可变类型

  • 可变类型与不可变类型:在不改变内存地址的情况下,能否改变其中的数据,若能则是可变类型,若不能则是不可变类型。

  • 可变类型

    • 列表

    • 字典

    • 集合

  • 不可变类型

    • 数值类型(整型/浮点型/布尔)

    • 字符串

    • 元组

  • 引用可以理解一种指向,python中=号赋值的本质就是建立或改变引用

  • 引用、不可变类型、可变类型的推导过程如下

# a、b、c三个变量都引用(指向)了10
a = 10
b = a
c = a
print(id(a), id(b), id(c), id(10)) # id都一样

# a不再指向10,bc还指向10
a = 20
print(id(a), id(20), id(b), id(c)) # a变了,bc没变

# 函数调用传参 都是引用传递 id一致
num = 10001
print(id(num))
def func(my_num):
    print(id(my_num))
func(my_num=num)

# 可变类型示例,两个list的id始终没有变化
my_list = [1, 2, 3]
your_list = my_list
print(id(my_list), id(your_list))
my_list[0] = 'hahaha'
print(id(my_list), my_list)
print(id(your_list), your_list)

深浅拷贝

list1 = [1, 2, [3, 4]]
list2 = list1.copy()

print("list1:", list1, id(list1))
print("list2:", list2, id(list2))

list1[2][0] = 5
print('修改了list1中的某个值')

print("list1:", list1, id(list1))
print("list2:", list2, id(list2))

ret = """结论:这是【浅拷贝】!
通过copy函数,复制的对象的id发生了改变
原列表的值改变,新列表的值也跟着变
"""
print(ret)

from copy import deepcopy # 导包使用 deepcopy深拷贝函数

list1 = [1, 2, [3, 4]]
list2 = deepcopy(list1)

print("list1:", list1, id(list1))
print("list2:", list2, id(list2))

list1[2][0] = 5
print('修改了list1中的某个值')

print("list1:", list1, id(list1))
print("list2:", list2, id(list2))

ret = """结论:这是【深拷贝】!
通过copy.deepcopy函数,复制的对象不光id发生改变
原列表的值改变,新列表的值不跟着变
原列表和新列表完全没有任何关联!
"""
print(ret)

range函数

range(11)
range(1, 11)
range(1, 11, 2)
ret = range(1, 11, 2) # 从1到10,步长为2
print(type(ret), ret) # <class 'range'> range(1, 11, 2)
print(list(ret)) # [1, 3, 5, 7, 9]
# 可用for循环逐一遍历
for i in range(5): # range函数默认从0开始,默认步长为1
    print(i)

列表推导式

# 列表推导式
print([i for i in range(5)])
print(list(range(5)))

# 带判断的列表推导式
print(
    # 符合if条件的i,for i in range(10),if i除以2的余数为0
    [i*2 for i in range(10) if i % 2 == 0]
)

lambda表达式

"""lambda表达式:简单函数的简洁写法,又成为匿名函数
lambda表达式用法:
    函数名 = lambda 参数1, 参数2, ... : 单行代码
    函数名(参数1, 参数2, ...)
注意:
1.匿名函数中不能使用 while 循环、for 循环,只能编写单行的代码
2.匿名函数中返回结果不需要使用 return,单行代码的结果就是返回值
"""

def func_sum(a, b):
    """计算a,b的和,返回结果"""
    return a + b

# 使用 lambda 表达式定义一个上面的函数
lambda_sum = lambda a, b: a + b
ret = lambda_sum(1, 3)
print(ret)

# 用法2,匿名函数这个名称的由来
print(
    (lambda a, b: a + b)(1, 3) # (lambda匿名函数)(参数)
)

filter函数

"""filter函数:只干过滤选择的事儿!不干处理数据的事儿!
自定义过滤判断函数,可迭代对象的每个元素作为入参,只有返回True的该元素才会被保留
每个元素都作为自定义函数的入参,有多少个元素,自定义函数就会被执行多少次
用法:
新的可迭代对象 = filter(判断函数, 可迭代对象)
print(list(新的可迭代对象))
"""
# 过滤列表中的奇数
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def func3(i):
    return i % 2 == 1
my_iterable = filter(func3, my_list)
newlist = list(my_iterable)
print(newlist)

# 可以使用lambda表达式
my_iterable = filter(lambda i: i%2==1, my_list)
print(list(my_iterable))


# 判断函数返回 True 或 False !!!
ret = func3(2)
print(ret)
ret = func3(3)
print(ret)

# 将某个列表中的所有数字全部变为正数,todo filter做不到!
my_list = [-5, 3, -7, -99, 8, -9]
def func1(i): # 将入参转为正数
    ret = -i if i < 0 else i
    return ret
print(func1(-9))
my_iterable = filter(func1, my_list)
print(list(my_iterable)) # 无效!

# 将某个列表中的所有正数取出,filter做的到!
def func2(i):
    # todo 过滤判断函数返回的必须是True或False
    return i > 0
my_iterable = filter(func2, my_list)
print(list(my_iterable))
print(list(filter(lambda i: i > 0, my_list)))

map函数

"""map函数:处理数据!
通过自定义函数,对可迭代对象的每一个元素分别进行操作,返回新的可迭代对象
new_可迭代对象 = map(处理每一个元素的函数, 可迭代对象)
"""

my_list = list(range(10))
print(my_list)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

ret = map(lambda i: i*i, my_list)
print(list(ret))
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce函数

"""reduce函数
通过自定义函数,先对第1、2元素进行操作,返回一个结果
再拿这个结果和第3个元素进行操作,直到得到最终的一个结果
结果 = reduce(处理函数, 可迭代对象) # 处理函数必须接收2个参数!
[英] reduce v. 使...缩小、减少
"""
from functools import reduce # reduce函数需要导包使用

ret = reduce(lambda x, y: x + y, list(range(10)))
print(ret) # 45

06-操作文件

文件的读写

# 查看当前操作系统默认编码格式
import sys
print(sys.getdefaultencoding()) # utf8
my_sys_default_encoding = sys.getdefaultencoding()

print('【写】')
with open('./111.txt', mode='w', encoding=my_sys_default_encoding) as f:
    f.write('职业法师刘海柱\n') # 写入不会换行!
    f.write('雷电法王杨永信')

print('【读全部】')
with open('./111.txt', mode='r', encoding=my_sys_default_encoding) as f:
    print(f.read(2)) # 读取规定数量的字符,并换行
    print(f.read()) # 读取全部

print('【读一行】')
with open('./111.txt', mode='r', encoding=my_sys_default_encoding) as f:
    # 每一行自带换行符,print再次换行,就出现了 输出结果有空行的现象
    print(f.readline(3))  # 读取字符数,并换行
    print(f.readline())

print('【逐行读】')
with open('./111.txt', mode='r', encoding=my_sys_default_encoding) as f:
    # 每一行自带换行符,print再次换行,就出现了 输出结果有空行的现象
    while True:
        ret = f.readline()
        if ret == '': # 如果读出来是空串,说明读完了
            break
        print(ret)

print('【读全部返回列表】')
with open('./111.txt', mode='r', encoding=my_sys_default_encoding) as f:
    print(f.readlines()) # 返回list ['职业法师刘海柱\n', '雷电法王杨永信']

文件的访问模式

"""mode参数 文件的访问模式 
r 只读,文件不存在就报错【默认模式】[常用]
w 覆盖写,文件不存在就创建文件
a 追加写,文件不存在就创建文件(append,接着写)[常用]

# b表示 byteString
rb 二进制 只读,文件不存在就报错 
wb 二进制 覆盖写,文件不存在就创建文件
ab 二进制 追加写,文件不存在就创建文件(append,接着写)

r+ 读+覆盖写,文件不存在就报错
w+ 覆盖写+读,文件不存在就创建文件,注意:该模式打开文件瞬间清空文件内容!
a+ 追加写+读,文件不存在就创建文件(append,接着写)[常用]
"""

文件的编码

"""encoding参数 文件的编码格式 
windows操作系统下,open操作文件时,encoding参数默认为gbk;
较新版本的win操作系统默认编码方式用的却是utf8,这个时候就必须声明 encoding=‘utf8‘。
linux/mac操作系统下,open操作文件时,encoding参数默认为utf8。
"""

相对和绝对路径

  • 相对路径,所谓的相对,是相对代码运行的路径;不是相对当前代码文件的路径!

  • 绝对路径,从根目录开始的,或从盘符开始描述的路径

# 相对路径
. # 一个点表示当前路径
.. # 两个点表示上一级路径
../../a/b.txt # 当前代码运行路径上一级的上一级下的a目录下的b.txt
./c.txt # 当前代码运行路径下的c.txt文件

# 绝对路径
/a/b/c.txt # linux mac的绝对路径
D:\360MoveData\Users\hahage\Desktop\a.txt # win的绝对路径

os模块常用函数

import os
"""通过文件夹查看文件,pycharm侧边栏存在延迟"""

with open('1.txt', mode='w', encoding='utf8') as f:
    f.write('大威天龙')
# 查看指定的路径下有哪些文件和文件夹
print(os.listdir('./')) # ls
# 文件重命名
os.rename('1.txt', 'haha.txt')
print(os.listdir('./'))
# 判断路径(可以是文件,也可以是文件夹)是否存在
print(os.path.exists('./haha.txt'))
# 判断文件(不是文件夹)是否存在
print(os.path.isfile('./haha.txt'))
# 删除文件
os.remove('haha.txt')
print(os.listdir('./'))
# 创建文件夹
os.mkdir('./haha')
print(os.listdir('./'))
# 删除文件夹
os.rmdir('./haha')
print(os.listdir('./'))
# 查看当前工作目录位置,即Python代码运行路径
print(os.getcwd()) # pwd
# 改变工作目录位置
os.chdir('../') 
print(os.listdir('./'))

07-处理异常

# traceback.format_exc() # 完整的异常堆栈信息
import traceback

try:
    print('语法错误是 sytanx error')
    print('异常错误是 exception error')
    print('a'+11)
    print(22222)
    print('a'+11)
except Exception as e:
    # 仅输出异常提示信息
    print(e)
    # traceback.format_exc() 完整的异常堆栈信息
    print(traceback.format_exc())
else:
    print('如果没有发生异常就执行这里')
finally:
    print('无论怎样都会执行这里')
    print('过分使用try except,会导致找不到问题所在位置,慎重使用')

    # 和循环判断with open一样,try.except也可以嵌套,他们可以互相嵌套
    try:
        print(5/0)
    # 只捕捉专项异常
    except ZeroDivisionError:
        print(traceback.format_exc())

print('【异常传递】')
def foo():
    print('foo start')
    print(num) # todo 报错的源头
    print('foo end')
def bar():
    print('bar start')
    # 代码运行至此才会引发异常
    foo() # todo 报错发生的导火索
    print('bar end')
try:
    bar()
finally:
    print(traceback.format_exc())

08-模块和包

  • 包 => 含有 __init__.py文件的文件夹

  • 模块 => py文件

  • 类 => class 【面向对象学习】

  • 函数(方法)=> def

以上均不能以数字开头,也不能和python内置的一切重名

模块和包的使用

# from 模块 import 函数
from os import listdir
ret = listdir()
print(ret)

# import 模块
import os
ret = os.listdir()
print(ret)

# 用 as关键字 起别名
import os as winos
ret = winos.listdir()
print(ret)

from os import listdir as win_listdir
ret = win_listdir()
print(ret)

__name__属性

只有运行入口的py文件(模块)的__name____main__,其他被引用的文件(模块)的__name__是不带扩展名的文件名

下边的a.py和b.py两个文件,在同一目录中!

  • a.py - 运行该文件

import os

print(os.__name__) # 内置属性 模块名字
print(os.listdir.__name__) # 内置属性 函数名字
print(__name__) # 当前正在运行的模块名字 永远是 __main__

import b # 引用了b文件,b文件发生了运行
print(b.__name__)

if __name__ == '__main__':
    print('aaaa')
    print(__name__)
  • b.py

print(__name__)

if __name__ == '__main__':
    # 如果被其他py文件引用,这里将不会执行
    # 因为被引用时,b.py的__name__是 b,而不是__main__
    # 只有b.py作为主文件运行时,b的__name__才是__main__
    print('bbbb')
    print(__name__)

__all__属性

如果一个py文件(模块)中有 __all__变量(list类型),当其他py文件(模块)使用from 模块名 import *引用它时,只能导入这个列表中指定的内容

  • a.py

__all__ = ['foo', 'my_int'] 

my_int = 666

def foo():
    return my_int

def bar():
    return 777
  • b.py - 运行该文件

import traceback
from a import *

try:
    print(my_int)
    print(foo())
    print(bar())
except:
    print(traceback.format_exc())

from a import bar
print(bar())

模块的搜索路径

一切以被运行的python文件所在的路径为准,是工作路径,同时也是相对路径的起点

import sys
print(sys.path)
"""sys.path可以说明:
运行py代码时,Python解释器会自动搜索要使用的模块
会自动向以下两个路径进行搜索:
1. 当前工作路径
2. Python虚拟环境路径

还可以手动添加导包环境路径:
import sys
sys.path.append('要导包的位置')

注意:自定义的一切变量、函数、类、模块、包,
都不要和python内置的一切重名!
"""

包是有特殊文件的文件夹

# 当我们使用一个包时,这个包目录下的__init__.py文件中的代码会自动执行。
# 包的 __init__ 文件中,也可以设置 __all__ 内置变量,限定 `from 包名 import *` 时默认导入的模块。
# 平时沟通时,包和模块的概念往往不区分
# 包和模块都存在内置的第三方的区别,第三方的就是玩家自己写的、非Python官方的
"""三方包需要额外安装
安装命令:
pip install 包名或模块名 -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install 包名或模块名==版本号
pip install 包名或模块名

卸载命令:pip uninstall 包名或模块名

注意:
1. 安装第三方的包和模块是在 python 解释器安装目录的 Lib/site-packages 下面
2. python虚拟环境和默认环境 需要各自安装各自的三方包!
安装到指定的py虚拟环境下 /虚拟环境路径/python -m pip install 包名或模块名
默认安装到默认python下:pip install 包名或模块名
"""

09-日志操作

  • 日志的作用:自带时间,代码位置,信息分级可控,比print方便

  • 日志基本分级:

    • debug 调试消息

    • info 普通消息

    • warning 警告消息

    • error 错误消息

    • critical 严重错误

  • python默认的日志包是logging,也有很多很成功的第三方包,比如loguru -- 虚拟环境python -m pip install loguru

# import logger
# pip install loguru 
from loguru import logger as log

# 日志文件名称
# {time} loguru包会自动填写文件创建的时间
# xxxx-xx-xx_xx-xx-xxxxxx 年 月 日 时 分 秒 微秒6位  【秒后3位的是毫秒 毫秒后3位是微妙】
logfile_path_str = './loguru_test_{time}.log'

# 日志保存,要先声明才有效
# log.add(logfile_path_str)
log.add(
    sink=logfile_path_str, # 日志文件名
    # 以下为可选参数
    rotation='200KB', # 设定日志大小, 200k一个文件
    compression='zip', # 设置压缩格式为zip
    retention='72h', # 每隔72小时清理一次日志
    serialize=True, # 默认为False,普通日志;True 把日志改成json格式
    encoding='utf8', # 声明日志文件的编码格式
    # level='WARNING' # 日志文件只记录WARNING及更严重级别的日志
)
""" 
1. 参数 rotation 
rotation='200KB', # 设定日志大小, 200k一个文件
rotation='14:00', # 每天中午14点创建新的日志文件
rotation='1 week', # 以周为单位,日志循环写入
2. 参数 compression='zip' # 支持多种压缩格式:
gz bz2 xz lzma tar tar.gz tar.bz2 tar.xz zip
3. level='WARNING' # 日志文件记录的级别,顺序如下:
DEBUG INFO SUCCESS WARNING ERROR CRITICAL
"""

# 自定义日志信息
log.debug('debug级别调试消息')
log.success('成功调用')
log.info('普通消息') # 常用
log.warning('警告消息')
log.error('错误消息')
log.critical('严重错误消息')

# 记录错误异常
try:
    print(5/0)
except:
    # 完整记录错误堆栈信息,显示内容和 traceback.format_exc() 一致
    log.exception('错误消息')

# 关闭控制台输出,且不写入日志文件
log.remove(handler_id=None)
log.info('这个信息不回显示,111')

10-时间操作

  • time 内置模块

import time
# 停止n秒
print(time.sleep(3))

# 1695539421.6926203秒 起始时间是1970年1月1日0点(unix纪元)
print(time.time()) # 时间戳 1695539421.6926203
# print(time.ctime()) # Sun Sep 24 15:16:49 2023
print(time.strftime('%Y-%m-%d %H:%M:%S')) # 2023-09-24 15:20:24
# 时间戳转日期,time.strftime函数多加一个参数
print(time.localtime(时间戳)) # 不返回时间戳!
# time.struct_time(tm_year=2023, tm_mon=10, tm_mday=7, tm_hour=12, tm_min=0, tm_sec=35, tm_wday=5, tm_yday=280, tm_isdst=0)
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(时间戳)))
# 日期转时间戳
print(time.mktime(
    time.strptime( # 注意区别 strptime strftime
        time.strftime('%Y-%m-%d %H:%M:%S'), # 格式化的时间:2023-09-24 15:20:24
        '%Y-%m-%d %H:%M:%S' # 指定格式
    )
))

  • datetime 内置模块

import datetime

# 当前时间
print(datetime.datetime.now()) # 2023-09-24 15:13:57.999473
# 格式化输出时间
print(datetime.datetime.now().strftime('%Y-%m-%d')) # 2023-09-24
# 时间戳 1695540551.546279
print(datetime.datetime.now().timestamp())

# 输出年月日时分秒
print(datetime.datetime.now().day)
print(datetime.datetime.now().year)
print(datetime.datetime.now().minute)

# 格式时间和时间戳互相转换
date_str = '2002-12-24'
my_dt = datetime.datetime.strptime(date_str, '%Y-%m-%d')
时间戳 = my_dt.timestamp()
print(时间戳) # 工作中 不要写中文命名!
print(datetime.datetime.fromtimestamp(时间戳))

# 指定时间
dt = datetime.datetime(2002,12,24,22,40,59)
print(dt) # 2002-12-24 22:40:59
print(dt.year)

# 计算日期差
print(datetime.datetime.now() - dt) # 7578 days, 16:42:58.277457

# 计算某个时间点的多久之后、多久之前 # 工作中 不要写中文命名!
时间差 = datetime.timedelta(days=10, hours=3, minutes=30, seconds=30)
print(datetime.datetime.now() - 时间差) # 2023-09-23 11:55:41.043847

11-json模块

# json是长得像字典的字符串,Python内置的json模块可以帮助我操作json
import json

my_dict = {'name': '职业法师刘海柱', 'age': 18}

# json.dumps():dict转json字符串,dumps==dump String
json_str = json.dumps(my_dict, ensure_ascii=False)
# ensure_ascii=False 是否使用ascii编码,默认为True
print(json_str, type(json_str)) # 双引号
print(my_dict, type(my_dict)) # 单引号

# json.loads():json字符串转dict,loads==load String
new_dict = json.loads(json_str)
print(new_dict, type(new_dict))

# json.dump() 将dict写入文件中,自动转换为json字符串
with open('./111.json', 'w', encoding='utf8') as f:
    json.dump(my_dict, f, ensure_ascii=False)

# json.load() 将文件中json字符串读取出来,返回dict
with open('./111.json', 'r', encoding='utf8') as f:
    new_dict2 = json.load(f)
    print(new_dict2, type(new_dict2))

12-面向对象

  • 类和对象的关系:

    • 类相当于图纸(抽象),对象相当于根据图纸制造的实物(具体)

    • 每个对象必须有一个对应的类

  • 类:

    • 很多事物存在 相同的属性和行为(也叫方法),比如人有姓名年龄,能吃饭睡觉等等

    • 描述 具有共同特征的事物的 抽象,称为 类 (class)

  • 类包含两个组成部分:

    • 属性(变量):比如姓名,年龄,身高,肤色等

    • 方法(函数):比如吃饭,睡觉,飞行,歌唱等

  • 对象:对象 是 类 的实例,是具体的实体

  • 面向对象的特点

    • 封装

      • 将属性和方法放到一起封装成一个整体(定义类),然后通过实例化对象来使用

      • 对属性和方法增加访问权限控制:公开权限、私有权限

    • 继承

      • 子类可以继承父类,子类继承父类之后,子类就拥有了父类中的属性和方法

      • 重写:子类从父类继承过来的方法,不满足子类需要或不完全满足子类需要

      • 私有和继承

    • 多态:同一个代码,由于传入的实例对象的不同,最终代码的调用有不同的效果,这就叫多态

封装

类的定义和使用

房子和家具示例代码

#家具分不同的类型(名字),并占用不同的面积
#输出家具信息时,显示家具的类型和家具占用的面积
#房子有自己的地址和面积
#房子可以添加家具,如果房子的剩余面积可以容纳家具,则提示家具添加成功;否则提示添加失败
#输出房子信息时,可以显示房子的地址、面积、占用面积、剩余面积、显示包含的所有家具的类型

class Item(object):
    """家具类"""
    def __init__(self, name, area):
        self._name = name # 家具类型(名字)
        self._area = area # 家具占用面积
    def __str__(self):
        return f'{self._name},面积{self._area}平米'
    

class House:
    """房子类"""
    def __init__(self, addr, area):
        self._addr = addr # 地址
        self._area = area # 总面积

        self.use_area = 0 # 占用面积
        self.free_area = self._area # 剩余面积初始等于总面积
        self.item_list = [] # 家具列表

    def __str__(self):
        ret = f"""房子坐落于{self._addr}
占地{self._area}
占用面积{self.free_area}
剩余面积{self.free_area}
目前家具有{self.item_list}
        """
        return ret

    def add_item(self, item):
        """添加家具,计算并修改各种面积
        item 是实例化的家具对象
        有 家具类Item._area 属性
        有 家具类Item._name 属性
        """
        # 修改占用面积
        self.use_area = self.use_area + item._area
        # 修改剩余面积
        self.free_area = self._area - self.use_area
        # 添加家具列表
        self.item_list.append(item._name)


# 实例化家具
sofa = Item(name='沙发', area=5)
bed = Item('床', 4)
desk = Item('书桌', 3)
print(sofa, bed, desk)

# 实例化房子
house = House(addr='金燕龙大院', area=20)
print(house)

# 向房子中添加家具
house.add_item(sofa)
house.add_item(sofa)
house.add_item(bed)
house.add_item(desk)
print(house)

del魔法方法

class Item:
    """家具类"""
    def __init__(self, name, area):
        self._name = name # 家具类型(名字)
        self._area = area # 家具占用面积
    def __str__(self):
        return f'{self._name},面积{self._area}平米'
    def __del__(self):
        # 当删除对象时,此方法会被自动调用执行
        print(f'丢弃了家具{self._name}')

sofa = Item('沙发', 5)
# 手动删除实例对象,自动执行__del__函数
del sofa
# 如果不手动删除,程序运行结束前会自动删除

try: # 类还在,对象却没了!
    print(Item)
    print(sofa)
except Exception as e:
    print(e)

公开和私有权限

  • 公开属性、公开方法:随便调用

  • 私有属性、私有方法:

    • 只能在类定义的内部调用

    • 以两个下划线开头__的都是私有权限

class Women:
    """"""
    def __init__(self, name):
        self.name = name # 公开属性
        self.__age = 18 # 私有属性
    def func1(self):
        # 公开方法
        print(self.name)
        print(self.__age) # 可以调用
        print(self.__func2()) # 可以调用

    def __func2(self):
        # 私有方法
        print(self.name)
        print(self.__age)

lady = Women('lady gaga')
lady.func1()
# lady.__func2() # 不可以调用
# print(lady.__age) # 不可以调用

# 实例对象._类名__属性或方法名 可强行突破!
print(lady._Women__age) # 强行调用私有属性
lady._Women__func2() # 强行调用私有方法

继承

  • 类的继承:在面向对象编程中,子类可以直接继承父类,从而使子类直接具备父类的能力(属性和方法)【代码复用】

  • 继承作用:解决代码重用问题,提高开发效率

  • 注意

    • 站在父类的角度来看,父类派生出子类

    • 站在子类的角度来看,子类继承于父类

    • 父类也叫基类,子类也叫派生类

    • object 是 python 中所有类的父类

多继承

class SmallDog(object):
    def eat(self):
        print('小口吃东西')

    def sleep(self):
        print('小憩一会')


class BigDog(object):
    def drink(self):
        print('大口喝水')

    def sleep(self):
        print('呼呼大睡')

# 多继承 默认调用先继承的那个父类中的同名方法
# SuperDog 类定义时同时继承了 SmallDog 和 BigDog
class SuperDog(BigDog, SmallDog):
    pass


# 创建一个 SuperDog 对象
sd1 = SuperDog()
# SuperDog 从 SmallDog 继承了 eat 方法
sd1.eat()
# SuperDog 从 BigDog 继承了 drink 方法
sd1.drink()
# 默认调用先继承的那个父类中的同名方法
sd1.sleep()

继承多层传递

# 爷爷类
class Animal(object):
    """动物类"""
    def eat(self):
        print('吃东西')

# 爸爸类
class Dog(Animal):
    """狗类"""
    def drink(self):
        print('喝水')

# 儿子类
class SuperDog(Dog):
    pass

# 创建一个 SuperDog 对象
sd1 = SuperDog()
sd1.eat()
sd1.drink()

重写父类方法

  • 重写父类(包括爷爷类及以上)方法:

    • 从父类继承的方法不能满足子类的需要,为了拓展功能而重新定义了同名函数

    • 重写形式:

      • 在子类中定义了一个和父类(包括爷爷类及以上)同名的方法(参数也一样),即为对父类(包括爷爷类及以上)的方法重写

    • 重写之后子类调用同名方法,默认只会调用子类的

  • 类继承存在顺序链:

    • 定义类名时从左到右的顺序,左边是右边的子类

    • class SuperDog(SmallDog, BigDog): 这个继承顺序为 继承顺序:SuperDog->SmallDog->BigDog

  • 子类调用父类被重写方法:在子类方法中还想调用父类的同名方法。可以使用如下方式:

    • 父类名.同名方法(self, 形参1, ……)

    • super(顺序链目标类左边的类名, self).同名方法(...)

class SmallDog(object):
    def eat(self):
        print('吃小东西')
class BigDog(object):
    def eat(self):
        print('啃大骨头')

class SuperDog(SmallDog, BigDog):
    # todo 继承顺序:SuperDog->SmallDog->BigDog
    def eat(self):
        print('--方式1:父类名.同名方法(self, ...)--')
        # 需求1:调用 SmallDog 父类的 eat 方法
        SmallDog.eat(self)
        # 需求2:调用 BigDog 父类的 eat 方法
        BigDog.eat(self)
        print('--方式2:super(顺序链目标类左边的类名, self).同名方法(...)--')
        # 需求1:调用 SmallDog 父类的 eat 方法
        super(SuperDog, self).eat()
        # 需求2:调用 BigDog 父类的 eat 方法
        super(SmallDog, self).eat()
        # 本质:super(顺序链上目标父类左边的子类, self).目标父类的同名方法()
        print('--方式3:super().同名方法(...)--')
        # 需求1:调用 SmallDog 父类的 eat 方法
        super().eat() # 与super(SuperDog, self).eat()相同

# 类的继承顺序链:在python中,一个类可以继承多个类,
# 而且父类还可能有父类,对于这种情况,类的继承是有一个先后顺序,
# 这个顺序就叫继承顺序链。
# 如何查看一个类的继承顺序链:类名.__mro__
print(SuperDog.__mro__)

# 创建一个 SuperDog 对象
sd1 = SuperDog()
sd1.eat()

继承链

class A:
    def func(self):
        print('A类中的func函数')

class C(A):
    def func(self):
        super().func()
        print('C类中的func函数')

class B(A):
    # todo 在B看来,自己的爹永远是A
    def func(self):
        super().func()
        print('B类中的func函数')

class D(B, C):
    # todo 在D看来,B的爹是C!因为D声明的继承链是 D->B->C
    # todo 但这不影响在B的领域,B的爹始终A!
    def func(self):
        super().func()
        print('D类中的func函数')

print('在D看来,B的爹是C')
obj = D()
obj.func()
print('在B看来,B的爹是A')
obj_b = B()
obj_b.func()

# 输出如下
在D看来,B的爹是C
A类中的func函数
C类中的func函数
B类中的func函数
D类中的func函数
在B看来,B的爹是A
A类中的func函数
B类中的func函数

禁止私有继承

# 父类中的私有属性和方法不能被子类直接继承
# 在父类中提供公开方法,该方法中进行私有属性和方法的访问

class Animal(object):
    """动物类"""
    def __init__(self, _name, _age):
        self.name = _name
        self.__age = _age  # 私有属性

    # 私有方法
    def __pee(self):
        """pee:撒尿"""
        print('上厕所嘘嘘')

    def show_info(self):
        print(f'名字:{self.name},年龄:{self.__age}')

    def sleep(self):
        # 睡觉撒尿
        self.__pee()
        print('睡觉了💤')


class Dog(Animal):
    """狗类"""
    def test(self):
        # Animal 中的私有属性和方法不能被 Dog 类继承
        # print(self.__age)
        # self.__pee()

        self.show_info()
        self.sleep()


# 创建一个 Dog 对象
dog1 = Dog('旺财', 2)
dog1.test()

多态

同一个代码,由于传入实例对象的不同,最终有不同的调用效果!

class PayClass(object):
    """支付类"""
    def pay(self):
        """付款"""
        pass
    def refund(self):
        """退款"""
        pass

class Alipay(PayClass):
    """支付宝类"""
    def pay(self):
        print('支付宝付款')
    def refund(self):
        print('支付宝退款')

class WeChat(PayClass):
    """微信支付类"""
    def pay(self):
        print('微信付款')
    def refund(self):
        print('微信退款')


def func(obj):
    # 于传入的实例对象的不同
    # 最终产生了不同的调用(执行)效果
    obj.pay()

# 什么是多态:同一个代码,由于传入实例对象的不同,最终有不同的调用效果!
ap1 = Alipay()
wc1 = WeChat()
func(ap1)
func(wc1)

# Python是动态语言,站在代码写手的角度,
# 本身就是多态,不存在非多态的情况
def add(a, b):
    return a + b
res = add(1, 2)
print(res) # 3
res = add('hello', 'world')
print(res) # helloworld

封装进阶

类属性与实例属性

class Dog(object):
    # 记录一共创建了多少个Dog对象
    count = 0 # 类属性

    def __init__(self, _name, _age):
        self.name = _name # 实例属性
        self.age = _age # 实例属性
        self.sum = 0 # 实例属性
        self.count = 666 # 实例属性
        # self就是实例对象!

        # 只要 __init__ 被调用,就说明创建了 Dog 对象
        Dog.count += 1 # 类属性的调用

dog1 = Dog('小黑', 2)
print(Dog.count) # 1
print(dog1.count) # 666
dog2 = Dog('大黄', 3)
print(Dog.count) # 2
print(dog2.count) # 666
dog3 = Dog('小白', 1)
print(Dog.count) # 3
print(dog3.count) # 666

实例方法 类方法 静态方法

class Dog(object):
    count = 0 # 类属性

    def __init__(self, _name, _age):
        self.name = _name # 实例属性
        self.age = _age # 实例属性
        Dog.count += 1 # 实例化一次就自增 1

    # 实例方法:只能通过实例对象进行调用
    def show_info(self):
        print(f'我的名字:{self.name}, 年龄:{self.age}')

    # 需求:提供一个显示一共有多少只狗的方法
    # 类方法:使用@classmethod标记,通过类名调用
    @classmethod
    def show_dogs_count(cls):
        print(f'现在一共有{cls.count}只狗')

    # 静态方法:使用@staticmethod声明,逻辑上属于类,但是和类“没有关系”
    # 用不到类中的任何数据,可以没有参数。
    @staticmethod
    def readme():
        print('温馨提示:交易造成杀害,领养代替买卖')


dog1 = Dog('小黑', 2)
dog2 = Dog('大黄', 3)
dog1.show_info() # 实例方法调用
dog2.show_info() # 实例方法调用
Dog.show_dogs_count()  # 类方法调用
Dog.readme() # 静态方法调用

13-闭包装饰器

闭包

  • 函数嵌套(外部函数中定义了一个内部函数)

  • 外部函数返回了内部函数

  • 内部函数使用了外部函数的变量(包括外部函数的参数)

def func_outer():
    # 定义一个全局变量 count,来记录 sub 函数的调用次数
    count = 0

    def sub(a, b):
        """计算两个数之差"""
        nonlocal count
        count += 1 # count = count + 1
        print(f'第{count}次调用该函数')
        return a - b

    return sub

# 注意:这里调用外部函数,是为了拿到内部函数对象
func = func_outer()
print(func)
func(1, 2)
func(2, 3)
func(3, 5)

装饰器

import time

# 装饰器函数
def get_time(func):
    def wrapper(num):
        # todo: wrapper函数的参数 func函数的参数
        s1 = time.time()
        # todo:调用原始函数,调用时可以先获取原始函数的返回值
        result = func(num)
        s2 = time.time()
        print('执行时间:', s2 - s1)
        # todo:将原始函数的返回值再返回
        return result

    return wrapper

@get_time
def func1(num):
    _sum = 0
    for i in range(1, num+1):
        _sum += i
    return _sum

res = func1(100)
print(res)

通用装饰器

# 编写一个通用的装饰器,计算函数的执行时间,要求可以装饰任意函数(带任何参数或返回值)
import time

def get_time(func):
    # todo:给装饰器的内部函数,设置两种不定长形参
    # wrapper函数 其实就是 new_func 新的func函数(增加了额外功能!)
    def wrapper(*args, **kwargs):
        # todo: 增加代码,扩展功能
        s1 = time.time()
        # todo:将内部函数接收的参数,拆解原封不动的传递给func,同时接收func返回值
        result = func(*args, **kwargs)
        # todo:增加代码,扩展功能
        s2 = time.time()
        print(f'函数的执行时间为:{s2-s1}')
        # todo:将func的返回值,再进行返回
        return result
    return wrapper

# 语法糖
@get_time
def func1():
    """func1函数无参数,也无返回值"""
    _sum = 0
    for i in range(1000000):
        _sum += i
    return _sum

@get_time
def func2(m, n):
    """计算m-n之间的数字和,func2函数有两个参数,也有返回值"""
    result = 0
    # 遍历计算 m-n 之间的数字和
    for i in range(m, n + 1):
        result += i
    # 返回计算的结果
    return result

result1 = func1()
print(result1)
result2 = func2(1, 1000000)
print(result2)

带参装饰器

"""
@装饰器(参数)
本质:带参装饰器的本质,是在普通的装饰器的外面,又包了一层函数
"""
import time

"""普通装饰器 2层函数嵌套,无需最外层的函数包裹
装饰器带参数,就在2层基础上,再加一层,专门接收装饰器的参数
"""
def func_outer(float_decimals):
    """接收装饰器的参数"""
    """返回 下一层函数(名字)"""
    def get_time(func):
        """接收被装饰的函数"""
        """返回 内层函数(名字)"""
        def wrapper(*args, **kwargs):
            """接收被装饰函数的参数"""
            """对被装饰的函数额外加功能,会用到外层函数的参数"""
            """运行被装饰的函数"""
            """返回 被装饰函数运行的结果"""
            s1 = time.time()
            result = func(*args, **kwargs)
            s2 = time.time()
            # 控制小数的位数
            ret = round(s2-s1, float_decimals)
            print(f'函数的执行时间为:{ret}秒')
            return result
        return wrapper
    return get_time

@func_outer(float_decimals=3) # 控制输出的小数位数
def func1():
    """func1函数无参数,也无返回值"""
    _sum = 0
    for i in range(1000000):
        _sum += i
    return _sum

print(func1())

14-多进程多线程

  • 并发与并行

    • 并发:在一段时间内快速交替去执行多个任务(多线程)

    • 并行:在一段时间内真正的同时一起执行多个任务(多进程)

  • 进程(Process)

    • 是操作系统进行资源分配的基本单位

    • 进程可以有一个或多个子进程

    • 最原始的父进程是由操作系统提供的

  • 线程(Thread)

    • 是CPU进行调度的基本单位

    • 线程可以有一个或多个子线程

    • 线程是由进程主动创建出来的,创建第一次创建子线程时才会出现主线程

  • 线程的资源共享问题(只存在于低版本的Python解释器比如3.6.5,3.10已解决):当多个线程同时操作同一个共享的全局变量时,可能会造成错误的结果,解决办法如下

    • 线程同步:保证同一时刻只能有一个线程去操作共享资源(全局变量)

      • 线程等待thread.join()

        • 让一个线程完全执行结束,再去执行另一个线程

        • 缺点:一个执行完再执行另一个,和单任务几乎没有区别

      • 互斥锁:

        • 多个线程去抢同一把"锁",threading.Lock()抢到锁的线程执行,没抢到锁的线程会阻塞等待。

        • 缺点:虽然保障程序执行的多任务,如果频繁的加锁、释放锁会额外增加执行的时间消耗

    • 从场景下手:不用多线程做累加count操作,只做append操作!

  • 进程线程对比

    • 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。

    • 线程不能够独立执行,必须依存在进程中。

    • 创建进程的资源开销要比创建线程的资源开销要大。

    • 进程之间不共享全局变量,线程之间共享全局变量,但是在低版本的Python中要注意线程资源竞争的问题。

    • 进程稳定性高,适合计算密集型任务;线程适合IO密集型任务

    • 目前Python多线程不能利用CPU多核心优势,想利用CPU多核心的优势,Python只能采用多进程

多进程

多进程示例

import time
import os
import multiprocessing
# os.getpid():process id,获取进程的编号
# os.getppid():parent process id,获取父进程编号

# 跳舞函数
def dance():
    print(f'dance进程的编号:{os.getpid()},父进程编号:{os.getppid()}')
    for i in range(5):
        print('跳舞中...')
        # 休眠1秒钟
        time.sleep(1)

# 唱歌函数
def sing():
    print(f'sing进程的编号:{os.getpid()},父进程编号:{os.getppid()}')
    for i in range(5):
        print('唱歌中...')
        # 休眠1秒钟
        time.sleep(1)

if __name__ == '__main__':
    # 最原始的父进程是由操作系统提供的
    print(f'主进程的编号为:{os.getpid()},父进程编号:{os.getppid()}')
    # 创建两个进程,一个执行 dance 函数,另一个执行 sing 函数
    dance_process = multiprocessing.Process(target=dance)
    sing_process = multiprocessing.Process(target=sing)
    # 启动进程
    dance_process.start()
    sing_process.start()

多进程执行带有参数的任务

import multiprocessing
import time

# 带有参数的任务(函数)
def task(count):
    for i in range(count):
        print('任务执行中...')
        time.sleep(0.2)
    else:
        print('任务执行完成')

if __name__ == '__main__':
    # 传参方式1 args=(参数值1, ...)
    # task_process = multiprocessing.Process(target=task, args=(3,))
    # 传参方式2 kwargs={'形参字符串1': 值1, '形参字符串2': 值2, ...}
    task_process = multiprocessing.Process(target=task, kwargs={'count': 5})
    # 启动进程
    task_process.start()

进程之间不共享变量

import multiprocessing
import time

# 定义全局变量
g_list = []

# 添加数据的函数
def add_data():
    for i in range(5):
        g_list.append(i)
        print('add:', i)
        time.sleep(0.2)
    print('add:', g_list)

# 读取数据的函数
def read_data():
    print('read:', g_list)

if __name__ == '__main__':
    # 创建添加数据的子进程
    add_data_process = multiprocessing.Process(target=add_data)
    # 创建读取数据的子进程
    read_data_process = multiprocessing.Process(target=read_data)

    # 启动添加数据子进程
    add_data_process.start()
    # 阻塞等待:主进程等待 add_data_process 执行完成,再向下继续执行
    add_data_process.join()
    # 启动读取数据子进程
    read_data_process.start()
    # 主进程读取数据
    print('main:', g_list)

    # 主进程延时 1s
    time.sleep(1)
    print('主进程结束!')

子进程设为守护进程

主进程结束,设为守护进程的子进程主动GG

import multiprocessing
import time

def task():
    for i in range(10):
        print('任务执行中...')
        time.sleep(0.5)

if __name__ == '__main__':
    # 创建子进程并启动
    sub_process = multiprocessing.Process(target=task)
    # TODO:设置子进程为守护进程
    sub_process.daemon = True
    sub_process.start()

    # 主进程延时 1s
    time.sleep(1)
    print('主进程结束!')

主进程终止子进程

子进程被动GG

import multiprocessing
import time


# 任务函数
def task():
    for i in range(10):
        print('任务执行中...')
        time.sleep(0.5)


if __name__ == '__main__':
    # 创建子进程并启动
    sub_process = multiprocessing.Process(target=task)
    sub_process.start()

    # 主进程延时 1s
    time.sleep(1)
    print('主进程结束!')
    # TODO: 终止子进程
    sub_process.terminate()
    

线程

多线程示例

import time
import threading

# 跳舞函数
def dance(num):
    for i in range(num):
        print('跳舞中...')
        time.sleep(1)

# 唱歌函数
def sing(num):
    for i in range(num):
        print('唱歌中...')
        time.sleep(1)

if __name__ == '__main__':
    # 创建两个线程,分别执行 dance 和 sing
    dance_thread = threading.Thread(target=dance, args=(5,))
    sing_thread = threading.Thread(target=sing, kwargs={'num':5})
    # 启动线程
    dance_thread.start()
    sing_thread.start()

线程共用全局变量

import threading
import time

# 定义全局变量
g_list = []

# 添加数据的函数
def add_data():
    for i in range(5):
        g_list.append(i)
        print('add:', i)
        time.sleep(0.2)

    print('add:', g_list)

# 读取数据的函数
def read_data():
    print('read:', g_list)


if __name__ == '__main__':
    # 创建添加数据的子线程
    add_data_thread = threading.Thread(target=add_data)
    # 创建读取数据的子线程
    read_data_thread = threading.Thread(target=read_data)

    # 启动添加数据子线程
    add_data_thread.start()
    # 阻塞等待:主线程等待 add_data_thread 执行完成,再向下继续执行
    add_data_thread.join()

    # 启动读取数据子线程
    read_data_thread.start()
    # 阻塞等待:主线程等待 read_data_thread 执行完成,再向下继续执行
    read_data_thread.join()

    print('main:', g_list)

线程互斥锁

注释lock锁相关代码,并使用 低版本的Python解释器(python3.6.5) 才能看到线程资源安全问题的效果!Python 3.10 已经解决了该问题!

# 互斥锁:多个线程去抢同一把"锁",抢到锁的线程执行,没抢到锁的线程会阻塞等待
import threading

# 定义全局变量
g_num = 0

# 创建一个多线程互斥锁
lock = threading.Lock()

def sum_num1():
    global g_num
    # 循环一次给全局变量加1
    for i in range(1000000):
        # 抢到锁,代码可以继续向下执行,否则就会阻塞等待
        lock.acquire() # 抢锁
        g_num += 1
        lock.release() # 释放锁
    print('sum1:', g_num)


def sum_num2():
    global g_num
    # 循环一次给全局变量加1
    for i in range(1000000):
        # 抢到锁,代码可以继续向下执行,否则就会阻塞等待
        lock.acquire() # 抢锁
        g_num += 1
        lock.release() # 释放锁
    print('sum2:', g_num)

if __name__ == '__main__':
    # 创建两个线程
    first_thread = threading.Thread(target=sum_num1)
    second_thread = threading.Thread(target=sum_num2)
    # 启动两个线程
    first_thread.start()
    second_thread.start()

    # 阻塞等待:主线程等待子线程结束再向下运行
    first_thread.join()
    second_thread.join()

    print(g_num)

15-网络编程

  • IP地址

    • IPv4

    • IPv6

    • 查看本机的IP地址

      • win ipconfig

      • linux ifconfig

    • ping命令

      • ping www.baidu.com 查看是否能连通指定的网站

      • ping 192.168.1.222 查看是否能连通指定的IP

  • Port端口

    • 0-65535

  • TCP/IP协议

    • 传输数据之前要建立连接,通过三次握手建立:

      • 客户端 --> 服务端 ,SYN 客户端告诉服务端我是谁

      • 服务端 --> 客户端 , ACK + SYN

        • 服务端告诉客户端我收到了你的SYN(ACK)

        • 服务端告诉客户端我是谁 (SYN)

      • 客户端 --> 服务端 ,ACK

        • 服务端告诉客户端我收到了你的SYN ,同时确认你收到了我的SYN(ACK)

    • 开始传输数据 报文+内容

      • 报文类似元数据,描述数据从哪来、到哪去,数据大小、数据类型等

      • 内容:byteString类型,即bytes

  • bytes和str类型的互相转换

a = '哈萨克斯坦@#$%^&*(abc'
# str -> bytes类型(byte String)
# 按照utf8的格式,将str转为byteString类型
a_bytes = a.encode() # 默认utf8
print(a_bytes)

# bytes类型 -> str
a = a_bytes.decode() # 默认utf8
print(a)

""" b'\xe5' 
bytes 字节类型字符串 只是为了方便网络通讯 16进制数字来表述符号
【b】 表示bytes
【\xe5】 表示后边两个字符是16进制数字
"""

# 按照gbk的格式,将str转为byteString类型
b_bytes = a.encode('gbk')
print(b_bytes)
b = b_bytes.decode('gbk') # bytes类型 -> str
print(b)

# bytes数据一定可以转换为字符串吗???不一定(比如:图片、视频、音频等)

  • TCP服务端代码

跨服务器测试时:

  • 服务端代码和客户端代码必须在同一个网段之内!

  • 服务端绑定ip时,请使用 0.0.0.0;对应的客户端代码要连接的ip写服务端的IP

"""TCP 服务端"""
import socket

# ① 创建一个服务端socket套接字,负责接收客户端的请求(门迎)
# socket.AF_INET:使用IPV4的地址
# socket.SOCK_STREAM:使用TCP协议
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# ② 绑定服务端的ip地址和端口号
# ('ip地址', 端口号)
# server_socket.bind(('127.0.0.1', 8080))
server_socket.bind(('192.168.25.76', 8080))

# ③ 设置服务端进入监听状态
# 服务端同一时间支持多少个客户端向它发起连接请求
server_socket.listen(128)

# ④ 服务端等待客户端进行连接
print('服务端等待接收客户端的请求...')

# 没有客户端来连接服务端时,accept方法会阻塞等到,直到有客户端来连接,accept才会返回
# service_client_socket:也是一个 socket 对象,负责和对应的客户端进行通信(服务员)
# ip_port:是一个元祖,包含的是客户端的ip和port
service_client_socket, ip_port = server_socket.accept()
print(f'服务端来自{ip_port}客户端的连接...')


# ⑤ 服务端接收客户端发送的数据
# 如果客户端没有给服务端发送消息,recv也会阻塞等待
recv_msg = service_client_socket.recv(1024) # bytes
print(f'接收到来自客户端的消息:{recv_msg.decode()}')

# ⑥ 服务端给客户端回应数据
send_msg = input('请输入给客户端回应的消息:') # str
service_client_socket.send(send_msg.encode())

# ⑦ 关闭服务端的 socket
service_client_socket.close()
server_socket.close()

  • TCP客户端代码

"""TCP客户端"""
import socket

# ① 创建一个客户端的 socket 套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# ② 通过客户端 socket 连接服务端
client_socket.connect(('192.168.25.76', 8080))

# ③ 发送消息给服务端
send_msg = input('请输入发送给服务端的消息:')
client_socket.send(send_msg.encode())

# ④ 接收服务端回应的消息
recv_msg = client_socket.recv(1024) # bytes
print(f'接收到来自服务端的消息:{recv_msg.decode()}')

# ⑤ 关闭客户端 socket
client_socket.close()

16-正则表达式

re正则表达式,一种专门用来匹配目标字符串的规则

正则语法描述
.匹配任意1个字符,除了 \n
\d匹配1位数字,即0-9
\D匹配1位非数字
\s匹配1位空白符:空格、Tab
\S匹配1位非空白符
\w匹配1位非特殊字符:即a-z、A-Z、0-9、_、汉字
\W匹配1位特殊字符
[列举字符]匹配1个[ ]中列举的字符:[a-z]表示匹配1个小写英文字符
*匹配出现0次或任意次的一个字符:\d* 表示 0个或任意个连续的数字
+匹配出现1次或任意次的一个字符:\d+ 表示 1个或任意个连续的数字
?匹配出现1次或0次的一个字符:\d+? 表示 1个数字
{m}匹配出现m次的字符:\d{3} 表示 连续3个数字
{m,n}匹配出现从m到n次的字符:\d{2,5} 表示连续2到5个数字
^匹配字符串开头:^a 表示以a开头的
$匹配字符串结尾 :b$ 表示以b结尾
[^指定字符]匹配除了指定字符以外的所有字符 [^\d]+表示除了数字以外的字符
|匹配左右任意一个正则表达式 \d+|\W+ 表示数字或特殊字符

re.match()

从头匹配一个,无则None

"""match函数:尝试从字符串起始位置根据正则表达式匹配一个结果
re.match(pattern正则表达式, string目标字符串)
1.如果不能从起始位置匹配成功,则返回None;
2.如果能从起始位置匹配成功,则返回一个匹配的对象
"""
import re
my_str = 'abc_123_DFG_456_abc'
# 匹配字符串bc(注:从头开始)
res = re.match('bc', my_str)
print(res) # None

# 匹配字符串abc(注:从头开始)
res = re.match('abc', my_str)
print(res) # 匹配成功,返回一个 Match 对象
# Match对象.group():获取匹配的内容
print(res.group())
print('-----------')

re.search()

不从头匹配返回一个,无则None

"""search函数:根据正则表达式扫描整个字符串,并返回第一个成功的匹配
re.search(pattern, string, flags=0)
1. 如果不能匹配成功,则返回None;
2. 如果能匹配成功,则返回一个匹配对象
"""
import re
my_str = 'abc_123_DFG_456_abc'

# 匹配连续的3位数字 # \d{3}
res = re.search(r'\d{3}', my_str)
print(res.group())
res = re.search(r'bc', my_str)
print(res.group())

re.findall()

不从头匹配,用list返回所有

"""findall函数:根据正则表达式扫描整个字符串,并返回所有能成功匹配的子串
re.findall(pattern, string, flags=0)
1. 如果不能匹配成功,则返回一个空列表;
2. 如果能匹配成功,则返回包含所有匹配子串的列表
"""
import re
my_str = 'abc_123_DFG_456_abc'

# 匹配字符串中的所有连续的3位数字
res = re.findall(r'\d{3}', my_str)
print(res)

re分组

import re
"""
示例1:正则匹配分组操作
语法:(正则表达式)
"""
# 匹配手机号前3、中4、后4位数据
my_str = '13155667788'
# 131 5566 7788
# \d{3}\d{4}\d{4}
# (\d{3})(\d{4})(\d{4})

res = re.match(r'(\d{3})(\d{4})(\d{4})', my_str)
print(res)
print(res.group()) # 完整的匹配结果

# Match对象.group(组序号)
print(res.group(1)) # '131'
print(res.group(2)) # '5566'
print(res.group(3)) # '7788'
print('--------------')

"""
示例2:给正则分组起别名
语法:(?P<分组别名>正则表达式)
"""

# 需求:使用正则提取出 my_str 字符串中的 `传智播客` 文本
my_str = '<div><a href="https://www.itcast.cn" target="_blank">传智播客</a><p>Python</p></div>'

res = re.search('<a.*>(?P<text>.*)</a>', my_str)
print(res)
print(res.group()) # 完整匹配结果
print(res.group(1)) # 根据组序号取匹配的数据
print(res.group('text')) # 根据组别名取匹配的数据


"""
示例3:引用正则分组
语法:(?P<分组别名>正则表达式).*(?P=分组别名)
"""
import re
# 需求: 找到字符串里反复出现3次的连续的数字

my_str = 'a123jkfjkfjg123' # ==> None
my_str = '123aq123a123' # ==> 123
my_str = '123123123' # ==> 123
my_str = '123 123123' # ==> 123
res = re.match(r'(?P<num>\d+)\D*(?P=num)\D*(?P=num)$', my_str)

if res:
    print('匹配成功')
    print(res.group(1))
    print(res.group('num'))
else:
    print('匹配失败')

re匹配修饰符

改规则

import re
"""
re.I:匹配时不区分大小写
re.M:多行匹配,影响 ^ 和 $
re.S:影响 . 符号,设置之后,.符号就能匹配\n了
"""

# re.I:匹配时不区分字母的大小写
my_str = 'aB'
res = re.match('ab', my_str, flags=re.I)
print(res.group())
print('----------------')

# re.M:开启多行匹配模式,把每一行字符串,当作一个独立的字符串进行匹配
my_str = 'aabb\nbbcc'
res = re.findall('^[a-z]{4}$', my_str, flags=re.M)
print(res)
res = re.findall('^[a-z]{4}$', my_str)
print(res)
res = re.findall('[a-z]{4}', '11aabb')
print(res)
res = re.findall('^[a-z]{4}$', '11aabb')
# 被匹配的字符串必须以字母开头以字母结尾
print(res)
print('----------------')

# re.S:让 . 也能匹配\n
my_str = '\nabc'
res = re.match('.', my_str, flags=re.S)
print(res)

# 多模式:flags=re.S|re.M|re.I
my_str = '1111\nabc'
res = re.findall('.', my_str, flags=re.S|re.M|re.I)
print(res)

re贪婪非贪婪

import re
"""
贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
正则中的量词包括:{m,n}、?、*和+,这些量词默认都是贪婪模式的匹配
可以在这些量词后面加?将其变为非贪婪模式。
"""
my_str = '<div>test1</div><div>test2</div>'
# 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
re_obj = re.match('<div>.*</div>', my_str)
print(re_obj.group()) # 获取整个正则表达式匹配的内容
print('----')
# 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
re_obj = re.match('<div>.*?</div>', my_str)
print(re_obj.group()) # 获取整个正则表达式匹配的内容

# \d{2,5}? == \d{2} != \d{2,5}
my_str = '221324324324242'
re_obj = re.match('\d{2,5}?', my_str)
print(re_obj.group())
re_obj = re.match('\d{2,5}', my_str)
print(re_obj.group())
re_obj = re.match('\d{2}', my_str)
print(re_obj.group())

re切割和替换

import re
# re.split(pattern, string, maxsplit, flags)
# 作用:对字符串进行分割
# 过程:先使用正则对字符串进行匹配,正则匹配到的内容作为分割符,对字符串进行分割
str1 = 'hello-python_hive'
res1 = re.split('[-_]', str1)
print(res1) # ['hello', 'python', 'hive']

# re.sub(pattern, repl, string, count, flags)
# 作用:对字符串中的内容进行替换
# 过程:先使用正则对字符串进行匹配,然后将匹配到的内容进行替换,返回替换之后的新字符串
str2 = 'hello-python_hive' # 'hello:python:hive'
res2 = re.sub('[-_]', ':', str2)
print(res2) # hello:python:hive

17-pymysql模块

查询

"""
pymysql获取查询结果
学习目标:常握 pymysql 获取SQL查询结果
"""
# ① 导入模块
import pymysql # pip install pymysql

# ② 创建连接
conn = pymysql.connect(host='192.168.88.131', port=3306,
                       user='root', password='itcast',
                       database='test', charset='utf8')
# ③ 创建游标
cursor = conn.cursor()
# ④ 执行SQL
sql = 'select * from students'
num = cursor.execute(sql) # 返回行数
# cursor.rownumber:记录当前获取的行号
print(f'受影响的行数:{num}')
# 获取 sql 查询的结果
# 返回2条数据
res = cursor.fetchmany(2)
print(res)
# 接着 返回1条数据
res = cursor.fetchone()
print(res)
# 接着 返回全部数据
res = cursor.fetchall()
print(res)
# 接着 返回一条数据,但没有了,只能是None
res = cursor.fetchone()
print(res)
print(cursor.rownumber) # 当前行数
# ⑤ 关闭游标
cursor.close()
# ⑥ 关闭连接
conn.close()

增删改

"""
pmysql增、删、改操作
学习目标:掌握 pymysql 对 mysql 数据库进行增、删、改操作
"""
# ① 导入模块
import pymysql
# ② 创建连接
conn = pymysql.connect(host='192.168.88.131', port=3306,
                       user='root', password='itcast',
                       database='test', charset='utf8')
# ③ 创建游标
cursor = conn.cursor()
# 开启事务
conn.begin()
# ④ 执行SQL
insert_sql = 'insert into students values("Tom", 25, "male", 2.9)'
cursor.execute(insert_sql)
select_sql = 'select * from students'
cursor.execute(select_sql)
# 获取查询的结果
res = cursor.fetchall()
print(res)
# 提交事务
conn.commit()
# ⑤ 关闭游标
cursor.close()
# ⑥ 关闭连接
conn.close()
# DELETE FROM students WHERE name = 'Tom';
# SELECT * FROM students;

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值