python学习

后缀

  • .py

    • 纯Python文件
  • .ipynb

    • Jupyter Notebook文件的扩展名,代表Ipython Notebook
  • .pyi

    • 类型提示文件,提供代码静态类型信息,帮助开发人员进行类型检查和静态分析
    • 命名和对应py文件相同,以便编译器将二者进行关联
  • .pyc

    • Python字节码文件的扩展名,存储已编译的Python源代码中间表示形式
    • 包含已编译字节码,可以更快被Python解释器加载和执行,因为解释器无需再次编译源代码
  • .pyd

    • Python扩展模块的扩展名,表示使用C或者C++编写的二进制Python扩展模块文件
    • 存储编译过后二进制文件,包含扩展模块代码以及与Python解释器交互信息
    • 通过import在Python中导入使用,如同普通Python模块
  • .pyw

    • Python窗口化脚本文件扩展名,表示特殊类型的Python脚本文件,用于创建没有命令行的窗口化应用程序
  • .pyx

    • Cpython源代码文件扩展名
    • 编译型的静态类型扩展语言,允许Python代码中使用C语言的语法和特性,以提高性能与C语言库进行交互

字符串

  • 1、startwith(xx)

    • 判断字符串是否以xx开头,返回一个布尔值
  • 2、endwith(xx)

    • 判断字符串是否以xx结尾,返回一个布尔值
  • 3、isdecimal()

    • 判断是否为十进制数
  • 4、isdigital()

    • 判断是否为数字
  • 5、strip()

    • 去除两边指定内容
  • 6、upper()

    • 将字符串变为大写
  • 7、lower()

    • 将字符串变为小写
  • 8、replace(‘A’,’B’)

    • 将A替换为B
  • 9、split(‘ ’)

    • 通过空格将字符串从左往右切分为列表
  • ​ rsplit(‘ ’)

    • 通过空格将字符串从右往左切分为列表
  • 10、join(‘ ’)

    • 将列表内的元素通过
  • 11、format()、f、%

    • 格式化字符串
  • 12、center(num,’A’)

    • 将字符串内容居中
  • 13、ljust(num,’A’)

    • 将字符串内容居左
  • 14、rjust(num,’A’)

    • 将字符串内容局右
  • 15、zfill(num)

    • 将二进制数据左边填充0,凑够num位数据
  • 16、append(’A’)

    • 将A附加到列表中
  • 17、len()

    • 获取长度
  • 18、切片只能读数据,无法修改数据(字符串内部存储时不允许修改)

    • str[x]
  • 获取字符串中字符

    • str[x:y]
  • 从x个字符到y-1个字符

    • str[x:]
  • 从x个字符到最后一个字符

    • str[:y]
  • 从开始到第y个字符

    • str[x:y:z]
  • [x,y]表区间范围,z表步长,xyz可为负(翻转字符)

  • for循环

    • range(x):从0到x-1

    • range(x,y,z):从x到y-1,步长为z

列表

  • 容器:存放不同类型数据

  • append:按照顺序附加

  • extend:批量追加

  • insert(num,str):插入,将字符串插到第num个位置

  • 注意:num<0时永远放在首位,num>str整体长度时永远放在末尾

  • remove:删除列表元素,如果删除元素列表内没有则报错

  • random:

    • choice:从列表中随机抽取一个
    • sample:随机抽取一个元素
    • shuffle:打乱列表
  • pop(num):根据num位置删除

  • clear:清空列表

  • index(str):找到str是第几个元素

  • sort:转为unicode再排序

  • ord:十进制

  • reverse:反转列表内的元素

  • extend:将别的列表元素添加到当前列表

  • enumerate:将可迭代对象内部元素和其下标序号封装到一起返回

  • zip:将两个列表压缩到一起进行迭代

公共功能:

  • +合并

  • *列表翻倍

  • in、len、索引

  • 千万不要循环对列表进行删除操作,否则删不干净,倒着删不影响前面元素索引

  • 转换:元组可以转换为列表

  • 嵌套:列表内可存放列表,从而提升列表维度,索引时候多级索引,空列表不能直接进行索引

  • 元组:有序且不可变的容器,在里面可存放多个不同类型元素,最好在末尾加上‘,’

  • 转换:字符串=>元组,[]进行索引

集合

  • 不允许存放重复可变类型

    • 无序,不支持索引
    • 可变,能添加修改
    • 不允许重复变量
  • 定义空集:v=set(),而v={}

  • 添加:add()

  • 删除:discard()

  • 交集:intersection

  • 并集:union

  • 差集:difference

  • 集合的存储原理:通过散列表进行散列,因此元素必须可哈希(int、bool、str、tuple)

  • 注意:1和true,0和false不能同时存在于集合,集合不可嵌套

  • none类型:空值,可节省内存

字典

  • 无序

  • 键不重复

  • 键值对可变

  • 创建空字典:dict()

  • 键必须可哈希:int、bool、str、tuple

  • 值:任意类型

  • 字典存储更加规范

  • 字典独有功能:get()取值

  • 若字典内没有则返回None

  • get(‘A’,’B):若A不在字典中,则返回B

  • keys():获取字典的所有键

  • value():获取字典的所有值

  • items():获取键值形成一个高仿列表内置无组

  • for key,value in info.items():获取键值

  • for item in info.items():item[0]获取键值

  • 设置默认值:setdefault(key,value)

  • 更新字典键对:update({key,value})没有则填,有则修改

  • 移除键值对:pop()

  • 按顺序移除:popitem()

  • |交、in、len、索引

  • 转为字典([[],[]])

类型判断

  • isinstance(obj,数据类型)

match语句

match A:
    case a:
        print(a)
    case b:
        print(b)

代码规范

名称

文件夹:commands、data_utils

文件:page、db_convert

注释

文件夹:_init_.py进行注释

代码:#

TODO:即将做的功能

减少if嵌套数量、减少循环数量、代码格式化

pass:占据代码块位置、实际什么也不做

is:表示内存地址是否一致

位运算:左移、右移、异或

单文件引用

注释放顶部、全局变量大写、TODO信息

主文件:

if (__name__=='__main__'):
    pass

配置信息放在config.py文件内

utils:工具包

src:业务代码

db:存放数据

app:主文件

bin:存放入口文件app.py

异常捕获

try:
	pass
except:
	pass
else:
    p
finally:
	pass

raise Exception():当出现指定情况时候,让编译器报出错误信息

异常类型:

  • warning
  • StandardError
    • EnvironmentError
      • IoError
    • ArithmeticError
      • OverflowError
      • ZeroDivisionError
    • RuntimeError
    • TypeError
    • ValueError
    • LookupError
      • IndexError
      • KeyError

当函数检测到不能解决的问题时,最好抛出一个异常,而不是引入一个不完美的修补

回溯:traceback.format_exc(),返回一个字符串

可以将回溯信息写入日志文件

assert(断言)

  • #本质就是
    if(符合条件):
        正常运行
    else:
        报错
    
  • 使用方式:assert + 条件(False或True) + 逗号 + 条件为False时显示的字符串

  • 断言是一种除错机制,用于验证代码是否符合编码人员的预期。编码人员在开发期间应该对函数的参数、代码中间执行结果合理地使用断言机制,确保程序的缺陷尽量在测试阶段被发现

  • 如果在程序运行阶段某个条件不符合断言机制,那么程序会崩溃退出

  • 断言必须使用宏定义,静止直接调用系统提供的assert()–如上所述,断言一般用于测试版本

  • 运行时可能会导致的错误,严禁使用断言

  • 严禁在断言内改变运行环境

  • 一般不要把多条语句放在同一个断言中

  • 断言失败程序直接崩溃

  • 执行时自动跳过assert语句:python -0 py文件

上下文管理

  • contextlib和with语句改写可复用的try/finally代码
  • 当需要使用异常捕获时候,能尽量使用with改写最好,这样可以免去编写try/finally结构所需要的重复代码
  • with语句提供contextmanager的修饰器,一个标准函数只要经过修饰即可使用with语句,这样做比标准写法更加便捷
  • 标准方式需要定义更新的类,提供___enter_____exit____方法
  • __enter__在进入with语句块之前被调用,这个方法的返回值赋给了with后的t变量
  • __exit__在执行完with语句块之后被调用

带有目标的with语句

  • with语句的情景管理器也可以返回一个对象

  • 通过as关键字可以指定一个局部变量,python会将这个对象赋给变量

  • 在情景管理器内可通过yield语句返回一个值,该值可通过as赋给变量

  • 如果在with语句块内发生异常,那么__exit__方法可以拿到关于异常的详细信息:

    • exc_type:异常类型
    • exc_value:异常对象
    • exc_tb:异常堆栈信息

contextlib模块

  • 除了自己实现__enter__和__exit__之外,还有更简单的方式
  • 使用contextlib把上下文管理器当作一个装饰器来使用
from contextlib import contextmanager

@contextmanager
def fun():
    print('start')
    yield 'doing'
    print('end')

if __name__ == '__main__':
    with fun() as f:
        print(f)
结果:
	start
	doing
	end
  • 如果被装饰的方法发生了异常,那么我们需要在自己的方法中进行异常处理,否则不会yield之后的逻辑

closing方法

from contextlib import closing

class Test():

    # 定义了 close 方法才可以使用 closing 装饰器
    def close(self):
        print('closed')

# with 块执行结束后 自动执行 close 方法
with closing(Test()):
    print('do something')

with语句块执行结束后,会自动调用Test实例的close方法

文件操作

ini、xml文件

字节类型:utf-8、gbk

读文件:

with open(filename,mode,encoding) as fp:
    
    fp.read()#读取文件
 

文件打开模式:

‘r’:只读

‘w’:只写

‘x’:文件存在报错

‘a’:追加

‘b’:二进制

‘t’:文本

‘+’:读写

读功能:

read(num):读取num个字符,mode=’rb’时候读num个字节

readline(num):读num行

readlines():将每行数据转为字符串放在列表内

flush():将内容从内存刷新到硬盘

tell():告诉光标位置

with():上下文管理,自动关闭文件

文件操作:

文件移动:shutil.move()

文件重命名:os.rename()

创建文件夹:os.makedirs()

ini格式为:installation缩写

configparser()模块

.section():获取节点形成列表

add_section():添加节点

remove_section:删除节点

excel文件操作

from openpyxl import load_workbook

a=load_workbook(filename)

相关操作:

  • sheetname:获取所有sheet名称

  • 选择sheet:A[‘sheet名称’]

  • 选择sheet内单元格:sheet.cell(x,y)

  • 选择sheet基于索引位置:worksheets[num]

  • 循环所有sheet

    for A in B:
        cell.value()
    

单元格起始位置为1

value:值

style:样式

font:字体

alignment:排列情况

sheet.rows:获取某个单元格

修改值:cell.value

压缩文件

shutil.make_archive(filename,extract_dir,format)

zipfile.ZIPFILE(file,mode,compression,allowziple)

  • file:创建zip压缩包的文件路径和名称
  • mode:模式w.r.a
  • compression:zipfile.ZIP_STORED 默认不改变文件大小 zipfile.ZIP_DEFLATED 压缩、变小
  • allowzip64:超过2GB时需要设置为TRUE

在zip中添加文件:zp.write(filename,arcname)

  • filename:添加内容的路径和名称
  • arcname:添加后的新名称,默认为不变

关闭压缩文件:zp.close()

路径操作

  • 获取当前运行py脚本所在路径:os.path.abspath(_file_)
  • 文件路径:base_dir+file_name
  • 路径拼接时兼容windows和linux:os.path.join(base_dir,files,file_name)
  • 判断路径是否存在:os.path.exists()
  • 获取文件上级目录:os.path.dirname()
  • 创建文件夹:os.makedirs()
  • 是否为文件夹:os.path.isdir()
  • 删除文件或文件夹:shutil.rmtree()
  • 拷贝文件夹:shutil.copytree()
  • 拷贝文件:shutil.copy()
  • 文件或文件夹重命名:shutil.rename()
  • 移动文件或文件夹:shutil.move()
  • 查看目录下所有文件:listdir()
  • 遍历目录:walk,返回值为path,folder-list(文件夹),file_list(文件),遵循深度优先
  • python解释器路径:sys.path

发送邮件

import smtplib

msg=HTMETEXT(‘A’,’html’,’utf-8’)

msg[‘from’]=formataddr([‘A’,’B’])

A:邮件发送人 B:发送地址

msg[‘subject’]=‘C’

C:邮件主题

发件部分:sever=smtplib.SMTP_SSL(‘A’)

sever.login(‘B’,’C’)

sever.sendmail(‘B’,’E’,msg.as_string())

sever.quit()

A:smtp邮箱地址:smtp.126.com/smtp.163.com

B:发件人邮箱:1614152376@qq.com

C:授权码

E:收件人邮箱

msg.as_string:邮件头部信息

from email.mime.text import MIMEText

from email.utils import format_addr

msg[‘to’]=format_addr(‘a’,’b’)

a:收件人 b:收件地址

序列化操作(pickle)

copyreg模块

  • 将python对象序列化为字节流,也可以将字节流反序列化为python对象
  • 该方法不够安全,如果混入恶意信息,进行反序列化时候会对程序产生危害
  • 如果之前保存的类和现在的类不同,在将类进行反序列化时候必将会有信息丢失
  • 此时可以使用copyreg模块为缺失属性提供默认值
copyreg.pickle(game_state,pickle_game_state)
state_after=pickle.loads(serialized)
print(state_after.__dict__)
  • 内置的pickle模块,只适合用来在彼此信任程序之间,对相关对象执行序列化和反序列化操做
  • 如果做法比较复杂pickle模块功能或许会出现问题
  • 把内置copureg模块和pickle结合使用,以便旧数据添加缺失的属性值、版本管理,并给序列化之后的数据提供固定引入路径

json模块

  • 安全方法,json数据反序列化时候不会给python程序产生额外风险
  • 如果在不信任的人或程序之间进行通信,用json最好

函数

动态参数

*args:按照位置传参,传入可迭代类型

**kargs:按照字典传参

注意:

  • 1、**一定在*之后

  • 2、参数和动态参数混合时,动态参数放后面

  • 3、默认值和动态参数同时存在

  • 4、用于将传入的参数转化为元组,如果用带有*的生成器为参数,调用这种函数,python会将该生成器完整迭代完成一轮。

获取内存地址:id(a)

函数参数默认传递内存地址,节省内存,对可变语言可修改

函数返回的是内存地址

python内部缓存驻留机制,变量和数字都使用相同内存地址

补充:传参时如果传列表,函数会自动打散列表,按顺序传值

函数和函数名:一个代指函数的变量

函数可被哈希,同时可充当集合的元素、字典的键

函数名赋值:python以函数为作用域

匿名函数:a=lambda 参数:函数体(单行) #函数返回值赋给a

三元运算:A if B else C

生成器:next()

放生成器对象,执行生成器代码

yeild A 断点,每次进入生成器以断点执行,遇到return则报错

send()执行生成器函数并给yeild传值

内置函数:

第一组:

abs()绝对值

pow(i,j):i的j次方

sum([list]):将可for循环的数据元素求和

a,b=divmod(i,j)

第二组:

min():求最小值(可迭代类型),后可添加函数

max():同理(返回结果为原值)

all():是否全为TRUE

any():只要一个为TRUE,则全为TRUE

第三组:

ord():获取字符的unicode码点

chr():获取unicode码点对应字符

第四组:

type():获取数据类型

enumerate():维护类型数据索引

id():获取内存地址

hash():转为哈希值

help():显示提示信息(使用手册)

zip():拉链函数

callable():是否可执行

推导式:i for i in range(N)

hashlib:

  • md5

匿名函数

lambda x:len(x)

拆解后:

def lambda(x):
	return len(x)

lambda后跟函数接口,冒号后跟函数具体实现,将具体实现产生的结果作为返回值返回

钩子函数

钩子(挂钩、回调)函数:作为参数传入目标函数,当运行目标函数时只有符合预期条件时才会调用钩子函数,通过钩子函数改变目标函数执行逻辑,输出结果。

  • 只有符合预期条件才会执行钩子函数
  • 钩子函数作为参数传入目标函数
  • 钩子函数运行结果作为目标函数的参数直接参与到目标函数运行逻辑中,用来改变目标函数运行逻辑。

类型暗示

#表示传给a为字符,传给b为整型,fan
def fun(a:str,b:int)->str:
	return a+str(b)

动态引入

  • 如果两个模块之间相互调用对方才能完成引用操作,那就会出现循环依赖现象,可能会导致程序启动时候崩溃

  • 打破循环依赖应该把相互依赖的代码重构为单独的代码,放在依赖目录最底层

  • 执行动态模块引入操作,可以缩减重构所花精力,尽量降低代码复杂度,这种操作最简单

枚举

  • from enum import Enum
    class A(Enum):
        a=1
        b=2
        c=3
    """
    继承Enum基类后一个枚举类中key不能相同
    如果希望value也不同可以导入unique
    """
    @unique
    class A(Enum):
        a=1
        b=2
        c=3
    

闭包

  • 防止局部变量污染全局变量

nonlocal

  • 获取上层作用域的某个变量值
  • 难以追踪

生成器(yield)

  • 生成器是迭代器的高阶产物,迭代器是iter方法,生成器则是next方法
  • 含有yield的函数不会立刻执行,而是得到一个生成器
  • 直到调用该生成器中_next_方法,函数开始执行
  • 函数遇到yield关键字,然后把yield当作return使用,函数中断并返回值
  • 当再次调用该函数时,不会再重新执行函数,而是从刚才yield的地方开始执行
  • 如果在函数外调用send()方法,则可以将函数外参数发送给函数内刚才yield的那个位置
  • 由于send()包含next方法,程序会接着往下运行
  • 迭代器只能返回一轮的结果,在抛出stopiteration异常的迭代器进行二轮迭代是不会有结果的
  • 如果想判断某个值是迭代器还是容器,可以拿该值调用两次iter函数,如果结果相同则为迭代器,否则为生成器

正则表达式

import re

findall(A,B):从B中匹配A并生成列表

(‘[abc]’,B):从B中匹配abc并生成列表

(‘q[abc]’,B):B中匹配abc并生成列表

(‘t[a-z]’,B):从B中匹配ta-tz的字符

(‘r.o’,B):从B中匹配r或o

(‘r.+o’,B):贪婪匹配(最长)

(‘r.+o?’,B):非贪婪匹配

(‘r\w+o’,B):

  • \d:数字
  • \a:任意空白符、空格、制表符
  • \w:字母数字下划线

数量:

  • *:重复0-N次
  • +:重复1-N次
  • ?:重复0或1次
  • {n}:重复n次
  • {n,}:重复n-N次
  • {n,m}:重复n-m次

其它:

  • ascii:匹配除了中文以外其它字符
  • 其它特殊字符可以通过\进行转义
  • 起始:^
  • 结束:$

方法:

  • match:匹配成功返回对象,否则返回None值
  • group:返回匹配的值
  • search:只匹配第一个成功的对象并返回
  • sub:替换匹配成功的值
  • split:根据匹配成功位置进行分割
  • finditer:迭代获取数据对象(找大量数据使用该方法)
  • item.groupdict():把匹配成功的数据生成字典

并发与并行

#多线程
from threading import Thread

t=thread(func)

t.start()

#多进程
from multiprocessing import Process
p=Process(target=func)
p.start()

#如果需要给每个线程命名并进行区分,则可给func传参
def func(name):
    pass
t=Thead(target=func,args=())

#线程池
#一次性开辟多个线程,用户直接给线程池提交子任务
from concurrent.futures import ThreadPoolExcutor,ProcessPoolExcutor
#创建线程池,N为线程数量
with ThreadPoolExcutor(N) as t:
    #M为任务数量
    for i in range(M):
        s.submit(func,args=())
        
#协程
async def func1():
    pass

async def func2():
    pass

A=func1()
B=func2()
tasks=[A,B]
#异步执行多个任务
asyncio.run(asyncio.wait(tasks))
#注意:当程序出现同步操作时将会中断,此时将任务挂起
#函数中的同步操作(实现中断指令)
await asyncio.B()

#主流写法:
async def func1():
    pass

async def func2():
    pass

#第一种写法
def main():
	f1=func1()
	f2=func2()
	await f1
	await f2

#第二种方法
def main():
	tasks=[f1,f2]
	await asynico.wait(tasks)

if __name__=='main':
    asyncio.run(main())
    
#tasks里面必须封装待执行的实例化协程对象
#python3.8过后tasks里面不能添加携程对象而是task对象,因此需要将协程对象转换为task对象
tasks=[
    asyncio.create_task(f1),
    asyncio.create_task(f2)
]
await asyncio.wait(tasks)

#aiohttp补充
#如果需要使用异步爬虫,则可使用aiohttp代替requests
async def spider(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url,headers) as res:
            res.content.read()
			#若要保存则
            with open() as fp:
            	fp.write(awit res.content.read())
            re

  • python存在一份全局解释器锁,编写程序时,应当避免多个线程争用同一份数据
  • 不加锁情况下,多条线程修改同一个对象,那么程序的数据结构可能会遭到破坏
  • 内置的threading模块中,有一个名叫lock的类,它用标准的方式实现互斥锁

管道

  • 管道是一种任务处理方式,可以把处理流程划分为若干阶段,并使用多条python同时执行这些任务
  • 构建并发式管线,要注意许多问题,包括防止某个阶段陷入持续等待状态,停止工作流程,防止内存膨胀
  • Queue类可以指定缓冲区尺寸,支持join方法,具备阻塞式队列

协程

asyncio

本质介绍:

asyncio内部有一个事件循环,名叫event_loop,所有待执行的任务都将放入事件循环,当事件循环中某个任务阻塞时候,自动切换到另一个任务,从而实现宏观上的异步,但是某个时间点只有一段代码在跑,且如果某个任务陷入死循环,将会造成其它任务饥饿。因此该模块适用于等待较多的程序提高性能,但是如果程序时刻运转,则帮助不大。

async def fun():将function变为coroutine function,这个功能是将函数变为coroutine对象

asyncio.create_task():将coroutine对象转换为task对象,并且将task对象注入event_loop事件循环

asyncio.gather():将task对象封装到为future对象进行await,返回值也按顺序封装成列表一起返回,从而避免了task对象一个一个的await,可以直接传入coroutine对象,gather方法会自动将coroutine对象封装为task并加入event_loop

asyncio.run():执行主函数,也就是将任务进行封装的过程函数

await:当某个任务需要阻塞时候,提醒event_loop进行切换,后面可跟coroutine对象、task对象、future对象

concurrent

利用futures类实现平行计算

pool=ProcessPoolExecutor(max_worker=2)	#cpu几核处理j
results=list(pool.map(gcd,numbers))

要点

  • 把引发cpu性能瓶颈的那部分代码用c语言扩展模块改写,可尽量提升程序执行速度,不过容易引发bug

  • multiprocessing模块提供一些强大工具,只需提供少量代码可实现平行计算

  • 最恰当做法是通过内置的concurrent.futures模块及ProcessPoolExecutor类来使用它

  • multiprocessing模块所提供那些高级功能,都特别复杂,尽量不要直接使用

深浅拷贝

import copy
#浅拷贝
copy.copy()
#深拷贝
copy.deepcopy()
#对于可变类型无论哪层都被拷贝
#特殊:元组遇到浅拷贝只拷贝一层
#元组深拷贝可以拷贝所有可变类型元素

面向对象编程

class object(self):
    #此处用于初试化类内一些信息参数
	def __init__(self):
        pass
    #此处用于定义类内一些方法
    def method(self):
        pass

封装

将类方法、属性封装

继承

子类(派生类)继承父类(基类、超类),从而实现代码重用,从左到右继承

  • 1、调用父类方法属性

  • 2、重写父类方法

class father():
	def _init_(self):
        self.a='aaa'
    def action(self):
        print('调用父类方法')
        
class son(father):
    def _init_(self):
        self.a='bbb'
    def action(self):
        print('子类重写父类')
  • 3、如果子类重写父类方法,但想调用父类方法,可使用super()
class father():
    def action(self):
        print('调用父类方法')
        
class son(father):
    def action(self):
        super().action()
  • 4、强制使用父类私有方法
class father():
    def _action(self):
        print('父类私有方法')
class son(father):
    def _action(self):
        super()._Father_action()
son=son()
son.action()
  • 5、调用父类__init__方法
class father():
    def _init_(self):
        self.a=a
class son(father):
    def _init_(self):
        super().init()  #或者father._init_(self)
son=son()
son.a
#总结:继承父类属性应加super()

6、不对父类初始化直接赋值,并在子类调用父类初始化过程中,增加自身的初始化参数值

class father():
    def _init_(self,a,b):
        self.a=a
        self.b=b
    def dev(self):
        return self.a-self.b

class son(father):
    def _init_(self,a,b,c=10):
        father._init_(self,a,b)
        self.c=c
son=son(1,2)

7、菱形继承

  • 通过不断调用super()方法来到达菱形的顶部,然后通过递归的方式不断实现代码
  • 调用super()时,必须写出当前类的名称,如果类的名称发生变化,也要修改每一条super()语句

多态

可传多种类型参数

方法

  • 绑定方法:默认一个self参数,由对象调用(self为调用对象)
  • 类方法:默认有一个cls参数,用类或对象都可调用(cls为调用方法这个类)
  • 静态方法:无默认参数,类和对象都可调用

属性

  • 由绑定方法+特殊装饰器组合创建
  • 多用public,少用private

成员修饰符:

  • 公有:任何地方都能调用这个成员name,可继承给子类
  • 保护:_name(单下划线)类内才可使用,可继承给子类
  • 私有:__name(双下划线)类内才可使用,不可继承给子类

注意:

  • 应当多使用保护变量,少用私有变量
  • python编译器无法严格保证private字段私密性
  • 当子类不受自己控制时,才可考虑用private避免名称冲突
  • 不要盲目将属性设置为private,应当一开始就做好规划,允许子类更多访问超类的内部API

父类中的私有成员子类无法继承

特殊语法访问私有变量名

_init_:初始方法

_new_:构造方法

_call_:对象+()

_str_:转字符串

_dict_:转字典

_setitem_:自动触发

_getitem_:字典中通过[]取值

_delitem_:删除字典中键值对

_enter_:当上下文管理协议with运行时候执行此方法

_exit_:当上下文管理协议with结束时候执行此方法

_iter_:迭代器,返回对象本身

_next_:返回下一个数据

注册

  • 函数/类的注册

    • 如果项目中有许多可能随处用到的函数和类,你希望吧所有的这些指定的函数和类整合到一个字典(key是函数名、类名、自定义的名字,value是对应的函数对象或者类),那么这样一个过程称为注册。
    • 如果我将一个函数放入这样一个字典,那就是函数的注册。
    • 如果项目中出现批量的工程平行的函数或者类,使用注册机制能够提高项目模块化。
  • 如何实现注册功能

    • #注册实质就是用一个字典对类就行管理
      #只需要声明一个字典,然后将类名存入就行
      #例如:
      class c1:
          pass
      
      class c2:
          pass
      
      def fun1:
          pass
      
      def fun2:
          pass
      
      #这样就可以将一个类给存入字典,非常方便
      register={}
      register[c1.__name__]=c1
      register[c2.__name__]=c2
      register[c3.__name__]=c3
      '''
      但是以上做法十分不够优雅,如果有几百个函数或者类需要注册,那么就需要写几百个类似:
      register[……]=……
      因此可以用装饰器来帮助我们自动化完成这些内容
      '''
      class Register(dict):
          def __init__(self, *args, **kwargs):
              super(Register, self).__init__(*args, **kwargs)
              self._dict = {}
          
          def register(self, target):
              pass
          
          def __setitem__(self, key, value):
              self._dict[key] = value
      
          def __getitem__(self, key):
              return self._dict[key]
          
          def __contains__(self, key):
              return key in self._dict
          
          def __str__(self):
              return str(self._dict)
          
          def keys(self):
              return self._dict.keys()
          
          def values(self):
              return self._dict.values()
          
          def items(self):
              return self._dict.items()
          
      def register(self, target):
              def add_register_item(key, value):
                  if not callable(value):
                      raise Exception(f"register object must be callable! But receice:				{value} is not callable!")
                  if key in self._dict:
                      print(f"warning: \033[33m{value.__name__} has been registered 					before, so we will overriden it\033[0m")
                  self[key] = value
                  return value
              if callable(target):  
                  # 如果传入的目标可调用,说明之前没有给出注册名字,我们就以传入的函数或者类的名字作为注册名
                  return add_register_item(target.__name__, target)
              else:                   
                  # 如果不可调用,说明额外说明了注册的可调用对象的名字
                  return lambda x : add_register_item(target, x)
      
      register_functions = Register()
      #现在就可以使用该对象注册类
      @register_functions.register
      def add(a : int, b : int):
          return a + b
      
      @register_functions.register("my multiply")
      def multiply(a : int, b : int):
          return a * b
      
      @register_functions.register
      def minus(a : int, b : int):
          return a - b
      

参考文献:

[Python进阶笔记(一)装饰器实现函数/类的注册 ](https://zhuanlan.zhihu.com/p/350821621)

总结:

  • 类的注册是一种很有用的模式
  • 开发者每次从父类继承子类时,父类的元类都可以自动运行注册代码
  • 通过元类实现类的注册,可以确保所有子类都不遗漏

元类

抽象方法

  • 实例方法

    • 只能通过对象进行调用,需要显示传self,即实例对象自己
  • @classmethod(类方法)

    • 可以通过对象或者类进行调用

    • 重构类的时候不需要修改构造函数,只需要额外添加需要处理的函数,然后使用@classmethod即可

    • 第一个参数必须默认传类,用cls参数传递当前类对象,该方法只能访问到类的数据属性,不能获取实例的数据属性

    • 通过类方法的多态机制,我们能够以更加通用的方式来构建并拼接具体子类

    • 调用类内属性和方法应当使用cls().属性名、cls().方法名();而非self.属性名

    • 可以不需要实例化,直接类名.方法名()来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁

    • # 例如
      class Data_test2(object):
          day=0
          month=0
          year=0
          def __init__(self,year=0,month=0,day=0):
              self.day=day
              self.month=month
              self.year=year
              
          @classmethod
          def get_date(cls,data_as_string):
              #这里第一个参数是cls, 表示调用当前的类名
              year,month,day=map(int,data_as_string.split('-'))
              date1=cls(year,month,day)     #返回的是一个初始化后的类
              return date1
       
          def out_date(self):
              print("year :",self.year)
              print("month :",self.month)
              print("day :",self.day)
      

  • @staticmethod(静态方法)
    • 只能通过类调用,与普通函数没有区别

修饰符

  • @property

    • 将类方法强制转化为属性,可以直接通过方法名称来访问该方法(后面不能加括号输入参数)

    • 纯属性取代get和set方法:可以在类中明确实现getter(获取器)和setter(设置器)方法,不过我们最好不要在属性方法中修改其它属性的值,否则会导致古怪的行为

    • 缺点:

      • 和属性相关的方法只能在子类里面共享,而与之无关的其他类无法复用同一份代码
    • 被该@property修饰的方法无法修改

    • #使用方式
      class people:
      	@property
      	def name(self):
         		return "zhangsan"
      
      p1=people()
      #此处调用时候就以属性的形式进行调用
      print(people.name)
      
    • .setter就可重写该方法

    • class people:
      	@property
      	def name(self):
         		return "zhangsan"
          #此处就重写了name方法
          @name.setter
          def name(self,value):
              return value
      
  • @decorator

    • 装饰器,将一个函数封装到另一个函数内部

    • def dec(func):
      	def wrapper():
      		print('start')
      		func()
      		print('end')
      	return wrapper
      	
      #采用此语法糖将fun1封装到dec中
      @dec
      def fun1():
      	print('running……')
      
    • 如果核心代码含有参数,则只需要在内层函数中增加参数即可

    • def dec(func):
          def wrapper(a,b):
              print('start')
      		func(a,b)
      		print('end')
          return wrapper
      
      @dec
      def fun1():
          print('running……')
      
  • 要点

    • python为装饰器提供专门语法,程序在运行的时候,能够使用一个函数来修改另一个函数
    • 对于调试器这种依靠内省机制的工具,直接编写装饰器会引发奇怪的行为
    • 内置的functools模块提供名为wraps的修饰器,开发者定义自己的修饰器应该用wraps做一些处理,避免问题

容器

python内置的容器:list(列表)、tuple(元组)、dict(字典)、set(集合)

  • 元组只能查看,不能增、删、改

  • 集合不允许重复,无序且不支持索引

  • 容器的本质是封装好的类,可以通过继承的方式自定义容器内部方法

  • 如果要定制子类可以直接从python自带容器类型中继承

collection.abc

  • 但是通过继承的方式需要重写每一种方法,如果想要避免这些麻烦可以使用内置的collection.abc模块,该模块定义一系列抽象基类,该基类提供每一种容器应当具备的方法,如果忘记重写就会指出错误。
  • 使用起来很简单,只需要继承该模块中对应的类就行

collections.all

  • 查看所有子类

  • 方法
    namedtuple() 创建命名元组子类的工厂函数,生成可以使用名字来访问元素内容的tuple子类
    ------------- ------------------------------------------------------------
    deque 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop)
    ----- ------------------------------------------------------------
    ChainMap 类似字典(dict)的容器类,将多个映射集合到一个视图里面
    -------- -----------------------------------------------------
    Counter 字典的子类,提供了可哈希对象的计数功能
    ------- --------------------------------------
    OrderedDict 字典的子类,保存了他们被添加的顺序,有序字典
    ----------- --------------------------------------------
    defaultdict 字典的子类,提供了一个工厂函数,为字典查询提供一个默认值
    ----------- --------------------------------------------------------
    UserDict 封装了字典对象,简化了字典子类化
    -------- --------------------------------
    UserList 封装了列表对象,简化了列表子类化
    -------- --------------------------------
    UserString 封装了字符串对象,简化了字符串子类化(中文版翻译有误)
    ---------- -----------------------------------------------------

collections.Counter

  • 计数器,可计数哈希对象
  • 它是一个集合
  • elements():返回迭代器,其中每个元素出现计数值所指定次<1则自动忽略
  • most_common():由频率高到低排序返回(元素,次数)
  • subtract():减去元素
  • fromkeys():

数据结构

双向队列(deque)

  • collection中的deque类是一种双向队列
  • 头部尾部插入或移除一个元素,只消耗常数级别的时间,适合FIFO的队列
  • 虽然list也可做到,但是list从头部操作元素会耗费线性级别的时间

字典

  • 标准字典是无序的,拥有相同键值对的两个dict上面迭代,可能出现不同顺序
  • 采用快速哈希表存储
  • 对应collection模块中OrderedDict类

  • 提供heappush、heappop、nsmallest函数,能够在list类型中创建堆结构
  • 只要访问堆中下标为0的元素,总能够查出最小值

itertools(迭代器)

把迭代器连接起来的函数:

  • chain:将多个迭代器按顺序连成一个迭代器
  • cycle:无限地重复某个迭代器中各个元素
  • tee:把一个迭代器拆分为多个平行的迭代器
  • zip_longest:与内置zip函数相似,但是它可以对应长度不同的迭代器

从迭代器中过滤元素的函数:

  • islice:不进行复制前提下,根据索引值来切割迭代器
  • takewhile:判定函数为True的时候,从迭代器中逐个返回元素
  • dropwhile:从判定函数初次为False的地方开始,逐个返回迭代器中的元素
  • filterfalse:从迭代器中逐个返回能令判定函数为False的元素,效果与内置filter函数相反

能够把迭代器中元素组合起来的函数:

  • product:根据迭代器中的元素计算笛卡尔积,并返回。可以用来改写深度嵌套的列表推导操作。
  • permutation:用迭代器中的元素构建长度为N的各种有序排列,并将所有排列形式返回给调用者。
  • combination:用迭代器中的元素构建长度为N的各种无序组合,并将所有组合形式返回给调用者。

如果我们需要自己编写迭代程序,可以先阅读itertools文档

网络编程

文件传输服务器

该服务器用于临时文件传输,不可用于企业级生产环境

  • 命令行:python -m http.server
    • <自定义端口号(默认端口号8000)>
    • – – bind <ip地址>
    • – – d <工作目录> 或者– – directory <工作目录> (默认规则目录为当前目录)
    • – – cgi :启用CGI请求处理程序

HTML知识补充

本部分知识会单独做笔记,此处只记录基础部分内容

  • 可扩展标记语言
  • 存储:放置配置文件
  • 传输:网络传输时用这种格式文件
  • 开始
  • ……
  • 结尾
  • 读取文件内容

网页解析

  • xml.etree模块

    • from xml.etree import ElementTree as et

    • parse():读取打开

    • getroot():获取根标签

    • 读取节点数据

    • root=et.xml(content)

    • for a in root:获取节点内的所有标签

    • iter(‘A’):在文件中寻找A标签

    • find:找

    • remve:删

    • set:重新设置

    • a.append(B):将B加入A标签

  • bs4模块

    • 具体可见:之前的bs4模块介绍

    • BeautifulSoup(fp,parser):传入html的文件指针fp,解析器‘html.parser’

      • select():查找标签

        • 传给select方法选择器 匹配
          div 所有名为
          的元素
          #author id属性为author的元素
          .notice 使用CSS class属性名为notice的元素
          div span
          元素之内元素
          div > span 直接在
          之内的元素,中间没有其它元素
          input[name] 所有名为,并有name属性的元素
          input[type=“button”] 所有名为,且type属性值为button的元素
        • 返回一个Tag对象列表,列表中有一个Tag对象,值可传递给str()函数

        • getText():返回查找到元素的文本

编译打包

  • pyintaller工具

  • pip install pyinstaller

  • 每次打包前需要将dist文件夹清空,或者增加-y参数,否则将会报错

以下参数可以多次使用

参数列表

内容参考至:Pyinstaller的所有命令选项

参数 含义
-F -onefile;打包单文件,如果代码在多个文件中则别用这个
-D -onedir;传入文件夹,打包多个文件,在dist中生成多个依赖文件,适合编写工具代码
-k -tk;部署时候包含TCL/TK
-a -ascii;不包含编码,在支持unicode的python版本默认包含所有编码
-d -debug;为调试被冻结的应用程序提供帮助。可以多次提供此参数以选择以下几个选项。- all:下列所有三个选项。- imports:为底层Python解释器指定。-v选项,使它在每次初始化模块时打印一条消息,显示模块从哪个位置(文件名或内置模块)加载。 -bootloader:告诉引导加载程序在初始化和启动绑定的应用程序时发出进度消息。用于诊断缺少导入的问题。- noarchive:不是将所有冻结的Python源文件作为归档文件存储在生成的可执行文件中,而是将它们作为文件存储在生成的输出目录中。
-w –windowed;使用windows执行时不打开命令行
-c -console;使用windows执行时打开命令行,为标准i/o打开一个控制台窗口(默认)
-s -strip;可执行文件和共享库将run through strip,注意cygwin的strip往往使普通的win32dll无法使用,对可执行库和共享库应用符号表条带
-X -upx;如果有upx安装(执行configure.py时候会检查),会压缩执行文件
-o -out;指定spec文件的生成目录,如果没有指定,而且当前目录是pyinstaller的根目录,会自动创建一个用于输出的目录,如果当前目录不是pyinstaller根目录,会输出到当前目录下
-p -path;设置导入路径,可以使用路径分割符(windows分号,linux冒号),指定多个目录,也可以使用多个-p参数设置多个导入路径,让pyinstaller自动寻找资源,如果使用虚拟环境,可将venv下的Lib文件中sitepackages路径传入
-i -icon;图标路径
-v -version;将verfile作为可执行文件版本资源(只对windows系统有效),添加一个版本资源从文件到exe
-n -name;要分配给绑定的应用程序和规范文件的名称(默认:第一个脚本的basename)
-m 添加manifest文件或XML到exe
-r 向Windows可执行文件添加或更新资源。资源是一到四个项目,FILEI.TYPEI.NAMEI.LANGUAGEIII。FILE可以是数据文件,也可以是exe/dll。对于数据文件,至少必须指定TYPE和NAME。LANGUAGE默认为O,也可以指定为通配符*,以更新给定TYPE和NAME的所有资源。对于exe/dll文件,如果在指定通配符时省略TYPE, NAME和LANGUAGE,则FILE中的所有资源将被添加/更新到最终的可执行文件
--uac-admin 使用此选项将创建一个Manifest,该Manifest将在应用程序重启时请求提升。
--uac-uiaccess Windows并行程序集搜索选项(高级),使用此选项允许提升应用程序与远程桌面一起工作。
--win-private-assemblies 任何绑定到应用程序中的共享程序集都将被更改为私有程序集。这意味着将始终使用这些程序集的确切版本,并且将忽略在用户机器上安装的任何较新的版本。
--win-no-prefer-redirects 在搜索要捆绑到应用程序中的共享程序集或私有程序集时,PyInstaller将不愿意遵循重定向到新版本的策略,并将尝试捆绑该程序集的确切版本。
--hidden -import 打包依赖文件
--distpath 应用程序放置位置
--workpath 临时文件存放位置
-y 替换输出目录
--clean 构建之前清理pyinstaller缓存文件
--log-level LEVEL 构建时控制台消息中的详细信息量。级别可能是TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL(默认值:INFO)中的一个。
--specpath 存放spec文件的文件夹
-add-data 要添加到可执行文件中的其他非二进制文件或文件夹。路径分隔符是平台特定的os。Pathsep(也就是;在Windows和:在大多数unix系统上)。
--add-binary 要添加到可执行文件中的附加二进制文件。
--collect -submodules 从指定的包或模块中收集所有子模块。
--collect-data 从指定的包或模块收集所有数据。
--collect-binaries 从指定的包或模块中收集所有二进制文件。
--collect-all 从指定的包或模块中收集所有子模块、数据文件和二进制文件。
--copy-metadata 复制指定包的元数据。
--recursive-copy-metadata 复制指定包及其所有依赖项的元数据。
--additional-hooks-dir 用于搜索钩子的附加路径。
--runtime-hook 自定义运行时钩子文件的路径。运行时钩子是与可执行文件绑定在一起的代码,在任何其他代码或模块之前执行,以设置运行时环境的特殊特性。这个选项可以多次使用。
--exclude-module 可选的模块或包(Python名,而不是路径名)将被忽略(就像没有找到它一样)。
--key 用于加密Python字节码的密钥。
--splash (实验性)在应用程序中添加带有图像图像文件的启动画面。启动画面可以在拆包时显示进度更新。
--noupx 不使用UPX,即使它是可用的(在Windows工作不同)
--upx-exclude 当使用upx时,防止二进制文件被压缩。这通常用于upx在压缩过程中损坏某些二进制文件的情况。FILE是不带路径的二进制文件的文件名。
--disable-windowed-traceback 在有窗口(noconsole)模式(仅Windows和macOS)中禁用未处理异常的追溯转储,并显示一条消息表明该功能已禁用。
以下是Mac特定选项
--osx-bundle-identifier Mac OS X.app bundle标识符被用作代码签名的默认唯一程序名。通常的形式是反向DNS表示法中的层次名称。例如:com.mycompany.department.appname(默认值:firstscript’s basename)
--target-architecture 目标架构(仅macOS;有效值:x86_64, arm64, universal2)。允许在通用2和单arch版本的冻结应用程序之间切换(提供python安装支持目标架构)。如果没有指定目标体系结构,则以当前运行的体系结构为目标。
--codesign-identity 代码签名标识(仅macOS)。使用提供的标识对收集的二进制文件和生成的可执行文件进行签名。如果没有提供签名标识,则执行临时签名。
--osx-entitlements-file 在对收集的二进制文件进行编码签名时使用的授权文件(仅限macOS)
--runtime-tmpdir 在一个文件模式下提取库和支持文件。如果给出了这个选项,引导加载程序将忽略运行时操作系统定义的任何临时文件夹位置。将在这里创建_meixxxxxx -文件夹。请使用这个选项,只有当你知道你在做什么。
--bootloader-ignore-signals 告诉引导加载程序忽略信号,而不是将它们转发给子进程。在这样的情况下非常有用,例如,一个管理进程同时向引导加载程序和子进程发出信号(例如通过进程组),以避免向子进程发出两次信号

spec文件参数

  1. 可以先pyinstaller py文件
  2. 修改生成的spec文件
  3. pyinstaller spec文件
# -*- mode: python ; coding: utf-8 -*-


block_cipher = None

#	此项目中所有的py文件(要打包进去的所有py文件),和主程序不在同一个包中的py文件用绝对路径。
a = Analysis(
    ['G:\\源码\\Python\\python打包\\打包工具.py'],
    pathex=[],	# 项目绝对路径
    binaries=[],
    datas=[],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,  
          [],
          name='docx2xml',	#打包程序的名字
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          # console=True表示,打包后的可执行文件双击运行时屏幕会出现一个cmd窗口,不影响原程序运行
          console=True,
          disable_windowed_traceback=False,
          target_arch=None,
          codesign_identity=None,
          entitlements_file=None,
          # 如果想要修改程序图标,使用在EXE()中加入 icon='xxxxx', 切记:绝对路径
          icon=[''],
          )

  • 变量 含义
    a Analysis类的实例,要求传入各种脚本用于分析程序的导入和依赖。a中内容主要包括以下四部分:scripts,即可以在命令行中输入的Python脚本;pure,程序代码文件中的纯Python模块,包括程序的代码文件本身;binaries,程序代码文件中需要的非Python模块,包括–add-binary参数指定的内容;datas,非二进制文件,包括–add-data参数指定的内容。
    pyz PYZ的实例,是一个.pyz文件,包含了所有pure中的所有Python模块。
    exe EXE类的实例,这个类是用来处理Analysis和PYZ的结果的,也是用来生成最后的exe可执行程序。
    coll COLLECT类的实例,用于创建输出目录。在-F模式下,是没有COLLECT实例的,并且所有的脚本、模块和二进制文件都包含在了最终生成的exe文件中。
    block_cipher 加密密钥
  • 修改比较多的是a、exe的内容

  • 参数 含义
    Analysis参数scripts 也是第一个参数,它是一个脚本列表,可以传入多个py脚本,效果与命令行中指定多py文件相同,即py文件不止一个时,比如“pyinstaller xxx1.py xxx2.py”,pyinstaller会依次分析并执行,并把第一个py名称作为spec和dist文件下的文件夹和程序的名称
    Analysis参数pathex 默认有一个spec的目录,当我们的一些模块不在这个路径下,记得把用到的模块的路径添加到这个list变量里,项目需要从什么地方导入自定义库。同命令“-p DIR/–paths DIR”.
    Analysis参数datas 作用是将本地文件打包时拷贝到目标路径下。datas是一个元素为元组的列表,每个元组有两个元素,都必须是字符串类型,元组的第一个元素为数据文件或文件夹,元组的第二个元素为运行时这些文件或文件夹的位置。例如:datas=[(’./src/a.txt’, ‘./dst’)],表示打包时将"./src/a.txt"文件添加(copy)到相对于exe目录下的dst目录中。也可以使用通配符:datas= [ (’/mygame/sfx/*.mp3’, ‘sfx’ ) ],表示将/mygame/sfx/目录下的所有.mp3文件都copy到sfx文件夹中。也可以添加整个文件夹:datas= [ (’/mygame/data’, ‘data’ ) ],表示将/mygame/data文件夹下所有的文件都copy到data文件夹下。同命令“–add-data”。
    Analysis参数binaries 添加二进制文件,也是一个列表,定义方式与datas参数一样。没具体使用过。同命令“–add-binary”。
    Analysis参数hiddenimports 指定脚本中需要隐式导入的模块,比如在__import__、imp.find_module()、exec、eval等语句中导入的模块,这些模块PyInstaller是找不到的,需要手动指定导入,这个选项可以使用多次。同命令“–hidden-import MODULENAME/–hiddenimport MODULENAME”。
    Analysis参数hookspath 指定额外hook文件(可以是py文件)的查找路径,这些文件的作用是在PyInstaller运行时改变一些Python或者其他库原有的函数或者变量的执行逻辑(并不会改变这些库本身的代码),以便能顺利的打包完成,这个选项可以使用多次。同命令“–additional-hooks-dir HOOKSPATH”。
    Analysis参数runtime_hooks 指定自定义的运行时hook文件路径(可以是py文件),在打好包的exe程序中,在运行这个exe程序时,指定的hook文件会在所有代码和模块之前运行,包括main文件,以满足一些运行环境的特殊要求,这个选项可以使用多次。同命令“–runtime-hook RUNTIME_HOOKS”。
    Analysis参数excludes 指定可以被忽略的可选的模块或包,因为某些模块只是PyInstaller根据自身的逻辑去查找的,这些模块对于exe程序本身并没有用到,但是在日志中还是会提示“module not found”,这种日志可以不用管,或者使用这个参数选项来指定不用导入,这个选项可以使用多次。同命令“–exclude-module EXCLUDES”。
    exe参数console 设置是否显示命令行窗口,同命令-w/-c。
    exe参数icon 设置程序图标,默认spec是没有的,需要手动添加,参数值就是图片路径的字符串。同命令“命令-i/–icon”。

关于动态加载的module导入问题

有些脚本中,我们使用了__import__等一些动态导入库的函数时,pyinstaller打包是无法找到这些模块的,在使用时就会报"ImportError 找不到指定模块",这个时候就需要手动导入动态加载的模块。

在以下结构中,我们__import__(‘A.AA’)

  • A
    • AA
      • AAA
      • AAB
    • AB
      • ABA
      • ABB

直接在spec中声明

在"Analysis参数hiddenimports"中添加需要导入模块,hiddenimports=[‘A.AA’]。

通过hook脚本导入

  1. 在spec中,使hiddenimports=[‘A’]
  2. 名称按照"hooks-xxx.py"的方式创建一个脚本,此时我们创建一个"hooks-A.py", 脚本的主要内容为给"hiddenimports"这个list赋值,可以直接赋值,也可以写逻辑(完全可以看成一个py脚本)。
  3. 填写spec中的hookspath参数,值为"hooks-xxx.py"的路径。

runtime_hooks参数的使用

runtime_hooks参数可以跟hookspath一样,指定脚本,当我们运行打包的exe时,会在exe执行前,先将参数中对应的脚本先执行一次。可以用作创建exe运行的一些环境。

加密问题

上面还有个变量block_cipher,主要是防止exe被反编译。

block_cipher = pyi_crypto.PyiBlockCipher(key='123456789')

虚拟环境打包

# 1.安装pipenv
pip install pipenv
# 2.进入文件夹
cd 文件夹路径
# 3.创建虚拟环境
pipenv install
# 4.进入虚拟环境
pipenv shell
# 5.查看依赖安装是否成功
pipenv graph
# 6.生成spec文件
pyi-makespec -D -C py文件路径
# 7.根据spec文件打包
pyinstaller spec文件路径

pip命令

  • 升级pip

pip install - -upgrade pip

或者:pip install -U pip

  • 安装某个版本

pip install package_name

指定版本:pip install package_name==版本号

  • 卸载包

pip uninstall package_name

  • 升级包

pip install - -upgrade package_name

pip install -U package_name

  • 查找安装包

pip search package_name

  • 查看包信息

pip show -f package_name

  • 查看需要被升级的包

pip list -o

  • 查看兼容问题

pip check package_name

pip check requirments.txt

  • 指定国内源

pip install -i source_url

  • 下载包但是不安装

pip download package_name -d ‘下载路径’

  • 如果下载路径为 ‘.’,则下载到默认路径

  • 批量安装requirements.txt

pip install -r requirements.txt

  • 生成requirements.txt文件
  1. pip freeze > requirements.txt

  2. 使用pipreqs

    1. pip install pipreqs

    2. pipreqs ./ - - encoding=utf-8或者pipreqs ./

    3. 如果项目中已存在requirements.txt文件则使用:pipreqs - -force ./

虚拟环境

virtualenv

pyvenv可以帮助我们创建不同python环境,我们可以再同一个系统上面安装软件包的多个版本,并且这些版本之间不发生冲突

  1. 访问虚拟工具:python -m venv 环境名称

  2. 安装虚拟工具:pip install virtualenv

  3. 进入虚拟环境:cd 环境名称\Scripts

  4. 激活虚拟环境:activate

  5. 查看虚拟环境并升级pip与安装模块包:pip list

  6. 退出虚拟环境:deactivate

  7. pyvenv命令可以创建虚拟环境,source bin/activate命令可以激活虚拟环境,deactivate可以停用虚拟环境

  8. pip freeze命令可以把环境依赖汇总到文件里面,再用pip install -r进行重建

Virtualenvwrapper

Virtaulenvwrapper是virtualenv的扩展包,用于更方便管理虚拟环境,它可以做: - 将所有虚拟环境整合在一个目录下 - 管理(新增,删除,复制)虚拟环境 - 快速切换虚拟环境

1. 安装

# on Windows
pip install virtualenvwrapper-win
# on macOS / Linux
pip install --user virtualenvwrapper
# then make Bash load virtualenvwrapper automatically
echo "source virtualenvwrapper.sh" >> ~/.bashrc
source ~/.bashrc

2. 创建虚拟环境

# on macOS/Linux:
mkvirtualenv --python=python3.6 venv
# on Windows
mkvirtualenv --python=python3 venv

3. 激活环境

workon #列出虚拟环境列表
workon [venv] #切换环境

4. 退出环境

deactivate

5. 删除环境

rmvirtualenv venv

6. 其他有用指令

pip freeze #查看当前安装库版本
#创建 requirements.txt 文件,其中包含了当前环境中所有包及 各自的版本的简单列表
#保持部署相同,一键安装所有包
pip install -r requirements.txt
pip freeze > requirements.txt 
lsvirtualenv    #列举所有的环境
cdvirtualenv    #导航到当前激活的虚拟环境的目录中,相当于pushd 目录
cdsitepackages   # 和上面的类似,直接进入到 site-packages 目录
lssitepackages     #显示 site-packages 目录中的内容

版本控制

  • python包有不同版本,如何在一台机器上安装不同版本的包

pyenv

  • 查看支持的python版本:pyenv install – –list
  • 切换python版本:pyenv globle 版本号
  • 删除python版本:pyenv uninstall 版本号

魔术方法

_eq_方法:可以改变=号的功能

_getattr_方法:读取不存在的属性时候确定是否进行调用

_getattribute_方法:只要读取属性就会被调用

  • 使用时候一定要注意不要对属性进行操作,否则会产生无限递归
  • 默认返回值一定是super().__getattribute__()
  • 该方法从实例的属性字典中直接获取属性值
class dictionaryDB(object):
    def __init__(self,data):
        self._data=data
        
    def __getattribute__(self,name):
        data_dict=super().__getattribute__('data')
        return data_dict[name]
    

如果要在__setattr__方法中修改对象的属性,那么需要通过super().__setattr__来完成

  • 通过_getattr__和__setattr_,我们可以用惰性的方式来加载并保存对象的属性。
  • __getattr__在待访问属性缺失时触发,而后者则会在每次访问属性时触发。
  • 如果需要在__getattribute__和__setattr__方法中访问实例属性,那么应该通过super()来做,从而避免无限递归

hasattr():判断对象是否包含对应的属性

_setattr_:写属性就会进行调用

  • 返回值一定是super()._settattr_()

_delattr_:删除对象中某个属性时候进行调用

  • 返回值是super()._delattr_()

_dir_:列出类方法

  • 定义dir重写列出方法结果

_get_:读取类方法时候调用

_delete_:使用_del_方法是否调用该方法

_slots_:一个属性名称的集合,并且只能使用这些属性

  • 通过限制属性的种类,利用白名单制度,让代码更有鲁棒性
  • access这些属性时候速度更快
  • 更加节省内存

_init_subclass_方法:对子类方法进行赋值

_iter_方法:[什么是迭代器 ](https://zhuanlan.zhihu.com/p/319402935)

_call_方法:一个类的实例变成可调用对象

class Animal:
    def _init_(self,name,hobby):
        self.name=name
        self.hobby=hobby
    def _call_(self,food):
        print(f'{animal} is {self.name},like eating {food}')
animal=Animal('cat','play')
animal(fish)
#该语法具体用途在于实例状态不稳定时候使用,更快一点
#可通过callable()判断

__new__方法:元类

在class语句上面放置挂钩,只要class语句处理完毕,这个挂钩就会立即触发

class Mytype(type):
    def __new__(cls,name,bases,dict):
        return type.__new__(cls,name,bases,dict)
    
#假设A是一个对象,由mytype创建
class A(object,metaclass=Mytype):
    pass

A=type("A",(),{})
O=A()

class是建立object的基础,metclass是建立class的基础

根据类创建对象步骤

  • 先执行类的__new__方法,创建对象
  • 再执行类内__init__方法,初始化对象

构造方法是_new_,初始化方法是__init__

对象是基于类创建的,类是默认由type创建的,而元类指定类默认由谁来创建

对象加括号的代码执行时候__call__方法执行

class A():
    pass

a=A()
a()	#此时执行call方法

_doc_:文档

  • 访问代码中的文档,让交互式开发变得更加方便,用内置helo函数查看
  • 为函数编写docstring,介绍重要的类和重要的函数
  • 如果函数没有参数,且仅有一个简单返回值,那么只需要一句话描述函数就够了
  • 如果函数没有返回值就不要在docstring里面提到
  • 如果函数不会抛出异常也不要在docstring提到
  • 如果函数接受动态参数*args和**kargs,那么应当在docstring内描述用途
  • 如果函数参数有默认值,那么应该指出默认值
  • 如果函数是个生成器,那么应该描述迭代的内容
  • 如果函数是个协程,应该描述协程所产生的值,应当使用yield表达式来接纳值,同时说明协程何时停止

_all_:导入

from foo import *
此方法会将__all__内的属性从foo引入,若foo模块没有提供__all__,则只会引入public属性

可以通过_init_.py文件将模块内的接口适当公布给外界

__all__=[]
from .models import *
from .utils import *
__all__+=models.__all__
__all__+=utils.__all__

_repr_:打印

  • 通过repr字符串输出调试信息,我们应该打印repr版本的字符串,字符串打印出来就会成‘string’

  • 可以通过修改类内的__repr__方法,该方法返回打印该类的对象时,该方法返回的值将会被打印出来

  • 把repr字符串传给内置eval函数,就可以将其还原给初始那个值

_dict_:字典

  • 在任意对象上面查询__dict))属性,以观察其内部信息

__init_subclass__

#defining a SuperClass
class SuperClass:
    # defining __init_subclass__ method
def __init_subclass__(cls, **kwargs):
    cls.default_name ="Inherited Class"
    # defining a SubClass
class SubClass(SuperClass):
 # an attribute of SubClass
	default_name ="SubClass" 
	print(default_name)
	
subclass = SubClass()
print(subclass.default_name)

输出

SubClass
Inherited Class
  • 在上面的示例中,有 2 个类(即超类和子类),子类继承自超类。default_name是子类的一个属性。

  • 属性default_name的值由 SuperClass 使用__init_subclass__方法更改。

  • cls是指继承的子类。提供给新类的关键字参数 (**kwargs) 将传递给父类的类__init_subclass__。

  • 为了与使用__init_subclass__的其他子类兼容,应该取出所需的关键字参数,并将其他子类传递给基类(Super Class)。

  • 这个__init_subclass__子类与Decorator类非常相似。但是,如果类装饰符只影响它们应用于的特定类,但是__init_subclass__只应用于定义该方法的类的未来子类。这意味着我们可以改变/定义从超类继承的任何新类的行为。

  • 尽管 __init_subclass__ 是独立于元类编程的,但类都是由默认元类 type 创建的,那么在 type.__new__() 创建了类之后会有哪些步骤:

    • 首先,type.__new__ 收集类命名空间定义的 set_name() 方法的所有描述符;
    • 其次,这些 __set_name__ 的特定描述符在特定的情况下调用;
    • 最后,在父类上调用钩子 __init_subclass__()

    若类被装饰器装饰,那么就将上述生成的对象传递给类装饰器。

    总结

    总的来说,__init_subclass__() 是钩子函数,它解决了如何让父类知道被继承的问题。钩子中能改变类的行为,而不必求助与元类或类装饰器。钩子用起来也更简单且容易理解。
    虽然本文还提到了 __set_name__ ,但它和__init_subclass__ 并不相互关联, __set_name__ 主要是解决了如何让描述符知道其属性的名称。

    __init_subclass__ 的目标是提供更简单的定制方式,在简单的场景下是元类的替代品。值得试一试。

    下面是graphql-python对该方法的使用,值得学习

项目文件结构

采用相对该目录的路径,引用目录中其它python文件

目录结构

  • main.py

  • mypackage/_init_.py

  • mypackage/models.py

  • mypackage/utils.py

相对方法引入utils模块,需要把上级模块绝对名称,写在引用语句from中

from mypackage import utils

如果某个包目录嵌套其它包,那么需要采用import来编写

同样名字的模块可以放置在不同的包内

_init_.py

  • 当一个包被导入时候会自动执行_init_.py文件
  • __all__可以决定本模块中哪些变量被导出,如果__all__放置在__init__.py文件中就可以决定本包中哪些模块被导出
  • 可用于批量导出包

名称空间

如果类名相同,可以在引入时候加上as起上别名,防止名称冲突

from fronted.utils import inspect as fronted_inspect
from analysis.utils import inspect as analysis_inspect

  • import [包名]
    
  • python包是一种包含其它模块的模块,我们可以用包划分成各自独立,互不冲突的名称空间

  • 为模块定义根异常,可以把API的调用者与模块API相隔离

  • 调用者使用API时,可以通过捕获根异常,来发现代码中隐藏的bug

  • 调用者可以通过捕获python的Exception基类,帮助研发者寻找API实现代码中的bug

  • 可以从模块的根异常,继承一些中间异常,并令API调用者捕获中间异常。

  • 只要文件夹中含有_init_.py文件,说明这是一个包

  • 避免循环导入包

模块

  • 任意一个.py文件都是一个模块

  • 最好一个模块只含一个类,这样更加符合面向对象编程

  • from [包\模块\类] import [模块\类\方法\变量]
    from [包] import *	#从包内导入所有模块
    
    import [包].[模块] as [别名]
    
  • 模块被导入时候模块内部文件会执行

项目框架

TODO

  • 将业务逻辑用Xmind理出来
  • 将每个业务功能都用单独文件进行封装
  • 文件内部逻辑使用TODO标注
  • 按照逻辑一步步实现项目功能

项目测试

unittest

from unittest import TestCase,main
class utilstestcase(TestCase):
    def test_to_str_bytes(self):
        self.assertEqual('hello',to_str(b'hello))
  • 如果在测试方法运行过程中,没有抛出异常,也没有因为assert语句导致AssertionError,测试就算顺利通过

  • 要想确信python程序能够正常运行,唯一的办法就是编写测试

  • 我们可以借助内置unittest模块写出良好的测试

  • TestCase子类中,为每一个需要测试的行为定义测试的方法,名称必须以test开头

  • 必须同时编写单元测试和集成测试,前者用来独立检验程序中的每个功能,后者用来检验模块交互行为

pdb

  • 我们可以通过pdb调试器进行交互,通过调试器查看程序的运行状况
  • 我们可以修改python程序,在想要调试的代码上方直接加入import pdb;pdb.set_trace(),以触发互动调试器
  • 我们可以在pdb提示符中输入命令,以便精确控制程序执行流程,这些命令使得我们能够交替查看程序状态并进继续向下运行程序

日志

logging模块

调试时候最好不要使用print()输出信息,因为调试结束后去除print()代码需要花很多时间,如果删错了得不偿失,对于用户希望看到的内容就使用print()功能,不希望用户看到就使用日志功能输出

  • debug():返回调试信息字符

  • disable(logging.CRITICAL):禁止日志,使得日志功能开关很容易

  • 日志级别

    • 级别 方法 描述
      DEBUG debug() 最低级别。用于细节。诊断问题使用
      INFO info() 记录一般事件信息,确认工作正常
      WARNING warning() 表示可能问题,当前无误,将来可能存在隐患
      ERROR error() 记录错误,程序做某事失败
      CRITICAL critical() 最高级别。致命错误,导致程序完全停止工作
    • 回复级别的好处在于改变希望看到的日志消息和优先级

  • basicConfig(filename,level,format):将日志文件写入filename,设置日志级别level=logging.DEBUG,格式format=‘%(astime)s-%(levelname)s-%message)s’

MU调试器

  • Debug按钮:调试程序
  • Continue按钮:继续运行至断点
  • StepIn:执行下一行代码,如果下一行代码是调用则执行调用函数第一行代码
  • StepOver:执行下一行代码,如果下一行代码是调用则完全执行调用函数
  • StepOut:执行下一行代码,如果已经进入函数则将函数完全执行直至退出函数
  • Stop:停止调试

性能优化

  • python提供内置性能分析工具(profilter),它可以计算出程序某个部分执行时间,在总体执行时间所占比例。

  • 优化python程序之前一定先分析其性能,因为python性能瓶颈很难观察出来

  • 性能分析时候,应该使用cprofile模块,而非profile模块,前者比后者更加精确

  • 我们通过profile对象的runcall方法分析程序的性能,该方法能够提供性能分析所需全部信息,它会按照树状函数调用关系,单独统计函数性能

  • 可以用stats对象筛选性能分析数据,并打印我们所需的那一部分。

内存泄漏

  • trcemalloc模块中cycle detector,内存管理是提供引用计数来处理的。
  • python程序内存使用情况和内存泄漏情况帅益舟很难判断的
  • 可提供gc模块进行了解,但是并不能看出如何分配
  • tracemalloc可找出致使内存用量增大根源

命令行工具

sys

  • 获取命令行参数列表

    • 其保存了程序文件名和命令行参数列表
    • argv[num]:获取第num个参数
  • 读取标准输入

    • stdin

    • fileinput

      • filename:读取正在读取文件名
      • fileno:文件描述符
      • filelineno:正在读取行是当前文件第几行
      • iisfirstline:正在读取的行是否读取文件第一行
      • isstdin fileinut:正在读取文件还是自己从标准输入读取内容
    • stderr

      • 获取异常错误信息

argparse

  • ArgumentParser
    • name/flags:参数名字
    • action:遇到参数时动作
    • nargs:参数个数,可以是具体名字,或者是‘+’(1个或多个参数)与‘*’(0个或多个参数)
    • const action:需要的常量值
    • default:不指定参数时的默认值
    • type:参数的类型
    • choices:参数的类型
    • required:可选参数是否可以省略
    • help:参数的帮助信息
    • metavar:在usage说明中的参数名称
    • dest:解析后的参数名称

调试器

pdb

命令 缩写 说明
break b 设置断点
continue cont/c 继续执行
next n 执行下一行,如果下一行是子程序,不进入子程序
step s 执行下一行,如果下一行是子程序,进入子程序
where bt/w 打印堆栈轨迹
enable - 启用禁用的断点
disable - 禁用启用的断点
pp/p - 打印变量或者表达式
list l 根据参数值打印源码
up u 移动到上层堆栈
down d 移动到下层堆栈
restart run 重新调试
args a 打印函数参数
clear cl 清除所有断点
return r 执行到当前函数结束

ipdb

  • pip install ipdb
  • set_trace:设置断点

代码规范

  • PEP8编码
    • 安装:pip install pep8
  • pycodestyle
    • 安装:pip install pycodestyle
    • 检查py文件格式规范:pycodestyle – –first 文件名.py
    • 打印检查报告:– –show –source
  • autopep8
    • 安装:pip install autopep8
    • 格式代码不保存:autopep8 文件名.py
    • 格式化代码并保存:autopep8 – –in–place 文件名.py

cmd用法

  • 切换路径:cd /d 路径

参考文献

本文档参考以下书目:

  • 《Effective Python》
  • 《Python编程快速上手:让繁琐工作自动化》
posted on 2023-08-23 20:50  读研随想录  阅读(23)  评论(0编辑  收藏  举报