Python基础语法2与常见问题

Python基础语法2与常见问题

文章目录

    • Python基础语法2与常见问题
        • 一、注释
        • 二、变量与常量
        • 三、输出
          • 1、格式化输出
            • 1)"~{}".format(a)
            • 2)'%s','%.1f'
          • 2、转义字符
        • 四、运算符
          • 1、幂运算
        • 五、基本数据类型
          • 1、浮点数取整数
          • 2、布尔类型
        • 六、字符串
          • 1、修改字符串的大小写
          • 2、字符串判断
          • 3、字符串分割
          • 4、字符串切片
          • 5、字符串其他方法
        • 七、基本类型转换
        • 八、逻辑运算(补充)
        • 九、对象类型转换
        • 十、面向过程与函数
          • 1、内建函数
            • 1)如何学会看API
          • 2、自定义函数
            • 1)关键字实参
            • 2)参数默认值
            • 3)任意数量的形参
            • 4)返回多个值
            • 5)限定方法的参数类型
        • 十一、类与继承
          • 1、基本概念
          • 2、super()的调用
        • 十二、列表
          • 1、列表的输出/遍历
          • 2、列表的访问
          • 3、列表的插入
            • 1)用insert()/append()插入一个元素
            • 2)插入多个元素
          • 4、列表的修改
          • 5、列表的删除
            • 1)用del,pop()根据索引位置删除:
            • 2)用remove()根据值删除:
          • 6、列表的排序
            • 1)用sort方法永久排序:
            • 2)用sorted函数临时排序:
          • 7、列表的切片
          • 8、列表的复制和引用
          • 9、列表的其他操作
            • 1)列表的复制方法copy()
            • 2)统计元素个数方法count()
            • 3)获取元素索引位置方法index()
            • 4)列表倒置reverse(),获取列表长度len()函数,list定义
            • 5)列表拼接
            • 6)元素判断
            • 7)join()方法将列表拼接成字符串
        • 十三、元组
          • 1、创建元组
            • 1)直接创建
            • 2)用**,**隔开创建元组
            • 3)用tuple()函数类型转换
          • 2、元组的拼接
            • 1)元组套元组
            • 2)元组拼接
          • 3、元组的修改(列表可改)
          • 4、元组的删除(删除对象)
          • 5、元组拆包
            • 1)普通元组拆包
            • 2)嵌套元组拆包
            • 3)剔除不想要的变量
            • 4)交换两变量
          • 6、元组的其余操作
            • 1)值出现次数
            • 2)最大最小值
            • 3)值的索引
        • 十四、小技巧
          • 1、for写在一行
            • 1)for可以加if写在一行
            • 2)list转dict - list元素作为key,索引作为值
          • 2、循环打印列表
          • 3、获取字典中最大值及对应的键
          • 4、判断dict当中key是否存在(两种方法)
          • 5、三目运算符
          • 6、自定义异常
          • 7、线程运行 & 线程同步
            • 1)线程运行
            • 2)线程同步(★★★★★)
            • 3)线程池
          • 8、进程运行 & 同步 & 返回值
            • 1)多进程的运行 - start
            • 2)多进程的同步 - join
            • 3)共享数据 - Queue & Manager
            • 4)多进程实现生产者消费者问题
          • 9、Queue实现生产者消费者问题
          • 10、进程池
            • 1)进程池常见命令
            • 2)Flask中使用进程池
            • 3)apply_async中的函数未执行
            • 4)managers.RemoteError报错解决
            • 5)ProcessPoolExecutor
          • 11、类加载,类对象调用类内方法时间开销问题
            • 1)实验1
            • 2)实验2
            • 3)实验3
            • 4)小总结
          • 12、假言assert & self.assertTrue
          • 13、python中import和`__import__`的区别(python反射)
          • 14、python反射机制 (`__import__`,`__getattr__`)
          • 15、时间,日期,时间戳的转换
          • 16、实例对象如果初始化返回True
          • 17、lambda表达式
            • 1)lambda是一个匿名函数
            • 2)lambda使用说明
            • 3)使用lambda表达式对list按属性值排序
          • 18、APScheduler实现定时任务
            • 1)循环 sleep(阻塞)
            • 2)Timer
            • 3)APScheduler定时框架
          • 19、With用法
          • 20、格式化输出
          • 21、python模块的导入 & `__all__`
            • 1)实验1
            • 2)实验2
            • 3)实验3
          • 22、python注解
            • 1)python注解(实验1)
            • 2)python注解(实验2)
            • 3)@classmethod & @staticmethod
          • 23、迭代器和生成器
            • 1)迭代器
            • 2)生成器
          • 24、`ModuleNotFoundError`报错
            • 1)从`..config`导入模块报`ModuleNotFoundError`错
            • 2) No module named `__main__.XX`
          • 25、python导入`.so`文件
          • 26、不可变映射类型 - (字典)MappingProxyType
          • 27、python实现消息队列
          • 28、nginx部署flask应用
          • 29、python for中zip的使用
          • 30、python生成requirements.txt
          • 31、字符串前面自动补零(zfill)
          • 32、python过滤器的使用
          • 33、`__call__`方法的使用(“实例对象 -> 可调用对象”)
          • 34、python截屏
          • 35、python获取文件夹下所有文件及子文件夹
            • 1)获取文件夹下所有文件
            • 2)获取文件夹下当前所有子文件夹
            • 3)文件重命名和复制
          • 36、文件导入拼接项目根路径
          • 37、python GIL特性
          • 38、python @dataclass的使用
          • 39、python处理异常
          • 40、对象列表按某个属性排序

跳转python"小技巧"

一、注释

"""..."""'''...'''实现多行注释

#... 实现当行注释

"""
多行注释1
"""
'''
多行注释2
'''
# 当行注释
print("不是任何地方都需要写注释,适宜如下:新的语法点,重要细节结论,实现某个功能等")

二、变量与常量

​ 关于python中的常量,并没有像C++里用const关键字来约束。但是我们平时通常用大写字母来命名常量,例如

PI = 3.14159265359
MAX_VALUE = 100

​ 但事实上 PI 仍然是一个变量,Python 根本没有任何机制保证 PI 不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果你一定要改变变量 PI 的值,也没人能拦住你。

三、输出

1、格式化输出

参考python基础_格式化输出(%用法和format用法)

1)“~{}”.format(a)
# format()可以在{}的地方替换变量的值
# python3.6前的format方法:在()中写格式化的值
a,b = 0.2,0.1
print("a + b = {}".format(a+b))

# python3.6的format方法:str前写'f',在{}中写格式化的值
print(f"a + b = {a+b}")
---
a + b = 0.30000000000000004
a + b = 0.30000000000000004
2)‘%s’,‘%.1f’
name = "wangxiaoxi"
print('hello %s' % name)

num = 1.1
print("num = %.4f" % num)
---
hello wangxiaoxi
num = 1.1000
2、转义字符

​ 可以在字符串前面添加一个r,表示一个原始的字符串。这样python就不会特殊处理字符串。

print("C\Lenovo\name")
print("C\\Lenovo\\name")

#如果不实用反斜杠转义,可以在字符串前面添加一个r,表示一个原始的字符串
print(r"C\Lenovo\name")
---
C\Lenovo
ame
C\Lenovo\name
C\Lenovo\name

四、运算符

1、幂运算
power = 7 ** 2
power1 = 7.0 ** 2
print(f"7的2次方={power}")
print(f"7.0的2次方={power1}")
---
72次方=49
7.02次方=49.0

五、基本数据类型

1、浮点数取整数
salary = 8050.53
# 向下取整:int()
salary_int = int(salary)
print(f"向下取整={salary_int}")

# 四舍五入:round(),第二个参数是保留小数点后多少位,默认是0
salary_round = round(salary,1)
print(f"四舍五入(保留一位小数)={salary_round}")
salary_round = round(salary)
print(f"四舍五入={salary_round}")

# 向上取整:math.ceil()
import math
salary_ceil = math.ceil(salary)
print(f"向上取整={salary_ceil}")
---
向下取整=8050
四舍五入(保留一位小数)=8050.6
四舍五入=8051
向上取整=8051
2、布尔类型

布尔类型与其他数据类型做与或非运算

python把0,空字符串’'和None看成False,其他数值和非空字符串都看成True。这里参考python布尔类型的短路运算https://www.cnblogs.com/chengjian-physique/p/7858496.html

好处是在写循环时,短路计算可以减少条件的判断,优化代码

a = True
print(a and 'a = T' or 'a = F')

#print(True and False or 'a = T')
print(a and '' or 'a = T')
print(a and 0 or 'a = T')
print(a and None or 'a = T')
---
a = T
a = T
a = T
a = T

六、字符串

1、修改字符串的大小写
# A: 修改字符串的大小写 title()、capitalize()、lower() 、upper()
str1 = "welcome to CHINA"
# 每个单词首字母大写
print(str1.title())
# 段落首写字母大写
print(str1.capitalize())
# 字符串转小写
print(str1.lower())
# 字符串转大写
print(str1.upper())

# 大写转小写,小写转大写 swapcase()
print(str1.swapcase())
---
Welcome To China
Welcome to china
welcome to china
WELCOME TO CHINA
WELCOME TO china
2、字符串判断
# B: 字符串字符判断
# String.isalnum()判断字符串中是否全部为数字或者英文,符合返回True,否则为False,如果有符号或空格也会返回False
print(str1.isalnum())
# String.isdigit()判断字符串是否全为整数
print(str1.isdigit())
---
False
False
3、字符串分割
# C: 字符串分割
str2 = "We will do better than ever before \n Fighting "
# 根据空格,换行符切分
print(str2.split())
# 根据指定字符切分
print(str2.split('n'))
# 根据换行符进行切分
print(str2.splitlines())
---
['We', 'will', 'do', 'better', 'than', 'ever', 'before', 'Fighting']
['We will do better tha', ' ever before \n Fighti', 'g ']
['We will do better than ever before ', ' Fighting ']
4、字符串切片
# - 字符串切片[start : end : step]
str4 = "Python"
print(str4[1:2])
print(str4[-1])
print(str4[-2])
print(str4[-2:])
print(str4[::-1])
---
y
n
o
on
nohtyP
5、字符串其他方法
# D: 字符串删除两边空白
# - 删除字符串两边的空白strip()、lstrip()、rstrip()
str3 = " just do it "
print(str3.strip())
print(str3.lstrip())
print(str3.rstrip())
---
just do it
just do it
 just do it

# - 其他len()、replace()
# len()求字符串长度时,空格也算一个字符
print(len(str3))
print(str3.replace(' ','-'))
---
12
-just-do-it-

七、基本类型转换

float(a)将变量a转化为浮点数,

int(b)将变量b转为整数

str©将变量c转为字符串

其中变量a,b,c为任意基本数据类型(数据结构不行)

八、逻辑运算(补充)

and = &, or = |

print(True and False)
False

print(True & False)
False

print(True or False)
True

print(True | False)
True

九、对象类型转换

注意和Java对象类型转换区分开

rom pandas import read_csv
from pandas import Series

df = read_csv(
    open(r"E://python/数据集/数据分析入门/data3.csv")
)

'''一列可作为Series,会自动添加缺失的索引'''
# 该语法会报错:Series s = df['name']
s = Series(df['name'])
type(df['name'])

十、面向过程与函数

  • 1、函数要手动传self,方法不用传self

  • 2、在面向过程的程序设计里面,函数是基本单元,利于分工合作。

  • 3、如果是一个函数,用类名去调用,如果是一个方法,用对象去调用。下面以列表操作为例子

    '''E:列表的删除'''
    #用del函数
    del list[0]
    print(f"新列表元素如下6:{list}")
    ---
    新列表元素如下6:['小王', '小李子', '小张', '小红', '大表哥']
    
    #用pop方法(有返回值,且默认删除末尾元素)
    a = list.pop()
    print(a)
    print(f"新列表元素如下6:{list}")
    ---
    新列表元素如下6:['小王', '小李子', '小张', '小红']
    
    b = list.pop(0)
    print(b)
    print(f"新列表元素如下7:{list}")
    ---
    新列表元素如下7:['小李子', '小张', '小红']
    
1、内建函数

参考2. 内置函数

或者在Zeal中搜索Built-in Functions

1)如何学会看API
  • 1、找到官方文档
  • 2、查看函数的参数表示的意思
  • 3、一定要记住个数和位置!!!

map()函数为例:map(function, iterable, ...)

其中第一个参数是函数(比如int()内置函数),第二个参数是可迭代的对象(比如list对象)。

list_float = [0.78,8.5,9.6,2.3]
list_int = list(map(int,list_float))
print(list_int)
---
[0, 8, 9, 2]
2、自定义函数
1)关键字实参
def myfunction(name,age):
    print(name,"的年龄为",age,"岁")

myfunction(name="王小希",age=18)
---
王小希 的年龄为 18#可以先考虑实参的顺序,然后再通过关键字实参的调用方式
myfunction("王小希",age=18)
---
王小希 的年龄为 18

Notes:

  • 每一个实参都有变量名与值组成,并且不用考虑函数形参的顺序,调用形式:形参名字=“值”
  • 优点:无需考虑实参的顺序,易错点:形参的名字一定要正确。
  • 可以先考虑实参的顺序,然后再通过关键字实参的调用方式
2)参数默认值
def myfunction(name,age=18):
    print(name,"的年龄为",age,"岁")

myfunction("王小希")
myfunction("王小希",age=23)
---
王小希 的年龄为 18 岁
王小希 的年龄为 23
3)任意数量的形参
  • 使用*来实现接受任意多个形参
def myfunction1(*names,age=18):
    for name in names:
        print(name,"的年龄为",age,"岁")

myfunction1("王小希","王小明","罗小黑")
---
王小希 的年龄为 18 岁
王小明 的年龄为 18 岁
罗小黑 的年龄为 18
  • 使用**来实现接受任意多个形参
def myfunction3(**options):
    print(type(options))
    for key in options:
        print(options[key])

dict={'key1':"json1",'key2':"json2"}
print(type(dict))
'''法1:传入 参数名 = value'''
myfunction3(key1="json",key2="json2")
'''法2:直接传入字典,但是记得字典前要加**'''
myfunction3(**dict)

Notes

  • 函数会创建一个空元组/空字典,接受任意数量的参数值

  • python可以通过使用"*“,也可以使用**”**“实现接受任意值多个形参**

    • 主要在于*参数传入的为一个元组
    • **参数出入的则为一个字典。
4)返回多个值
def myfunction2():
    name1 = "王小希",
    name2 = ("王小明","罗小黑")
    return name1,name2

name1,name2 = myfunction2()
print(name1,name2)
---
('王小希',) ('王小明', '罗小黑')

Notes

  • python将多个返回值封装成一个元组
  • 通过元组拆包的方式获取里面的值。
5)限定方法的参数类型

参考python限定方法参数类型、返回值类型、变量类型等

经常使用类型

  • int, long, float: 整型,长整形,浮点型ide
  • bool, str: 布尔型,字符串类型函数
  • List, Tuple, Dict, Set: 列表,元组,字典, 集合学习 (from typing import )
  • Iterable, Iterator: 可迭代类型,迭代器类型ui
  • Generator:生成器类型
from typing import List
from util.annotation import constraint
from model.basic.trace import Trace

'''
4大约束条件判别:
- 安全间隔约束
- 优先级约束
- 落地时段约束
- 起飞时段约束
'''
@constraint(value="Constraint1")
# 单一安全间隔约束
def aqjg_constraint(trace1: Trace, trace2: Trace, hjbm_2_index_dict: dict, aqjg_matrix: List[List]) -> bool:
    # 航线1先起飞,航线2后起飞
    row = hjbm_2_index_dict[trace1.hjbm]
    col = hjbm_2_index_dict[trace2.hjbm]
    if (abs(trace2.qf_time - trace1.qf_time) < aqjg_matrix[row][col]):
        return False
    return True

Note

python限定方法参数类型可以减少方法注释,但是python并不会检查参数类型是否符合,如果参数类型为List[List]二维数组,List一维数组照样可以传进去

十一、类与继承

1、基本概念
  • 继承的类为父类,被继承的类为子类
  • 子类继承负类中所有方法,同时还可以有自己的属性和方法

下面代码的核心部分包括

  • 会员类在继承人类时:类后加括号表继承,即class Member (People):

  • 子类(会员类)在初始化对象时,要调用父类(人类)的初始化方法,即

    def __init__(self, name, location, career, hope):
            super().__init__(name, location, career)
            self.hope = hope
    
  • 类中的方法需要显式传入self,表明该方法是类对象的行为,这是方法和函数的区别。

  • 子类可以重写父类的方法

class People:
    """定义人类"""

    def __init__(self, name, location, career):
        self.name = name
        self.location = location
        self.career = career
        self.age = 18  # 默认的属性值,不需要在init方法列表体现

    def introduce_you(self):
        # format()方法的另外一个用法,构造消息。也可以把消息写在一个函数里面,有兴趣的圈友可以试一下
        introduce = ' Python 实战圈的圈友们好,我是{n},今年{a},来自{l}.我的工作是{c},很高兴认识大家!'
        mess = introduce.format(n=self.name,
                                l=self.location,
                                a=self.age,
                                c=self.career)
        print(mess)

    def read_age(self):
        # 读取年龄属性
        print('{}今年{}岁'.format(self.name, self.age))

    def update_age(self, new_age):
        # 更新属性的方法
        if new_age < 0:
            print('年龄不能为负数')
        else:
            self.age = new_age


"""
    定义会员类,继承类父类People
    会员类,有自己的属性introduction
"""


class Member (People):
    """会员类,继承人类"""

    def __init__(self, name, location, career, hope):
        super().__init__(name, location, career)
        self.hope = hope

    def introduce_you(self):
        """ 重写父类的方法"""
        introduce = ' Python 实战圈的圈友们好,我是{n},今年{a},来自{l}.我的工作是{c},很高兴认识大家!在咱们圈子里,我希望自己能 {h}.'
        mess = introduce.format(n=self.name,
                                l=self.location,
                                a=self.age,
                                c=self.career,
                                h=self.hope
                                )
        print(mess)

    def set_hope(self, hope):
        """定义子类的方法"""
        self.hope = hope
        print("{}的希望是{}".format(self.name, self.hope))


# 实例化对象
Grace = Member("Grace", "上海", '数据分析师', '彻底学会Python')
# 实现自我介绍
Grace.introduce_you()
# 实现更新年龄
Grace.update_age(19)
# 调用方法更新年龄
Grace.read_age()
# 请修改你的祝福
2、super()的调用

这里有个疑惑,请看这段代码:

#Define model
class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork,self).__init__()

解读参考self参数 - __ init__ ()方法 super(Net, self).init()是什么

但是为什么super()里要传子类实例,可以不写吗?

十二、列表

它是由一系列按特定顺序排列的元素组成(有序集合),参考python列表的常用操作方法

1、列表的输出/遍历
list = ['小王','小李','小张','小红']

'''A:列表的输出'''
print("列表元素如下1:{}".format(list))
---
列表元素如下1:['小王', '小李', '小张', '小红']

print(f"列表元素如下2:{list}")
---
列表元素如下2:['小王', '小李', '小张', '小红']

'''列表循环输出'''    
for i in List:
     print i,
2、列表的访问
'''B:列表的访问'''
print(list[0])
---
小王

print(list[-1])
---
小红
3、列表的插入
1)用insert()/append()插入一个元素
'''C:列表的插入'''
'''按索引位置插入'''
list.insert(0,'小表弟')
print(f"新列表元素如下3:{list}")
---
新列表元素如下3:['小表弟', '小王', '小李', '小张', '小红']

'''末尾追加(在构建空列表,网络爬虫中较为常用)'''
list.append('大表哥')
print(f"新列表元素如下4:{list}")
---
新列表元素如下4:['小表弟', '小王', '小李', '小张', '小红', '大表哥']
2)插入多个元素
'''B:某个位置插入多个元素'''
list1[3:2] = ['小李子1','小李子2']
---
['小表弟', '小王', '小李','小李子1', '小李子2', '小张', '小红']
4、列表的修改
'''D:列表的修改'''
#按索引位置修改元素
list[2] = '小李子'
print(f"新列表元素如下5:{list}")
---
新列表元素如下5:['小表弟', '小王', '小李子', '小张', '小红', '大表哥']
5、列表的删除
1)用del,pop()根据索引位置删除:
'''E:列表的删除'''
'''根据位置删除'''
#用del函数
del list[0]
print(f"新列表元素如下6:{list}")
---
新列表元素如下6:['小王', '小李子', '小张', '小红', '大表哥']

#用pop方法(有返回值,且默认删除末尾元素)
a = list.pop()
print(a)
print(f"新列表元素如下6:{list}")
---
新列表元素如下6:['小王', '小李子', '小张', '小红']

b = list.pop(0)
print(b)
print(f"新列表元素如下7:{list}")
---
新列表元素如下7:['小李子', '小张', '小红']
2)用remove()根据值删除:
'''根据值删除'''
list.append('小红')
print(f"新列表元素如下8:{list}")
---
新列表元素如下8:['小李子', '小张', '小红', '小红']

#remove只能删除第一个值=xx的元素
list.remove('小红')
print(f"新列表元素如下9:{list}")
---
新列表元素如下9:['小李子', '小张', '小红']
6、列表的排序
1)用sort方法永久排序:
'''F:列表的排序'''
'''1、永久排序'''
list1 = ['p','f','b','a','c','f','g']
list1.sort()
print('升序',list1)
---
升序 ['a', 'b', 'c', 'f', 'f', 'g', 'p']

list1 = ['p','f','b','a','c','f','g']
list1.sort(reverse=True)
print('降序',list1)
---
降序 ['p', 'g', 'f', 'f', 'c', 'b', 'a']
2)用sorted函数临时排序:
'''2、临时排序'''
list1 = ['p','f','b','a','c','f','g']
list1_1 = sorted(list1)
print(list1_1)
---
['a', 'b', 'c', 'f', 'f', 'g', 'p']
print(list1)
---
['p', 'f', 'b', 'a', 'c', 'f', 'g']
7、列表的切片
list1 = ['小表弟', '小王', '小李', '小张', '小红', '大表哥']

'''A: 切片概念(默认:start = 0,end = -1,step = 1 表示从左到右的方向'''
a = list1[2:4]
---
['小李', '小张']

b = list1[:4]
---
['小表弟', '小王', '小李', '小张']

c = list1[4:]
---
['小红', '大表哥']

'''获取最后三个元素'''
d = list1[-3:]
---
['小张', '小红', '大表哥']

'''获取偶数位置元素'''
e = list1[::2]
---
['小表弟', '小李', '小红']

'''获取奇数位置元素'''
f = list1[1::2]
---
['小王', '小张', '大表哥']

'''逆序获取元素'''
g = list1[::-1]
---
['大表哥', '小红', '小张', '小李', '小王', '小表弟']
8、列表的复制和引用
'''C:复制列表:切片复制等价copy(),=赋值等价于引用'''
list2 = list1[:]
list3 = list2
list2.append('小捣蛋')
list1
---
Out[25]: ['小表弟', '小王', '小李', '小李子1', '小李子2', '小张', '小红', '大表哥']

list2
---
Out[26]: ['小表弟', '小王', '小李', '小李子1', '小李子2', '小张', '小红', '大表哥', '小捣蛋']

list3
---
['小表弟', '小王', '小李', '小李子1', '小李子2', '小张', '小红', '大表哥', '小捣蛋']

备注:

list3是list2的引用,list2修改了,list3也会发生修改

9、列表的其他操作
'''G:列表的其他操作'''
list1 = ['p','f','b','a','c','f','g']
1)列表的复制方法copy()
'''1、复制列表copy()'''
list2 = list1.copy()
print(list2)
# ['p', 'f', 'b', 'a', 'c', 'f', 'g']
2)统计元素个数方法count()
'''2、统计列表中某元素个数'''
print('f出现的次数:',list2.count('f'))
# f出现的次数: 2
print('b出现的次数:',list2.count('b'))
# b出现的次数: 1
3)获取元素索引位置方法index()
'''3、找出某一个元素的索引位置:index'''
print('f的索引位置:',list2.index('f'))
4)列表倒置reverse(),获取列表长度len()函数,list定义
'''4、颠倒顺序'''
print("原:",list2)
list2.reverse()
print("反:",list2)

'''列表长度'''
len(list2)

'''list()函数定义列表'''
list4 = list('hello')
---
['h', 'e', 'l', 'l', 'o']
5)列表拼接
'''列表的拼接(三种方法)'''
list1 = ['a','b','c']
list2 = ['python','is','my','favorite']
'''+法'''
list3 = list1 + list2
---
list3:Out[9]: ['a', 'b', 'c', 'python', 'is', 'my', 'favorite']

'''extend()'''
list1.extend(list2)
---
list1: Out[11]: ['a', 'b', 'c', 'python', 'is', 'my', 'favorite']

'''*法'''
list4 = list2 * 3
---
Out[15]: 
['python',
 'is',
 'my',
 'favorite',
 'python',
 'is',
 'my',
 'favorite',
 'python',
 'is',
 'my',
 'favorite']

比较两种列表拼接:

list1 = ['p', 'y', 't', 'h', 'o', 'n'].extend(['python', 'Java', 'C++'])
print(type(list1))
---
<class 'NoneType'>

list2 = ['p', 'y', 't', 'h', 'o', 'n']
list3 = ['python', 'Java', 'C++']
list2.extend(list3)
print(list2)
---
['p', 'y', 't', 'h', 'o', 'n', 'python', 'Java', 'C++']

Notes:为什么上面第一种方法实现不了列表拼接。

假设1:'[]'一开始不是列表,是在赋值给变量时自动转换成列表的

假设2:list.extend()无返回值

print(type(['p', 'y', 't', 'h', 'o', 'n']))
---
<class 'list'>

print(type(['p', 'y', 't', 'h', 'o', 'n'].extend(['python', 'Java', 'C++'])))
---
<class 'NoneType'>

print(type(list(['p', 'y', 't', 'h', 'o', 'n']).extend(['python', 'Java', 'C++'])))
---
<class 'NoneType'>

list3 = ['python', 'Java', 'C++']
print(type(list3))
<class 'list'>

经验证,假设1错误,假设2成立

6)元素判断
'''判断元素是否在列表中 in / not in'''
val = '大王' in list1
---
Out[5]: False

val1 = '大王' not in list1
---
Out[6]: True
7)join()方法将列表拼接成字符串
'''join()方法将序列中的元素以指定的字符连接生成一个新的字符串'''
linked = '-'
linked.join(list1)
---
Out[32]: '小表弟-小王-小李-小李子1-小李子2-小张-小红-大表哥-小捣蛋'

十三、元组

Notes

元组通常是不能被修改,而列表是可以

1、创建元组
1)直接创建
'''1、直接使用'''
tup1 = 1,2,3
print("tup1:",type(tup1))
---
tup1: <class 'tuple'>


'''2、用()'''
tup2 = (1,2,3)
print("tup2:",type(tup2))
---
tup2: <class 'tuple'>
2)用**,**隔开创建元组
tup3 = (1)
print("tup3:",type(tup3))
---
tup3: <class 'int'>


'''3、用,隔开'''
tup4 = (1,)
print("tup4:",type(tup4))
---
tup4: <class 'tuple'>
3)用tuple()函数类型转换
'''用tuple()函数'''
'''4、字符串转元组'''
print(tuple("python"))
---
('p', 'y', 't', 'h', 'o', 'n')


'''5、列表转元组'''
print(tuple(['python','Java','C++']))
---
('python', 'Java', 'C++')
2、元组的拼接
1)元组套元组
'''6、元组里包含元组'''
tup5 = ('p', 'y', 't', 'h', 'o', 'n'),('python', 'Java', 'C++')
print(tup5)
---
(('p', 'y', 't', 'h', 'o', 'n'), ('python', 'Java', 'C++'))
2)元组拼接
tup6 = ('p', 'y', 't', 'h', 'o', 'n') + ('python', 'Java', 'C++')
print(tup6)
---
('p', 'y', 't', 'h', 'o', 'n', 'python', 'Java', 'C++')

Notes:列表的拼接可以用extend,但是元组拼接没有

3、元组的修改(列表可改)
'''元组不能修改'''
tup = (1,[1,2,3,4],'str')
tup[2] = 'java'
print(tup)
---
TypeError: 'tuple' object does not support item assignment

'''修改元组里的列表'''
tup = (1,[1,2,3,4],'str')
tup[1].append(5)
print(tup)
---
(1, [1, 2, 3, 4, 5], 'str')

Notes

元组里的元素一般是不能修改的,但是如果元组里有列表,就可以通过修改该列表间接修改该元组

4、元组的删除(删除对象)
del tup
print(tup)
---
NameError: name 'tup' is not defined
5、元组拆包
1)普通元组拆包
tup = ('python','java','C++')
a,b,c = tup
print(a,b,c)
---
python java C++

# 变量的数量必须和元组中对象的数量一样,否则出错。
a,b = tup
print(a,b)
---
ValueError: too many values to unpack (expected 2)

Notes

变量的数量必须和元组中对象的数量一样,否则出错

2)嵌套元组拆包
'''嵌套元组也可以拆包'''
tup = (1,'python',('java','C++'))
a,b,(c,d) = tup
print(a,b,d)
---
1 python C++
3)剔除不想要的变量

Notes

’_‘表示占位符,’*'表示剩余元组元素

tup = (1,'python',('java','C++'))
# 如何剔除不想要的变量?
# 使用 _ 表示不想要的元组值
_,_,(a,_) = tup
print(a)
---
java

#使用 * 表示任意多个对象值 *rest
a,*rest = tup
print(a)
print(*rest)
---
1
python ('java', 'C++')
4)交换两变量
a = 'java'
b = 2
a,b = b,a
print(a,b)
---
2 java

Notes:

  • python交换两变量,不像C,java需要中间量
  • 底层实现原理是元组的拆包!!!
6、元组的其余操作

Notes

元组内元素类型一致才能使用max函数

tup = (1,2,3,4,5,6,[1,2,3,4],'str')
tup1 = (1,2,3,4,5,6)
'''元组其余操作'''
print(max(tup))
---
TypeError: '>' not supported between instances of 'list' and 'int'
1)值出现次数
print(tup.count(2))
---
1
2)最大最小值
tup1 = (1,2,3,4,5,6)
'''元组其余操作'''
print(max(tup1))
print(min(tup1))
---
6
1
3)值的索引
tup = (1,2,[1,2,3,4],'str')
print(tup.index(3))
---
ValueError: tuple.index(x): x not in tuple

print(tup.index(2))
---
1

十四、小技巧

跳转到开头

1、for写在一行
>>> b=[j for i in a for j in i]       
>>> b      
[1, 2, 3, 4, 5, 6, 7, 8, 9]

但是要注意一点,假设用列表append值,for写在一行时,有可能会导致传入的值是一个对象,这样在列表遍历时就会报错

>>> payoff_Matrix = list()
>>> print(payoff_Matrix)
>>> print([[0, 0]] * 5)
>>> payoff_Matrix.append([[0, 0]] * 5 for i in range(0, 5))
>>> print(payoff_Matrix)
[]
[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[<generator object <genexpr> at 0x000001D00AF0B5C8>]

所以这时候尽量不要写在同一行

>> payoff_Matrix = list()
>> print(payoff_Matrix)
>> print([[0, 0]] * 5)
>> for i in range(0, 2):
>>    payoff_Matrix.append([[0, 0]] *2)
>>    print(payoff_Matrix)
[]
[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
1)for可以加if写在一行
foo = [2,18,6,9,35,44]
print([x for x in foo if x % 3 == 0])
---
[18, 6, 9]
2)list转dict - list元素作为key,索引作为值

参考https://blog.csdn.net/weixin_43665662/article/details/113437279

t_list = ["apple","banana","key"]
t_dict = {el : index for index, el in enumerate(t_list)}  #注意是{},不是[]
print(t_dict)
---
{'apple': 0, 'banana': 1, 'key': 2}
2、循环打印列表
>>> print([0] * 10)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
3、获取字典中最大值及对应的键

参考Python 获取字典中最大值及对应的键

prices = {
    'A':123,
    'B':450.1,
    'C':12,
    'E':444,
}

max_prices = max(zip(prices.values(), prices.keys()))
print(max_prices) # (450.1, 'B')
4、判断dict当中key是否存在(两种方法)

如果key=0不存在,mydict['0']会报错

d = dict()
# print(d['0']) #KeyError: '0'
print('0' in d)
print(d.get('0'))
---
False
None
5、三目运算符

python的三目运算符和c++,java的不一样

a=(x if (x>y) else y)
6、自定义异常

参考Python:用户自定义异常

java用throw,python用raise

def modelLoader(modelName):
    '''
    可选模型有:GPUHA,DHA,ARHA,RDHA,RHA3
    :return: 返回模型
    '''

    if (modelName == 'GPUHA'): return GPUHA.GPUHA()
    elif(modelName == 'DHA'): return DHA.DHA([None], [None])
    elif(modelName == 'ARHA'): return ARHA.ARHA()
    elif(modelName == 'RDHA'): return RDHA.RDHA([None], [None])
    elif(modelName == 'RHA3'): return RHA3.rHA()
    else:
        raise(modelException(modelName))

model = modelLoader("RHA34")
---
Traceback (most recent call last):
  File "E:/python/EGG/main.py", line 63, in <module>
    model = modelLoader("RHA34")
  File "E:/python/EGG/main.py", line 26, in modelLoader
    raise(modelException(modelName))
__main__.modelException: <exception str() failed>
RHA34  does not exist, please correct your modelName
7、线程运行 & 线程同步
1)线程运行

Python多线程之threading.Thread实现

from threading import Thread

class MyThread(Thread):
    def run(self):
        function_name(self.parameter1)

    def __init__(self, parameter1):
        Thread.__init__(self)
        self.parameter1 = parameter1

t = MyThread(parameter1)
t.start()
# 只需要增加一句代码:子线程执行完毕以后,主线程再关闭
t.join()
2)线程同步(★★★★★)

同步锁:RLock

threading.RLock()
lock.acquire() # 加锁
lock.release() # 释放锁
---
Note:RLock和Condition用途不同

条件变量:cond

Python 线程条件(Condition),(线程等待另一个线程的执行)

import threading
import time

def go1():
    with cond:  #使用条件变量(资源 Lock)
        for  i  in range(8):
            time.sleep(1)
            print(threading.current_thread().name,i,"go11")
            if i==5:   
                cond.wait() #等待cond.notify(),再继续执行。(释放条件变量(资源 Lock))

def go2():
    with  cond:  #使用条件变量(资源 Lock)
        for i in range(7):
            time.sleep(1)
            print(threading.current_thread().name, i)
        cond.notify()  #通知,触发 cond.wait()。(释放条件变量(资源 Lock))

cond=threading.Condition()  #线程条件变量
threading.Thread(target=go1).start()  #和下面的线程的次序不能调。这个线程先拿到cond条件变量(资源 Lock)
threading.Thread(target=go2).start()  #这个线程不会先拿到cond条件变量(资源 Lock)

事件管理标志:event

参考Python threading中event的使用

通过threading.Event()可以创建一个事件管理标志,该标志(event)默认为False,event对象主要有四种方法可以调用:

  • event.wait(timeout=None):调用该方法的线程会被阻塞,如果设置了timeout参数,超时后,线程会停止阻塞继续执行;
  • event.set()将event的标志设置为True,调用wait方法的所有线程将被唤醒;
  • event.clear():将event的标志设置为False,调用wait方法的所有线程将被阻塞;
  • event.isSet()判断event的标志是否为True

可以开启一个线程去修改这个标志位(不用加锁),进而去关闭所有正在运行的线程(对于while(True)线程的关闭很管用)。

def setCond(lock,event):
    for i in range(0,10):
        time.sleep(5)

    #lock.acquire()
    event.set()   #将event的标志设置为True
    #lock.release()
while(True):
     flag = event.isSet()
        if(flag):
            break
3)线程池

参考

  • python 线程安全的数据类型_详解python多线程、锁、event事件机制的简单使用

  • python线程池的使用 必看,很详细!!!!!

  • Python多线程-Event(事件对象)

a、创建线程池ThreadPoolExecutor,提交任务submit(),查询状态done(),获取结果result()

from concurrent.futures import ThreadPoolExecutor,as_completed

b、取消任务cancel()

使用cancel()方法可以取消提交的任务,如果任务已经在线程池中运行了,就取消不了。上面的例子,线程池大小为1,添加两个线程后,第一个线程已经在线程池运行,取消不了,第二个线程还在等待,所以可以取消

c、as_completed() as_completed()方法是一个生成器,在没有任务完成的时候,会阻塞

d、使用map方法,无需提前使用submit方法,map方法与python标准库中的map含义相同,都是将序列中的每个元素都执行同一个函数

for data in executor.map(get_html, time_list):
    print(data)

e、shutdown():关闭executor

在用完一个线程池后,应该调用线程池的shutdown()方法,该方法将启动线程池的关闭序列。调用shutdown()方法后的线程池不再接受新任务,但会将以前所有已提交的任务执行完成。当线程池中的所有任务都执行完后,该线程池中的所有线程都会死亡。

8、进程运行 & 同步 & 返回值

参考

  • Python 多进程multiprocessing.Process之start()和join()
1)多进程的运行 - start
# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
import time


def run_proc(name):
    time.sleep(10)
    print('run_proc')
    print('Run child process %s (%s)...' % (name, os.getpid()))


def hello_world():
    # time.sleep(5)
    time.sleep(20)
    print('hello world!')
    print('Run child process (%s)...' % (os.getpid()))


if __name__ == '__main__':
    print('Parent process %s.' % os.getpid())
    p1 = Process(target=run_proc, args=('test',))
    p2 = Process(target=hello_world)
    print('Process will start.')
    p1.start()
    p2.start()
    print('Process end.')
---
Parent process 14368.
Process will start.
Process end.
run_proc
Run child process test (11368)...
hello world!
Run child process (16428)...
2)多进程的同步 - join

为了同步主进程和子进程,这时就需要用到p1.join(),目的是让主进程阻塞,等待子进程结束后再继续往下运行

# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
import time


def run_proc(name):
    time.sleep(10)
    print('run_proc')
    print('Run child process %s (%s)...' % (name, os.getpid()))


def hello_world():
    # time.sleep(5)
    time.sleep(20)
    print('hello world!')
    print('Run child process (%s)...' % (os.getpid()))


if __name__ == '__main__':
    print('Parent process %s.' % os.getpid())
    p1 = Process(target=run_proc, args=('test',))
    p2 = Process(target=hello_world)
    print('Process will start.')
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('Process end.')
---
Parent process 9892.
Process will start.
run_proc
Run child process test (17152)...
hello world!
Run child process (8816)...
Process end.
3)共享数据 - Queue & Manager

可以通过queue.get()来获取不同进程处理的值

参考

  • python补充之multiprocessing(二)Queue的使用
  • python快速上手之Multiprocessing多进程(让多核计算机发挥真正的作用)
  • python-opencv 多进程读取rtsp视频流,并保存到本地 - Manager
import multiprocessing as mp

def job(q,a):
    res=0
    for i in range(20):
        res+=i
    q.put(res+a)#queue  #将返回值存入queue中

if __name__=='__main__':

    q=mp.Queue()  #在进程开始前定义Queue(先进先出),可以在访问冲突时自动对Queue加锁(进程安全)
    p1=mp.Process(target=job,args=(q,1))  #注意参数是元组,即使只有一个参数,也要写成(q,)的tuple
    p2=mp.Process(target=job,args=(q,2))

    p1.start()
    p2.start()
    p1.join()
    p2.join()
    res1=q.get()
    res2=q.get()

    print(res1) #逐个取出
    print(res2)
    print(res1+res2)
---
191
192
383
4)多进程实现生产者消费者问题

参考python多进程下的生产者和消费者模型

from multiprocessing import Process, Queue
import time


# 消费者方法
def consumer(q, name):
    while True:
        res = q.get()
        # if res is None: break
        print(f"{name} 吃了 {res[0]}{res[1]}")


# 生产者方法
def producer(q, name, food):
    while(True):
        time.sleep(1)  # 模拟生产西瓜的时间延迟
        res = (1,food)
        print(f"{name} 生产了 1 个 {food}")
        # 把生产的vegetable放入到队列中
        q.put(res)


if __name__ == "__main__":
    # 创建队列
    q = Queue()
    # 创建生产者
    p1 = Process(target=producer, args=(q, "kelly", "西瓜"))
    c1 = Process(target=consumer, args=(q, "peter",))
    p1.start()
    c1.start()

    # p1.join()
    # q.put(None)
    print("主进程")
---
主进程
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
kelly 生产了 1 个 西瓜
peter 吃了 1 个 西瓜
...

如果将视频帧frame放入队列,也可以看成是生产者消费者问题

from multiprocessing import Process,Queue
import cv2
import time

cam = cv2.VideoCapture(0)
#开启子进程生成视频帧
def generate_frame(q):
    while(True):
        success, frame = cam.read()
        if(success):
            q.put(frame)

#开启子进程读取视频帧
def read_frame(q) -> None:  # 提醒返回值是一个None
    #开始逐帧读取
    while(True):
        frame = q.get()
        print(frame)
        cv2.imshow("res",frame)
        c = cv2.waitKey(5)   #设置一下延迟(即每一帧的停留时间),否则不能正常显示图片
        if(c == 27):
            break

if __name__ == '__main__':
    # queue_t = queue.Queue()
    queue_t = Queue()
    print("queue是否为空 : ",queue_t.empty())
    flag = True
    p1 = Process(target=generate_frame, args=(queue_t,))
    p2 = Process(target=read_frame, args=(queue_t,))
    p1.start()
    p2.start()
    p1.join()  #阻塞主进程
    p2.join()  #阻塞主进程
    print("End")
9、Queue实现生产者消费者问题

Python 队列(Queue)用法

python全局变量的线程安全问题

Python并行编程cookbook(含mpi4py,Lock,Celery,Queue)

from Queue import Queue,LifoQueue,PriorityQueue
#先进先出队列
q=Queue(maxsize=5)
#后进先出队列
lq=LifoQueue(maxsize=6)
#优先级队列
pq=PriorityQueue(maxsize=5)

for i in range(5):
    q.put(i)
    lq.put(i)
    pq.put(i)

print "先进先出队列:%s;是否为空:%s;多大,%s;是否满,%s" %(q.queue,q.empty(),q.qsize(),q.full())
print "后进先出队列:%s;是否为空:%s;多大,%s;是否满,%s" %(lq.queue,lq.empty(),lq.qsize(),lq.full())
print "优先级队列:%s;是否为空:%s,多大,%s;是否满,%s" %(pq.queue,pq.empty(),pq.qsize(),pq.full())

print q.get(),lq.get(),pq.get()

print "先进先出队列:%s;是否为空:%s;多大,%s;是否满,%s" %(q.queue,q.empty(),q.qsize(),q.full())
print "后进先出队列:%s;是否为空:%s;多大,%s;是否满,%s" %(lq.queue,lq.empty(),lq.qsize(),lq.full())
print "优先级队列:%s;是否为空:%s,多大,%s;是否满,%s" %(pq.queue,pq.empty(),pq.qsize(),pq.full())

在Python中,队列是最常用的线程间的通信方法,因为它是线程安全的,自带锁。而Condition等需要额外加锁的代码操作,在编程对死锁现象要很小心,Queue就不用担心这个问题。

#from Queue import Queue
from queue import Queue
import time,threading
q=Queue(maxsize=0)

def product(name):
    count=1
    while True:
        q.put('气球兵{}'.format(count))
        print ('{}训练气球兵{}只'.format(name,count))
        count+=1
        time.sleep(5)
def consume(name):
    while True:
        print ('{}使用了{}'.format(name,q.get()))
        time.sleep(1)
        q.task_done()
t1=threading.Thread(target=product,args=('wpp',))
t2=threading.Thread(target=consume,args=('ypp',))
t3=threading.Thread(target=consume,args=('others',))

t1.start()
t2.start()
t3.start()

Note:

  • python3.6找不到Queue模块,直接import queue即可
10、进程池
1)进程池常见命令

python中multiprocessing.pool的使用参考

  • Python程序中的进程操作-进程池
  • python进程池:multiprocessing.pool
  • torch.multiprocessing.Pool的使用

Note

  • 常见命令有:applyapply_asyncjoincloseterminate

  • torch.multiprocessing.Pool的使用基本一致

#coding: utf-8
from torch.multiprocessing import Pool
import time

def func(msg):
    while(True):
        print("msg:", msg)
        time.sleep(3)
        print("end") 

class MyProcess:

    #线程池交给类方法去处理
    def test(self):
        pool = Pool(processes=3)
        for i in range(4):
            msg = "hello %d" % (i)
            pool.apply_async(func, (msg,))  # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

        time.sleep(5)

        print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
        pool.close()
        pool.terminate()  # 中断所有工作进程
        pool.join()  # 调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
        print("Sub-process(es) done.")


if __name__ == "__main__":
    process = MyProcess()
    process.test()
---
msg: hello 0
msg: hello 1
msg: hello 2
end
msg: hello 0
end
msg: hello 1
end
msg: hello 2
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
Sub-process(es) done.

如果修改成这样:

...
def test(self):
    pool = Pool(processes=3)
    for i in range(4):
        msg = "hello %d" % (i)
        t = pool.apply_async(func, (msg,))  # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        t.get()  #会反复调用进程1,等待其结果的返回,直到有结果返回
        ...
---
msg: hello 0
end
msg: hello 0
end
msg: hello 0
end
msg: hello 0
end
msg: hello 0
end
msg: hello 0

如果有返回值,则效果如下:

def func(msg):
    while(True):
        print("msg:", msg)
        time.sleep(3)
        print("end")
        return "ttttt" + str(msg)
    ...
def test(self):
    pool = Pool(processes=3)
    for i in range(4):
        msg = "hello %d" % (i)
        t = pool.apply_async(func, (msg,))  # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        res = t.get()  #会反复调用进程1,等待其结果的返回
        print(res)   #一旦取到结果,则不再反复调用
        ...
---
msg: hello 0
end
ttttthello 0
msg: hello 1
end
ttttthello 1
msg: hello 2
end
ttttthello 2
msg: hello 3
end
ttttthello 3
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
Sub-process(es) done.
2)Flask中使用进程池

​ Flask中使用进程池参考 在Flask服务中使用进程池加速

import flask
import math
import json
from concurrent.futures import ProcessPoolExecutor

process_pool = ProcessPoolExecutor()
app = flask.Flask(__name__)

# 判断是否是素数
def is_prime(n):
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    sqrt_n = int(math.floor(math.sqrt(n)))

    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True


@app.route("/is_prime/")
def api_is_prime(numbers):
    number_list = [int(x) for x in numbers.split(",")]

    rsts = process_pool.map(is_prime, number_list)

    rst = json.dumps(
        dict(zip(number_list, rsts))
    )

    return rst


if __name__ == '__main__':
    app.run()
3)apply_async中的函数未执行
  • multiprocess模块使用进程池调用apply_async()提交的函数及回调函数不执行问题

主要原因是该进程往同步队列里丢数据,导致进程池无法被执行,去掉同步队列的代码即可。

解决方案:

将原来创建Queue的方式,由multiprocessing.Queue()改成multiprocessing.Manager.Queue()即可,参考Python3之无法在进程池中使用队列Queue的解决方案

import multiprocessing
import time

class Test:
    def __init__(self):
        self.pool = multiprocessing.Pool()
        # self.queue = multiprocessing.Queue()
        m = multiprocessing.Manager()
        self.queue = m.Queue()
        ...

还需要将原来用mp创建的共享数据,转换成用mp.Manager()来创建(比如Event,Value,Queue)

import multiprocessing as mp
...
manager = mp.Manager()
e = manager.Event()
queues = [manager.Queue() for _ in range(self.args.num_cams)]
queues_res = [manager.Queue() for _ in range(self.args.num_cams)]   #存储最终结果的队列

counter1 = manager.Value('i', 0)
counter2 = manager.Value('i', 0)
argss = [copy.deepcopy(self.args) for _ in range(self.args.num_cams)]
# if self.args.num_cams == 1:
if self.args.video is None:
    argss[0].video = video

    pool.apply_async(extract_keypoints_parallel, (queues[0], argss[0], counter1, counter2, self.consecutive_frames, e))
    pool.apply_async(alg2_sequential, (queues, argss, self.consecutive_frames, e, queues_res))
    ...
4)managers.RemoteError报错解决

在帮师姐将fall_detection代码嵌入到检测平台中发现,fall_detection是多进程的,而且其中一个进程负责输出fall的检测帧结果。要想让主进程从子进程中取出frame,只能通过两个子进程的queues来获取,这里为了图方便,直接塞进一个新的queues_res来装入frame,交给主进程去读取。但是为了避免两个子进程不被服务器管理,这里引入了进程池,但是在使用时遇到了一些问题。

原始代码如下所示,运行会报如下错误raise convert_to_error(kind, result) multiprocessing.managers.RemoteError: ,主要原因是主进程使用了子进程的共享队列queues_res,虽然manager.Queue()是进程安全的,但是这进程安全是针对进程池提供的进程而言的,对于主进程,它不被Pool管理,故使用queues_res时,没有加锁,会报错

class A(object):    
    ...
    def begin(self,pool,video):
        print('Starting...')

        manager = mp.Manager()
        e = manager.Event()
        queues = [manager.Queue() for _ in range(self.args.num_cams)]
        queues_res = [manager.Queue() for _ in range(self.args.num_cams)]   #存储最终结果的队列
        counter1 = manager.Value('i', 0)
        counter2 = manager.Value('i', 0)
        argss = [copy.deepcopy(self.args) for _ in range(self.args.num_cams)]
        # if self.args.num_cams == 1:
        if self.args.video is None:
            argss[0].video = video

        p1 = pool.apply_async(extract_keypoints_parallel,
                              (queues[0], argss[0], counter1, counter2, self.consecutive_frames, e))
        # pool.apply_async(alg2_sequential, (queues, argss, self.consecutive_frames, e, queues_res))
        p2 = pool.apply_async(alg2_sequential, (queues, argss, self.consecutive_frames, e, queues_res))

        return queues_res

if __name__ == "__main__":
    #输入接口
    f = FallDetector()

    pool = Pool(processes=3)
    # pool = Pool(processes=3)  # 用进程池管理多个进程
    # queues_res = f.begin(video="rtsp://admin:[email protected]:554/h264/ch1/main/av_stream")
    # queues_res = f.begin(pool=pool,video=0)
    queues_res = f.begin(pool,video=0)
    f.begin(pool, video=0)

    print("输出结果")
    for frame in detect(queues_res):
        print(frame)
        # print(frame['img'])
        # cv2.imshow("res",frame['img'])
        cv2.imshow("res", frame)
        c = cv2.waitKey(5)
        if (c == 27):
            break

    pool.terminate()  #终止进程池所有工作进程
---
Process SpawnPoolWorker-2:
Process SpawnPoolWorker-1:
Traceback (most recent call last):
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\process.py", line 258, in _bootstrap
    self.run()
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\pool.py", line 108, in worker
    task = get()
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\queues.py", line 337, in get
    return _ForkingPickler.loads(res)
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\managers.py", line 881, in RebuildProxy
    return func(token, serializer, incref=incref, **kwds)
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\managers.py", line 930, in AutoProxy
    incref=incref)
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\managers.py", line 731, in __init__
    self._incref()
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\managers.py", line 786, in _incref
    dispatch(conn, None, 'incref', (self._id,))
  File "D:\programmingSoftware\Anaconda\Anaconda\lib\multiprocessing\managers.py", line 82, in dispatch
    raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError: 

解决思路:主进程在使用queue_res时,加锁即可。参考Why python throws “multiprocessing.managers.RemoteError” for shared lock?

5)ProcessPoolExecutor
  • ProcessPoolExecutor进程池
11、类加载,类对象调用类内方法时间开销问题

对同一个操作效果,比较时间开销

1)实验1

发现类对象调用类内所有方法,和类对象调用类内汇总方法,时间开销差不多

import time
class A:
    def __init__(self):
        print("init")

    def func1(self):
        for i in range(0,10000):
            # print("func1")
            continue
        pass

    def func2(self):
        for i in range(0, 10000):
            # print("func2")
            continue
        pass

    def func3(self):
        for i in range(0, 10000):
            # print("func3")
            continue
        pass

    def all(self):
        self.func1()
        self.func2()
        self.func3()

'''类外对象调用类内func1,func2,func3方法'''
a = A()
startT = time.time()
a.func1()
a.func2()
a.func3()
endT = time.time()
print("interval(func1,func2,func3) = ",endT - startT)

'''类外对象调用类内all方法,实现对所有方法func1,func2,func3的调用'''
b = A()
startT = time.time()
b.all()
endT = time.time()
print("interval(all) = ",endT - startT)
---
init
interval(func1,func2,func3) =  0.0009984970092773438
init
interval(all) =  0.0009984970092773438
2)实验2

比较类对象在管理数据流和不管理数据流情况下的时间开销,发现类对象管理数据流比不管理数据流慢,小则几毫秒,大则2秒,但是感觉还是有点问题,要不试试分别运行这两段代码??接着就有了实验3

import time
import cv2

class A:
    def __init__(self,videoPath=None):
        print("init")
        self.videoPath = videoPath

    def func1(self):
        k = 0
        video = cv2.VideoCapture(self.videoPath)
        totalFrame = video.get(cv2.CAP_PROP_FRAME_COUNT)
        print("totalFrame = ", totalFrame)
        while(k < totalFrame):
            ret,img = video.read()
            self.func4()
            self.func5()
            cv2.imshow("img",img)
            cv2.waitKey(1)
            k += 1
            continue
        video.release()
        print()

    def func2(self):
        for i in range(1000):
            continue
        pass

    def func3(self):
        for i in range(1000):
            continue
        pass

    def func4(self):
        for i in range(1000):
            continue
        pass

    def func5(self):
        for i in range(1000):
            continue
        pass

    def all(self):
        self.func1()
        self.func2()
        self.func3()


videoPath = "E:/python/数据集/YawDD数据集/YawDD/YawDD dataset/Dash/Female/9-FemaleNoGlasses.avi"

'''类对象不管理数据流'''
a = A()
startT = time.time()
video = cv2.VideoCapture(videoPath)
k = 0
totalFrame = video.get(cv2.CAP_PROP_FRAME_COUNT)
print("totalFrame = ",totalFrame)
while(k < totalFrame):  #video.isOpened()检查初始化是否成功
    ret,img = video.read()
    # a.func1()
    a.func2()
    a.func3()
    k += 1
    cv2.imshow("img", img)
    cv2.waitKey(1)
    continue
video.release()
endT = time.time()
video.release()
print()
print("interval(without source governing) = ",endT - startT)

'''类对象本身管理数据流'''
b = A(videoPath=videoPath)
startT = time.time()
b.all()
endT = time.time()
print("interval(with source governing) = ",endT - startT)

---
init
totalFrame =  1532.0

interval(without source governing) =  18.046899795532227
init
totalFrame =  1532.0

interval(with source governing) =  20.535900115966797

---
init
totalFrame =  1532.0

interval(without source governing) =  16.4721097946167
init
totalFrame =  1532.0

interval(with source governing) =  18.723338842391968
3)实验3

怀疑时间差产生的原因是前面一段代码运行时python没有及时释放内存,或者没有释放某些子程序,导致CPU负载大。因此分别运行实验2这两段代码,得出的结论是类对象管理数据流和不管理数据流,两者运行时间开销差不多

init
totalFrame =  1532.0

interval(without source governing) =  13.777432680130005

init
totalFrame =  1532.0

interval(with source governing) =  12.208465337753296
4)小总结

参考面向对象与面向过程的优缺点

1、面向对象相对面向过程的优点

1)结构清晰。使人们的编程与实际的世界更加接近,所有的对象被赋予属性和方法,结果编程就更加富有人性化。

2)封装性。减小外部对内部的影响。封装将对象有关的数据和行为封装成整体来处理,使得对象以外的部分不能随意存取对象的内部属性,从而有效地避免了外部错误对它的影响,大大减小了查错和排错的难度。

3)容易扩展,代码重用率高。容易扩展,在大框架不变的情况下很容易就开发出适合自己的功能,实现简单,可有效地减少程序的维护工作量,软件开发效率高。

2、面向对象相对面向过程的缺点

1)增加工作量。如果一味地强调封装,当进行修改对象内部时,对象的任何属性都不允许外部直接存取,则要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担,增加运行开销,并且使程序显得臃肿。

2)性能低。由于面向更高的逻辑抽象层,使得面向对象在实现的时候,不得不做出性能上面的牺牲计算时间和空间存储大小的都开销很大

12、假言assert & self.assertTrue

参考用好Assert断言,让Bug无藏身之地~

self.assertTrue()unittest自带的断言方法

import unittest

class MyClass(unittest.TestCase):

    def test_vertification(self):
        self.assertTrue(1 == 2)   #unittest自带的断言方法,会报AssertionError: False is not true

    def test_vertification1(self):
        # assert(1 == 2)  #会报    assert(1 == 2) AssertionError
        assert(1 == 1)
13、python中import和__import__的区别(python反射)

参考

  • [Python系列]Python的import的路径机制及引用报错详解
  • python中__import__与import的区别

__import__与import的对比

import __import__
倾向 固定式声明 动态加载
适用场景 已经明确知道项目中哪些模块 模块可以动态插拔、动态引入运行
举例 import os #导入固有的os模块 __import__(‘employee’).find(name=‘李林’) #动态加载刚安装的员工管理模块,并且查找一个叫“李林”的人

语句 from spam.ham import eggs, sausage as saus 的结果将为

_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage

Python基础语法2与常见问题_第1张图片

14、python反射机制 (__import____getattr__

参考

  • Python 反射机制解析
  • PYTHON里的反射(自学习)
#dynamic.py
imp = input("请输入模块:")
dd = __import__(imp) # 等价于import imp
inp_func = input("请输入要执行的函数名称:")

f = getattr(dd,inp_func,None)#作用:从导入模块中找到需要调用的函数inp_func,然后返回一个该函数的引用.没有找到就返回None

f() # 执行该函数

Note

  • 这里输入的字符串(即"A.B")要在Source Root路径下,否则会报module为None的错误。
>>testInflection.model.A
def helloTest():
    print("test")
    pass

>>testInflection.model.B
def helloTest():
    print("test")
    pass

>>test.py
_temp = __import__("testInflection.model",globals(),locals(),["A","B"])

A = _temp.A
A1 = getattr(_temp,"A")  #获取模块的python文件
func = getattr(A,"helloTest")  #获取python文件中的"helloTest"方法
print("A = {}, A1 = {}, A.func = {}".format(A,A1,func)) #A和A1等价
---
A = <module 'testInflection.model.A' from 'E:\\研究生任务\\python算子实现\\wangxiaoxi1\\src\\testInflection\\model\\A.py'>, A1 = <module 'testInflection.model.A' from 'E:\\研究生任务\\python算子实现\\wangxiaoxi1\\src\\testInflection\\model\\A.py'>, A.func = <function helloTest at 0x0000029B2E2C6598>

Note:

  • __import__导入的是模块名(module)(注意路径要写对,格式为A.B,引入的是模块名,不是模块中的python文件,python文件要在__import__中额外指定fromlist=()。)
  • getattr()获取模块中的python文件,以及python文件里的类或方法
  • __import__作用和importlib.import_module()类似。

例如

 module = __import__("model",globals(),locals(),[modelName])  #获取模块
 pyModel = getattr(module,modelName)  #获取模块中python文件
 modelCls = getattr(pyModel,modelName)()  #获取类模型,并实例化
 model = modelCls()
15、时间,日期,时间戳的转换

python中时间、日期、时间戳的转换

# 引入模块
import time, datetime

# [python中时间、日期、时间戳的转换](https://www.cnblogs.com/jfl-xx/p/8024596.html)
# 字符类型的时间
tss1 = '2013-10-10 23:40:00'
# 转为时间数组
timeArray = time.strptime(tss1, "%Y-%m-%d %H:%M:%S")
print(timeArray)
# timeArray可以调用tm_year等
print(timeArray.tm_year)   # 2013
# 转为时间戳
timeStamp = int(time.mktime(timeArray))
print(timeStamp)  # 1381419600

tss2 = "2013-10-10 23:40:00"
# 转为数组
timeArray = time.strptime(tss2, "%Y-%m-%d %H:%M:%S")
# 转为其它显示格式
otherStyleTime = time.strftime("%Y/%m/%d %H:%M:%S", timeArray)
print(otherStyleTime)  # 2013/10/10 23:40:00

tss3 = "2013/10/10 23:40:00"
timeArray = time.strptime(tss3, "%Y/%m/%d %H:%M:%S")
otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
print(otherStyleTime)  # 2013-10-10 23:40:00

Note:

  • 解决OverflowError: mktime argument out of range

    原因:时间戳是从1970年1月1日早上8点开始的,于是将开始时的代码修改如下即可正常运行

    begin = time.mktime(time.strptime('1970-01-01 08:00:00', '%Y-%m-%d %H:%M:%S'))
    

    windows下才会出现这个问题。

16、实例对象如果初始化返回True
def func():
    pass

class A():
    pass

func_a = func()
class_A = A()
if func_a:
    print(f"func_a {True}")
else:
    print(f"func_a {False}")

if class_A :
    print(f"class_A  {True}")
else:
    print(f"class_A  {True}")
---
func_a False
class_A  True
17、lambda表达式
1)lambda是一个匿名函数
g = lambda x : x * 2   #参数 : 返回值
res1 = g(2)
print(res1)  #4

res2 = g(3)
print(res2)  #6

lambda作为一个表达式,定义了一个匿名函数,上例的代码x为入口参数,x*2为函数体,用函数来表示为:

1 def g(x):
2     return  x * 2

相较于函数的形式,lambda表达式更为简洁

lambda表达式接受列表[]类型的输入

import numpy as np

f = lambda x : x * 2

state = np.random.RandomState(2)
x_all = state.permutation(5)
y_all = f(x_all)
print(f"x_all = {x_all}")
print(f"y_all = {y_all}")
---
x_all = [2 4 1 3 0]
y_all = [4 8 2 6 0]

这里使用torch进行one-hot编码

import torch

f = lambda y: torch.zeros(10,dtype=torch.float).scatter_(dim=0,index=torch.tensor(y),value=1)
res = f(1)
print(res)
---
tensor([0., 1., 0., 0., 0., 0., 0., 0., 0., 0.])
2)lambda使用说明

参考Python lambda介绍

Python中,也有几个定义好的全局函数方便使用的,filter, map, reduce

foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]

print filter(lambda x: x % 3 == 0, foo)  #等价于:if(x % 3 == 0) return x
#[18, 9, 24, 12, 27]

print map(lambda x: x * 2 + 10, foo)  #等价于: for + return x * 2 + 10
#[14, 46, 28, 54, 44, 58, 26, 34, 64]

print reduce(lambda x, y: x + y, foo)  #等价于:两两相加,做规约
#139

上面例子中的map的作用,非常简单清晰。但是,Python是否非要使用lambda才能做到这样的简洁程度呢?在对象遍历处理方面,其实Python的for…in…if语法已经很强大,并且在易读上胜过了lambda。

比如上面map的例子,可以写成:

print [x * 2 + 10 for x in foo]

非常的简洁,易懂。

filter的例子可以写成:

print [x for x in foo if x %3 ==0]

同样也是比lambda的方式更容易理解

lambda使用说明:

lambda 定义了一个匿名函数

lambda 并不会带来程序运行效率的提高,只会使代码更简洁。

如果可以使用for…in…if来完成的,坚决不用lambda

如果使用lambda,lambda内不要包含循环,如果有,我宁愿定义函数来完成,使代码获得可重用性和更好的可读性

3)使用lambda表达式对list按属性值排序

参考https://blog.csdn.net/qq_38890412/article/details/101721571

class Student:

    def __init__(self, name, subject, mark):
        self.name = name
        self.subject = subject
        self.mark = mark


s1 = Student("Jack", "os", 60)
s2 = Student("Jim", "cn", 61)
s3 = Student("Pony", "se", 65)


#方法1:lambda表达式
print(f"lambda expression")
L = [s1, s2, s3]
L.sort(key=lambda t: t.mark)  #传入一个函数,按分数从小到大排序学生
for i in range(0, len(L)):
    print(L[i].name + "," + L[i].subject + "," + str(L[i].mark))
---
lambda expression
Jack,os,60
Jim,cn,61
Pony,se,65

等价于:

L = [s1, s2, s3]
def key(t):
    return t.mark
L.sort(key=key)  #传入一个函数,按分数从小到大排序学生
for i in range(0, len(L)):
    print(L[i].name + "," + L[i].subject + "," + str(L[i].mark))
18、APScheduler实现定时任务

参考

  • Python 定时任务的实现方式
  • BlockingScheduler与BackgroundScheduler区别
  • APScheduler 定时任务详解
1)循环 sleep(阻塞)

这种方式最简单,在循环里面放入要执行的任务,然后 sleep 一段时间再执行

from datetime import datetime
import time
# 每n秒执行一次
def timer(n):
    while True:
        print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        time.sleep(n)
# 5s
timer(5)

这个方法的缺点是,只能执行固定间隔时间的任务,如果有定时任务就无法完成,比如早上六点半喊我起床。并且 sleep 是一个阻塞函数,也就是说 sleep 这一段时间,啥都不能做。

2)Timer

threading 模块中的 Timer 是一个非阻塞函数,比 sleep 稍好一点。

from datetime import datetime
from threading import Timer
# 打印时间函数
def printTime(inc):
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    t = Timer(inc, printTime, (inc,))
    t.start()
# 5s
printTime(5)

Timer 函数第一个参数是时间间隔(单位是秒),第二个参数是要调用的函数名,第三个参数是调用函数的参数(tuple)

3)APScheduler定时框架

终于找到了可以每天定时喊我起床的方式了

APScheduler是一个 Python 定时任务框架,使用起来十分方便。提供了基于日期、固定时间间隔以及 crontab 类型的任务,并且可以持久化任务、并以 daemon 方式运行应用

使用 APScheduler 需要安装

pip install apscheduler

首先来看一个周一到周五每天早上6点半喊我起床的例子

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
# 输出时间
def job():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
# BlockingScheduler
scheduler = BlockingScheduler()
scheduler.add_job(job, 'cron', day_of_week='1-5', hour=6, minute=30)
scheduler.start()

APScheduler四个组件

APScheduler 四个组件分别为:触发器(trigger),作业存储(job store),执行器(executor),调度器(scheduler)

a、触发器(trigger)

包含调度逻辑,每一个作业有它自己的触发器,用于决定接下来哪一个作业会运行。除了他们自己初始配置意外,触发器完全是无状态的
APScheduler 有三种内建的 trigger:

date: 特定的时间点触发
interval: 固定时间间隔触发
cron: 在特定时间周期性地触发

b、作业存储(job store)

存储被调度的作业,默认的作业存储是简单地把作业保存在内存中,其他的作业存储是将作业保存在数据库中。一个作业的数据讲在保存在持久化作业存储时被序列化,并在加载时被反序列化。调度器不能分享同一个作业存储。
APScheduler 默认使用 MemoryJobStore,可以修改使用 DB 存储方案

c、执行器(executor)

处理作业的运行,他们通常通过在作业中提交制定的可调用对象到一个线程或者进城池来进行。当作业完成时,执行器将会通知调度器。
最常用的 executor 有两种:

ProcessPoolExecutor
ThreadPoolExecutor

d、调度器(scheduler)

通常在应用中只有一个调度器,应用的开发者通常不会直接处理作业存储、调度器和触发器,相反,调度器提供了处理这些的合适的接口。配置作业存储和执行器可以在调度器中完成,例如添加、修改和移除作业。

配置调度器(★★★★★)

APScheduler提供了许多不同的方式来配置调度器,你可以使用一个配置字典或者作为参数关键字的方式传入。你也可以先创建调度器,再配置和添加作业,这样你可以在不同的环境中得到更大的灵活性。

下面来看一个简单的 BlockingScheduler 例子

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

def job():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
# 定义BlockingScheduler
sched = BlockingScheduler()
sched.add_job(job, 'interval', seconds=5)
sched.start()

上述代码创建了一个 BlockingScheduler,并使用默认内存存储和默认执行器。(默认选项分别是 MemoryJobStoreThreadPoolExecutor,其中线程池的最大线程数为10)。配置完成后使用 start() 方法来启动。

如果要给job传参,可以在add_job中使用args参数,如果要给job设置指定id,可以使用id参数

rom datetime import datetime

from apscheduler.schedulers.blocking import BlockingScheduler


def func(name):
    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    print(now + f" Hello world, {name}")


scheduler = BlockingScheduler()
scheduler.add_job(func, 'interval', seconds=3, args=["desire"], id="func")
scheduler.start()

移除job

  • 1)通过job的ID来调用remove_job方法
  • 2)通过在add_job()中得到的job实例调用remove()方法
  • 如果一个job完成了调度(例如他的触发器不会再被触发), 它会自动被移除

如果job_id不存在,remove_job会报错,可以用try - except来处理

# remove
job = scheduler.add_job(func, 'interval', seconds=3, args=["desire"], id="job_remove")
job.remove()

# remove_job
scheduler.add_job(func, 'interval', seconds=3, args=["desire"], id="job_remove")
scheduler.remove_job(job_id="job_remove")

终止调度器中的执行器

scheduler.shutdown()  #终止调度器中的任务存储器以及执行器
scheduler.shutdown(wait=False)

默认情况,会终止任务存储器以及执行器,然后等待所有目前执行的job完成后(自动终止),wait=False 此参数不会等待任何运行中的任务完成,直接终止。但是如果scheduler没有执行,shutdown()会报错:

19、With用法

参考

  • python的with关键字
  • Python 中 with用法及原理

with表达式其实是try-finally的简写形式。但是又不是全相同。

实际上,在with后面的代码块抛出异常时,exit()方法被执行。开发库时,清理资源,关闭文件等操作,都可以放在exit()方法中。

with open('1.txt') as f:
    print(f.read())

print(f.closed)

with 语句实质是上下文管理:

  • 1、上下文管理协议。包含方法__enter__() 和 exit(),支持该协议对象要实现这两个方法。
  • 2、上下文管理器,定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
  • 3、进入上下文的时候执行__enter__方法,如果设置as var语句,var变量接受__enter__()方法返回值。
  • 4、如果运行时发生了异常,就退出上下文管理器。调用管理器__exit__方法。
class Mycontex(object):
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        print("进入enter")
        return self
    def do_self(self):
        print(self.name)
    def __exit__(self,exc_type,exc_value,traceback):
        print("退出exit")
        print(exc_type,exc_value)
if __name__ == '__main__':
    with Mycontex('test') as mc:
        mc.do_self()
---
进入enter
test
退出exit
None,None
20、格式化输出
if batch % 100 == 0:  
    loss,current = loss.item(),batch * len(X)  
    print(f"loss = {loss:>7f}, [{current:>5d}/{size:>5d}]")  

'>‘右对齐,’<‘左对齐,’^'居中对齐

print('{:10s} and {:>10s}'.format('hello','world'))  # 取10位左对齐,取10位右对齐
hello      and      world

更多格式化输出详见python基础_格式化输出(%用法和format用法)

21、python模块的导入 & __all__

__all__表示对于该包中,有哪些子包或模块可以对外可见。

包的目录结构如下:

.
└── mypackage
    ├── __init__.py
    ├── subpackage_1
        ├── __init__.py
        ├── module_a.py
        └── module_c.py
    ├── subpackage_2
        ├── __init__.py
        ├── module_b.py
1)实验1
# module_b.py
from package import *

print(dir())
sub_package1.module_a.aa()
---
NameError: name 'sub_package1' is not defined
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
2)实验2
# package.__init__.py
__all__ = ['sub_package1', 'sub_package2']

执行module_b.py

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sub_package1', 'sub_package2']
Traceback (most recent call last):
    sub_package1.module_a.aa()
AttributeError: module 'package.sub_package1' has no attribute 'module_a'
3)实验3
#package.subpackage1.__init__.py
__all__ = ['module_a']

import package.sub_package1.module_a

执行module_b.py

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sub_package1', 'sub_package2']
aa
22、python注解
1)python注解(实验1)

参考Python中的注解“@”

'''
@link: 
@introduction: Python3.0之后加入新特性Decorators,以@为标记修饰function和class
'''
def spamrun(fn):
    def sayspam(*args):
        print("spam,spam,spam")
        fn(*args)

    def sayspam1(*args):
        print("spam1,spam1,spam1")
        fn(*args)

    return sayspam1   #返回值为函数

def spamrun1(fn):
    def sayspam2(*args):
        print("spam2,spam2,spam2")
        fn(*args)

    return sayspam2 #返回值为函数

@spamrun1
@spamrun
def useful(a, b):
    print(a * b)


if __name__ == "__main__":
    useful(2, 5)
---
spam2,spam2,spam2
spam1,spam1,spam1
10

上面的代码等价于

spamrun1(spamrun(useful(2,5)))

or

if __name__ == "__main__"
  useful = spamrun1(spamrun(useful))
  useful(a,b)

这里只简单介绍了针对函数的用法,其实还可以针对class使用。

2)python注解(实验2)
def attrs(**kwds):
    def decorate(f):
        for k in kwds:
            setattr(f, k, kwds[k])
        return f

    return decorate

@attrs(versionadded="2.1",
       author="wangxiaoxi1")
def mymethod1(f):
    print(getattr(mymethod2, 'versionadded', 0))
    print(getattr(mymethod2, 'author', 0))
    print(f)

@attrs(versionadded="2.2",
       author="wangxiaoxi2")
def mymethod2(f):
    print(getattr(mymethod1, 'versionadded', 0))
    print(getattr(mymethod1, 'author', 0))
    print(f)


if __name__ == "__main__":
    mymethod1(1)
    mymethod2(2)
---
2.2
wangxiaoxi2
1
2.1
wangxiaoxi1
2

通过getattr获取类,方法中的属性值。其中注解@attrs是mymethod1,mymethod2方法中的内置方法,是其属性

3)@classmethod & @staticmethod

参考

  • Python 中的 classmethod 和 staticmethod 有什么具体用途? - 知乎用户的回答 - 知乎
  • Python 中的 classmethod 和 staticmethod 有什么具体用途? - 印第安老斑鸠的回答 - 知乎

总结如下:

  • 类和实例都可以调用static method,在实例化对象时,并不会为其实例化static method,因此参数中没有self

  • class method可以用来为一个类创建一些预处理的实例.

代码如下:

@staticmethod:

  • static method中没有self参数,也没有cls参数
class Pizza(object):
   @staticmethod
   def mix_ingredients(x, y):
       return x + y

   def cook(self):
       return self.mix_ingredients(self.cheese, self.vegetables)


pizza = Pizza()
print(pizza.mix_ingredients == Pizza.mix_ingredients)
print(pizza.cook == Pizza.cook)
---
True
False

@classmethod:

  • 返回实例化的类对象,这样就不用重载类,修改__init__方法了。可以通过@classmethod实现构造函数重载
  • 这里要注意的是,第一个参数为cls,表示这个类
class Pizza1(object):

   @classmethod
   def wash(cls, vegetable_num):
       print(f"wash {vegetable_num} vegetable")
       return cls()  #返回实例化的类对象

   def cook(self,cheese_num,vegetable_num):
       print(f"cook {cheese_num} cheese and {vegetable_num} vegetable")

pizza_ = Pizza1.wash(5)
pizza_.cook(2,4)
---
wash 5 vegetable
cook 2 cheese and 4 vegetable
class DateM(object):
   def __init__(self,year=0,month=0,day=0):
       self.day = day
       self.month = month
       self.year = year

   @classmethod
   def get_date(cls,string_date):
       year,month,day = map(int,string_date.split('-'))
       date1 = cls(year,month,day)  #使用__init__构建对象
       return date1

   def out_date(self):
       print(f"year = {self.year}")
       print(f"month = {self.month}")
       print(f"day = {self.day}")

date = DateM.get_date('2021-7-4')
date.out_date()
---
year = 2021
month = 7
day = 4
23、迭代器和生成器
1)迭代器
#!/usr/bin/python3

list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print (x, end=" ")
---
1 2 3 4

也可以使用next函数

#!/usr/bin/python3

import sys         # 引入 sys 模块

list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象

while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()
2)生成器

未使用yield的斐波那契數列实现

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        print b 
        a, b = b, a + b 
        n = n + 1
fab(5)
---
1 
1 
2 
3 
5

使用yield,将print改为yield,就在保持简洁性的同时获得了 iterable 的效果

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b, b + 1      #使用 yield可以返回多个值
        # print b 
        a, b = b, a + b 
        n = n + 1

for n in fab(5): 
    print n
---
1    2
1    2
2    3
3    4
5    6

yield和return的比较:

  • yield输出的结果可以在方法外被读取(生成当前值),yield后续的操作会继续执行,直至下一次yield

  • 如果将上面的yield改为returnreturn后面的语句就不执行了,而且无法当做迭代器使用,会报

Traceback (most recent call last):
  File "E:/研究生任务/摔倒检测工具包/test/__init__.py", line 10, in <module>
    for n in fab(5):
TypeError: 'int' object is not iterable
24、ModuleNotFoundError报错
1)从..config导入模块报ModuleNotFoundError

参考

  • python程序在命令行执行提示ModuleNotFoundError: No module named ‘XXX’ 解决方法
  • Python3导入自定义模块的3种方式

在报错模块中添加

import sys
import os
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(curPath)[0]
sys.path.append(rootPath)

如果执行文件和模块不在同一目录,这时候直接import是找不到自定义模块的。如下图:

Python基础语法2与常见问题_第2张图片

  • 执行文件main.py在main目录下
  • pwcong模块在python目录下

sys模块是python内置的,因此我们导入自定义模块的步骤如下:

  1. 先导入sys模块
  2. 然后通过sys.path.append(path) 函数来导入自定义模块所在的目录
  3. 导入自定义模块。

这时候 main.py 这样写:

# main.py
# -*- coding: utf-8 -*-

import sys
sys.path.append(r"C:\Users\Pwcong\Desktop\python")

import pwcong

pwcong.hi()

最后执行main.py文件,最终输出 hi ,第二种方式完成。

2) No module named __main__.XX

参考程序运行出现ModuleNotFoundError: No module named __main__.XX'; __main__is not a package

当导入自己写的包时,出现上述情况,一般是因为路径问题,运行主程序是在哪个路径,则包里的程序的所导入的包也应该加上此路径。

25、python导入.so文件

参考

  • python文件打包/导入 .so 文件

  • Ubuntu系统下编译.so文件报错:undefined symbol: _Py_ZeroStruct

26、不可变映射类型 - (字典)MappingProxyType

在使用Flask写服务器时,发现python的dict类型无意间变成了MappingProxyType,连dict

里面对应key的value也不再是类对象,变成了type类型

主要原因是:

Flask的app默认单线程运行,对于同个key-value对象,如果同一时刻已经有人对它进行操作,则另一个人想同时去操作它就会被禁止,key-value对象为可读状态

解决方法是:

app(threaded = True)

# 不可变映射类型,(字典)MappingProxyType
'''
   python3.3开始,types模块中引入了一个封装类名叫MappingProxyType
   如果给这个类一个映射,它会返回一个只对映射视图.
   虽然是个只读的视图,但是它是动态的,这意味着如果对原映射做出了改动,
   我们可以通过这个视图观察到,但是无法通过这个视图对原映射做出修改
'''

​```python
#示例
from types import MappingProxyType
#创建一个集合
index_a = {'a' : 'b'}
#创建index_a的映射视图
a_proxy = MappingProxyType(index_a)
print(a_proxy)
a_proxy['a']
# #不能对a_proxy视图进行修改
# a_proxy['b'] = 'bb'
#但是可以对原映射进行修改
index_a['b'] = 'bb'
print(a_proxy)
​```

{'a': 'b'}
{'a': 'b', 'b': 'bb'}

https://www.cnblogs.com/ydf0509/p/8298551.html

27、python实现消息队列

参考

  • python 消息队列、异步分布式
  • Python-RabbitMQ消息队列的发布与订阅
28、nginx部署flask应用

参考flask + nginx + uWSGI部署

29、python for中zip的使用

将tracks,p1,good封装成zip,再解封成元组

for i, (tr, (x, y), flag) in enumerate(zip(tracks, p1.reshape(-1, 2), good)):
    ...
30、python生成requirements.txt

参考python生成requirements.txt的两种方法

如安装 pipreqs 即可以导出项目依赖的包:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pipreqs

之后在进入项目目录下,执行即可

pipreqs ./ --encoding=utf8

#如果requirement.txt已存在,则
pipreqs . --encoding=utf8 --force
31、字符串前面自动补零(zfill)
#图片路径
im_dir = 'test/'

'''图片路径下的图片命名格式为000001.jpg 000002.jpg ……. 00100.jpg……'''
for i in range(1,num):
    im_name = os.path.join(im_dir, str(i).zfill(6)+'.jpg')  #Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0
    frame = cv2.imread(im_name)
    print(im_name)
---
test/000001.jpg
test/000002.jpg
test/000003.jpg
test/000004.jpg
test/000005.jpg
test/000006.jpg
test/000007.jpg
test/000008.jpg
test/000009.jpg
test/000010.jpg
test/000011.jpg
test/000012.jpg
test/000013.jpg
test/000014.jpg
test/000015.jpg
...
32、python过滤器的使用

利用函数进行迭代器元素的过滤,比单纯使用运算符更强大

'''filter(function, iterable) 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。'''
def is_odd(n):
    return n % 2 == 1

tmplist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
newlist = list(tmplist)
print(newlist)  
---
[1, 3, 5, 7, 9]
33、__call__方法的使用(“实例对象 -> 可调用对象”)

参考nn.Model中的forward()前向传播不调用

可调用对象

关于__call__方法,不得不先提一个概念,就是可调用对象(callable),我们平时自定义的函数、内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数callable。

如果在类中实现了__call__方法,那么实例对象也将成为一个可调用对象

Ex1:

'''Ex1'''
'''__call__方法的初次使用'''
class A():
    def __call__(self):
        print('i can be called like a function')

a = A()
a()
print()
---
i can be called like a function

Ex2:

通过实现__call__方法,让实例化的对象无需通过指定方法完成函数调用,直接通过B()(x)完成默认的call函数的调用

'''Ex2'''
'''通过实现__call__方法,让实例化的对象无需通过指定方法完成函数调用,直接通过B()(x)完成默认的call函数的调用'''
class B(object):
    def __call__(self, param):
        print('我在__call__中,传入参数', param)
        res = self.forward(param)
        return res

    def forward(self, x):
        print('我在forward函数中,传入参数类型是值为: ', x)
        return x
b = B()
x = b("Hello, my call")
print(x)
print()
---
我在__call__中,传入参数 Hello, my call
我在forward函数中,传入参数类型是值为:  Hello, my call
Hello, my call

这也解释了pytorch中forward的实现原理

但是在torch.nn.Module中,实际上没有使用__call__,而是使用如下方式

def _forward_unimplemented(self, *input: Any) -> None:
    raise NotImplementedError

class Module():
    forward: Callable[..., Any] = _forward_unimplemented
    ...    
    def _call_impl(self, *input, **kwargs):
       ...

    __call__ : Callable[..., Any] = _call_impl

可以发现,Module.py文件中是没有forward方法的,如果在子类Net中没有实现forward,会在_forward_unimplemented中报错raise NotImplementedError,这也说明forward方法是实现,而不是重写。 但是在模拟时报错,提示实例对象不是可调用对象

'''参考'''
'''Ex3: 模拟Module, Net'''
from typing import Callable,Any

def _forward_unimplemented(self, *input: Any):
    print("是我_forward_unimplemented绑定的forward, 我是个未实现的forward方法")

class Module():
    def __init__(self):
        print("Module参数初始化")

    forward: Callable[..., Any] = _forward_unimplemented

class Net(Module):
    def __init__(self):
        super(Net, self).__init__()
        print("Net参数初始化")

    def forward(self,x):
        print(f"Net调用forward,执行x = {x}")
        return x ** 2

net = Net()
res = net(5)  #TypeError: 'Net' object is not callable(该实例对象不是可调用对象)
print(res)
---
Module参数初始化
Net参数初始化
TypeError: 'Net' object is not callable(该实例对象不是可调用对象)

这里就不深究原因了,typing模块的Callable的使用参考

  • Python - typing 模块 —— Callable
  • Python - typing 模块 —— Any Type
34、python截屏

参考Python如何截图保存的三种方法

import time
from PIL import ImageGrab
if __name__=="__main__":  
    pic = ImageGrab.grab()
    pic.save("picture.jpg")
35、python获取文件夹下所有文件及子文件夹
1)获取文件夹下所有文件
import glob
import os

imgDir = "../dataset/fire/"
total_readPaths = glob.glob(os.path.join(imgDir, "*.*"))

for path in total_readPaths:    #遍历所有文件路径
    ...
2)获取文件夹下当前所有子文件夹

参考 https://www.cnblogs.com/bigtreei/p/9316369.html

from pathlib Path
'''
目录结构如下: 
└─fire
        │  manhole_close000.jpg
        │  manhole_close001.jpg
        │
        └─enhance
            ├─color
            │      manhole_close000.jpg
            │      manhole_close001.jpg
            │
            └─resize
                    manhole_close000.jpg
                    manhole_close001.jpg
'''
imgDir = "../dataset/fire/"
for p in Path(imgDir).iterdir():

    #遍历子文件夹下所有文件
    for s in p.rglob('*.jpg'):  
          print(s)
3)文件重命名和复制
srcFile = './actwork/linkFile/allExtLinks.txt'
dstFile = './actwork/linkFile/allExtLinks-copy.txt'
try:
    os.rename(srcFile,dstFile)
except Exception as e:
    print(e)
    print('rename file fail\r\n')
else:
    print('rename file success\r\n')
36、文件导入拼接项目根路径

存在的问题:由于python文件放置的位置不同(一个在主文件夹,一个在子文件夹),在调用模型时如果通过相对路径去访问权重文件,这就会导致不同python文件在访问这个权重文件时需要传入该权重文件相对于该python文件的路径,这就有点麻烦。如果想让模型对外透明,不需要用户输入权重文件的位置,想把权重文件路径写死,这里可以考虑将项目文件的根路径和权重文件实际在项目中的位置进行拼接即可。

存在问题的代码:

# 人脸检测器scrfd
scrfd_detetor = SCRFD("../../model/SCRFD/scrfd_500m_kps.onnx")  # scrfd人脸定位

# 人脸身份识别器
sess = onnxruntime.InferenceSession('../../model/MobileNetV2_onnx/IDRecognition.onnx')  # 通过onnx文件加载onnxruntime推理引擎

id_img_Dir = "../../data/id_dataset/"

修改后的代码:

import sys
import os
from pathlib import Path
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(curPath)[0]  
sys.path.append(rootPath)

from pathlib import Path

rootPath = str(Path(rootPath).parent)   #'F:\\IdentityDetection\\ID_recognition'
# 人脸检测器scrfd
scrfd_detetor = SCRFD(rootPath + "/model/SCRFD/scrfd_500m_kps.onnx")  # scrfd人脸定位
# 人脸身份识别器
sess = onnxruntime.InferenceSession(rootPath + '/model/MobileNetV2_onnx/IDRecognition.onnx')  # 通过onnx文件加载onnxruntime推理引擎

id_img_Dir = rootPath + "/data/id_dataset/"
37、python GIL特性

参考python中的GIL详解

38、python @dataclass的使用

参考

  • Python 的 dataclasses

  • Python中的数据类dataclass详解

使用自定义类 Class时,存在

  • 对象的描述不太友好

  • __init__方法中重复代码 (示例中每个属性都需要写3遍)

  • 需要自己实现__repr__方法, 和比较方法__eq__, __gt__

class Player:
    def __init__(self, name, number, position, age, grade):
        self.name = name
        self.number = number
        self.position = position
        self.age = age
        self.grade = grade
    def __repr__(self):
        return f'Player: \n {self.name}\t #{self.number}\t @{self.position}\t <{self.grade}>'
    def __eq__(self, other):
        return self.age == other.age
    def __gt__(self, other):
        return self.age > other.age
    def swing(self, pos):
        self.position = pos

dataclass 提供一个简便的方式创建数据类

  • 默认实现__init__(), __repr__(), __eq__()方法.

  • dataclass支持数据类型的嵌套

  • 支持将数据设置为不可变

 @dataclass
 class Player:
   number: int
   position: str
   age: int
   grade: str
   name: str = ""
39、python处理异常

参考python异常和错误

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

其中BaseException是所有错误的基类。

40、对象列表按某个属性排序

参考 Python 对象按照某个属性排序

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

    def __repr__(self):
        return '(%s: %s)' % (self.name, self.score)


import functools   # 排序引用包


def my_cmp(self, s):
    """
    自定义排序方法
    :param self:
    :param s:
    :return: 按照 成绩从大到小, 当相等后,安装字母 A - Z
    """
    if self.score > s.score:
        return -1
    elif self.score < s.score:
        return 1
    else:
        if self.name < s.name:
            return -1
        elif self.name > s.name:
            return 1
        else:
            return 0


def testClassSort():
    L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77), Student('fbl', 88)]   # 对象列表
    print(sorted(L, key=functools.cmp_to_key(my_cmp)))    # 调用自定义排序方法排序

Note:注意my_cmp要有self参数,否则会报TypeError: my_cmp() takes 1 positional argument but 2 were given

你可能感兴趣的:(技能树,#,python,python,开发语言)