Python是一门非常方便的静态语言,使用语法简洁,语言格式更易于让大众理解,在当今的大数据的浪潮下,Python的数据分析,机器学习等等起到了巨大的作用,因此学习Python必不可少。
当然在我看来,对于一些偏于计算机方向的同学,Python不适宜作为一门个人的主要语言,因为相对于C/C++他缺少了很多东西,比如内存,指针这些,这些关乎代码的存储和运行效率,不可或缺!
本博客将持续更新,以进阶和高阶为主,基础部分(列表,字典等等不做概述)。
go for it!
Python基础
函数
关键字:global,nonlocal
global:将变量声明为全局变量
def varible():
global a # 此时a是全局变量
nonlocal:将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)
# 自裁
拷贝
浅拷贝:拷贝了最外层的对象(变量名称),内部的元素只拷贝了一个引用,地址不变,把存放变量的地址值传给被赋值,最后两个变量引用了同一份地址
# the first channel
a = b
# the second channel
import copy
b = copy.copy(a)
深拷贝:外层的对象和内部的元素都拷贝(赋值)了一遍,被赋值的变量开辟了另一块地址用来存放要赋值的变量的值(内容)
import copy
b = copy.deepcopy(a)
五大高级函数
map(映射函数):把函数一次作用在列表中的每个元素上
map(func, object)
# example
def func(x):
return x * 3
lambda(匿名函数):函数名 = lambda 形参:返回值
la = lambda x : x * 3
# x相当于func的形参,x*3是functional的返回值
reduce(累积函数):一次把序列中的元素进行累积;新把对象中的前两个元素取出,计算出一个值然后保存,依次累积
from functools import reduce
reduce(func, object)
# func函数是有两个参数的函数
# example
def func(x, y):
return x + y
zip(拉链函数):用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
zip([iterable, ...])
# example
a = [1,2,3]
b = [4,5,6]
zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
zip(*zipped) # 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]
filter(过滤函数):消除一个序列中不符合规律的元素
fliter(func, object)
# example
stack = [1, 2, 3, 4]
filter(lambad x : x%2 == 0, stack)
异常
异常种类
捕获异常
# 通用格式
try:
# 监测的代码块(可能发生异常)
except:
# 如果发生异常,执行此代码
# 特定异常格式
try:
# 监测的代码块
except ErrorType as e:
# 如果发生异常,执行此代码
print(e)# 表示把异常输出信息放入e变量中
# 万能异常格式
try:
# 监测的代码块
except Exception as e:
# 如果发生异常,执行此代码
print("报错信息是e:", e)
# else:没有异常时执行的代码
#finally:不管有没有异常都会执行的代码
抛出异常
# 关键字:raise
# 执行了raise后面的代码不可执行
raise Exception("xxx")
自定义异常
# 自裁
模块
.py文件,里面定义了一些函数和变量,需要的时候就可以导入这些模块
模块分类:
- 内置模块(标准库)
- 第三方模块(第三方库)
- 自定义模块
导入模块
import package
from father-package import son-package
# 调用自定义模块
# 调用模块内的函数,函数,类:
1. module.func()
2. from ... import ...
# 把模块内的所有内容导入
from ... import *
模块起别名
import test as name
包
定义:包含__init__.py的文件夹
import导入包时,首先执行__init__.py文件的代码(不建议在__init__写太多代码)
init.py
# __init__.py的主要作用:导入这个包内的其他模块
from __init__ import other_module
# 定义一个变量列表__all__,放入要引入的模块,类等等
__all__ = ['module1', 'module2', 'module3']
module1.py
def res():
print("YES")
go.py
from packagename import *
module1.res() # module1是__all__列表里面的内容
递归函数
- 一个函数在内部不调用其他的函数,而是调用他本身
- 必须有一个明确的结束条件
- 每进行更深一层的递归时,问题规模相比上次递归都要有所减少
- 相邻两次重复之间有紧密的联系
- 递归深度有限度,默认为1000
# 斐波那契
# 从第三项开始,每一项等于前两项之和
闭包
在嵌套函数的前提下,内部函数使用了外部函数的变量,外部函数返回了内部函数,则称使用了外部函数变量的内部函数称为闭包
- 函数体嵌套了一个函数
- 内层函数使用了外层函数的变量
- 外层函数的返回值是内层函数的函数名
def outer():
n = 10
def inner():
print(n)
return inner
outer() # 返回是内层函数的地址
ot = outer()
ot() # 调用内部函数
# problem:为什么要写成这种写法:当有参数的时候,方便传参
# such as
ot = outer(m)
ot(n)
装饰器
装饰器本质上是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象
def wrapper(func): # func是形参,但是往里面传入的值是被装饰的函数名
def inner():
print("是否登录:已经登录")
func()
return inner
def test():
print("发表评论:很喜欢~")
# 使用装饰器来装饰函数
wr = wrapper(test)
# 调用装饰器里面的inner函数
wr()
语法糖用法:@装饰器名称
def wrapper(func): # func是形参,但是往里面传入的值是被装饰的函数名
def inner():
print("是否登录:已经登录")
func()
return inner
# 使用语法糖的形式
@wrapper
# 被装饰的函数
def test2():
print("it is nice")
# 直接调用被装饰的函数即可
test2()
当被装饰函数里面有参数时
def outer(fn):
def inner(c, d): # 如果要在被装饰函数里面传参数则在内层函数里面也要传参
fn(c, d)
return inner
@outer
def exam(a, b):
print("和为:", a + b)
exam(1, 2)
被装饰的函数有可变参数
def funa(hanshu):
def inner(*args, **kwargs):
print("开始执行被装饰的函数")
hanshu(*args, **kwargs)
print("执行完成")
return inner
def test(*args, **kwargs):
print(*args)
print(**kwargs)
fa = funa(test)
fa('a', 'b', 'c', 'd', name="app")
# a, b, c, d用元组来接收,name = "app"用字典来接收
多个装饰器:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
# the one
def maketest1(fn):
def inner():
print("a:fn", fn)
return inner
# the two
def maketest2(fn):
def inner2():
print("b:", fn)
return inner2
# the one of adj
@maketest1
def test1():
return "a"
# the two of adj
@maketest1
@maketest2
def test3():
return "阿萨德"
# the three of adj
test1()
test3()
类和对象
类:具有相同属性和功能的一切事物
对象:就是类的具体表现,是面向对象编程的核心
class 类名:
pass
类的三要素:类名,属性(对对象的特征描述),方法(对象具有的行为)
class Hunman:
hair = "brown"
# 看
print(Human.hair)
# 增
Human.name = "liuhao"
# 删
del Human.hair
# 改
Human.hair = "black"
对象:对象名 = 类名()
实例方法:由对象调用,至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self
class Person:
name = "liuhao" # 类属性
def speak(self): # 实例方法
print("liuhao is speak")
# 实例化对象
p1 = Person()
# 调用对象里面的方法
p1.speak()
构造方法
__init__方法:通常用来做属性初始化或赋值操作;类被实例化的时候会自动执行init方法
# the first example
class Test:
def __init__(self):
print("this is a init")
te = Test()
# the second example
class Hero:
def __init__(self, name, hp, at):
self.name = name
self.hp = hp
self.at = at
def move(self):
print(f'{self.name}在移动')
def attack(self):
print(f'{self.name}的生命值是{self.hp},发出了一招青莲剑歌{self.at}')
# 实例化对象
hero1 = Hero("李白", 1000, 2000)
hero1.move()
hero1.attack()
hero2 = Hero("刘浩", 2000, 3000)
hero2.move()
hero2.attack()
类属性,实例属性
1.类属性属于类,实例属性属于对象
2.类属性在内存中只保存一份,实例对象在每个对象中都保存一份
3.类属性,类可以访问到,实例属性也可以访问到;实例属性,类访问不到,实例属性可以访问到
(类属性是公共属性publish,实例属性是私有属性private)
析构方法
删除对象时,解释器默认会调用del方法
class Person:
def __init__(self):
print("this is a init")
def __del__(self):
print("this was destroyed")
p = Person()
print("这是最后一句")
# output
# this is a init
# 这是最后一句
# this was destroyed
封装
- 类本身就是一种封装
- 类中定义私有,只在类的内部使用,外部无法访问(在属性名和方法加上__):一般不能直接调用,需要通过另外一个类中函数来调用(类名.方法/变量),第二种直接使用self.函数名
- 保护成员:_
继承
类间关系,描述一个类从另一个类获取成员信息的类间关系
- 子类具有父类的所有属性和方法
# 格式:class 类名(父类名)
class Father:
def move(self):
print("我是沙雕")
class Son(Father):
def mo(self):
print("yes")
son = Son()
# 子类的实例化对象调用父类的公有函数
son.move()
多态
同一种事物的多种形态
静态方法
类中的函数,不需要实例,可以通过实例化对象调用
@staticmethod
def run():
print("刘浩是小狗")
类方法
针对类对象定义的方法,类方法内可以直接访问类属性,或者调用其他类方法
@classmethod
def run(cls): # cls是类对象
print("刘浩还是小狗")
new方法:类名实例化对象时,python解释器会首先会调用new方法为对象分配空间,然后再执行init初始化对象
'''
在内存总为对象分配空间
返回对象的引用
'''
单例模式
# __init__:其实不是实例化对象调用的第一个方法
# __new__:最先调用的是__new__方法
'''
类名()实例化对象时,python解释器首先会调用new方法为对象分配空间,然后再执行init初始化对象
new方法作用:
1. 在内存中为对象分配空间
2. 返回对象的引用
'''
class Test(object):
def __init__(self):
print('这个是init方法')
# cls代码类对象本身,如__main__.Test()
def __new__(cls, *args, **kwargs):
print(cls)
print('这是new方法')
te = Test()
重写new方法
class Person(object):
# 4. 利用这个实例对象调用init方法
def __init__(self):
# 5. 执行init里面的代码
print('init')
def __new__(cls):
print('new')
# 第一种写法
# 1. 父类名.方法名()
# 2. res是实例对象的引用
res = object.__new___(cls)
# 3. 返回res,返回对象引用
return res
# 第二种写法
# super().方法名()
super().__new__(cls)
p1 = Person()
# output:init, new
'''
new:创建对象
init:初始化对象
'''
单例模式:是一种常见的软件设计模型,该模式的主要目的是确保某一个类只有一个实例存在,当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场
实例化不同的对象,资源分配空间不同
单例模式可以节省内存空间,实例化不同对象会产生不同的内存地址,造成资源浪费
'''
实现单例模式的方法:
1. 通过@classmethod
2. 通过装饰器
3. 通过__new__实现
4. 通过导入模块
'''
# 设计流程
# 定义一个类属性,初始值是None,用于记录仪单例对象的地址
class Exam(object):
# 记录第一个被创建的对象引用
ins = None
def __init__(self):
print('init')
# 重写new方法
def __new__(cls, *args, **kwargs):
if cls.ins = None:
# 调用父类的new方法,为第一个对象分配空间
cls.ins = object.__new__(cls)
return cls.ins
# 单例模式,每一次实例化所创建的实例都是同一个,内存地址都一样
a1 = Exam()
print(a1)
a2 = Exam()
print(a2)
a3 = Exam()
print(a3)
# 通过装饰器实现
def outer(fn):
# 创建一个字典来保存类的实例对象
ins = {}
def inner():
if fn not in ins:
# 创建一个对象保存在字典中
# fn()是实例化的对象
fn()
# fn是类名,fn()是实例化的对象
ins[fn] = fn()
return ins[fn]
return inner
@outer # 装饰器修饰的是Test类
class Test(object):
pass
te = Test()
迭代器和生成器
迭代器
# for循环工作原理
# 在内部对刻碟的对象调用__iter__方法,获取迭代器对象
# 再一次次的通过迭代器对象调用__next__方法获得迭代结果
# 判断对象是否可迭代:isinstance()
for collections.abc import Iterable
isinstance(对象, Iterable) # 返回布尔值
迭代器有两个函数:iter()和next()
iter():得到一个可迭代对象
next():获取迭代结果
生成器:python中,使用了yield的函数被称为生成器
# 原来
for i in range(30):
print(i*2)
# 生成器,得到的结果是一个迭代器,可通过next得出
'''
1. yield语句一次返回一个结果,在每个结果中间,挂起函数,以便下次从他离开的地方继续执行
2. yield效果使函数中断,并保存中断的状态
'''
li2 = [i*2 for i in range(30)]
线程
线程是CPU调度的基本单位,每个进程至少都有一个线程,这个线程通常就是主线程。
程序启动默认会有一个主线程,自己创建的线程称为子线程。
一个进程默认有一个线程, 线程里面可以创建多个线程,线程是依附在进程里面的,没有进程没有线程。
单线程
import time
def func1():
print('a')
time.sleep(1)
print('b')
def func2():
print('c')
time.sleep(1)
print('d')
if __name__ == '__main__':
...
模块
import threading
# 开始线程
start()
# 创建子线程
Thread()
参数
'''
target:执行的任务名
args:以元组的形式给执行任务传参
kwargs:以字典的形式给执行任务传参
'''
守护线程:主线程执行完了,子线程也会跟着结束。
# t1表示的是子线程,规定放在开启线程的前面
t1.setDaemon(True)
t1.start()
阻塞主线程:暂停的作用,等添加了join的子线程执行完,主线程才继续执行。
t1.start()
# t1表示的是子线程,规定放在开启线程的后面
t1.join()
获取线程名字
t1.getName()
修改线程名字
t1.setName("啊啊啊!")
显示当前线程的对象名
threading.current_thread().name
线程执行代码的封装:定义一个线程类
from threading import Thread
import time
class MyThread(Thread):
# 重构一个run方法,表示线程活动的方法
def run(self):
print('面向对象')
time.sleep(2)
print('线程')
if __name__ == '__main__':
# 创建一个线程实例
my = MyThread()
# 启动线程,默认调用run方法
my.start()
多线程资源竞争、线程同步
from threading import Thread
import time
# 1.资源共享
# 2.线程同步的方式:继续等待(join)、互斥锁
# 同步:有两个线程,线程A写入,线程B读取线程A的值。主线程和子线程之间各自执行完自己的代码直到结束
互斥锁:保证多个县访问共享数据不会出现数据错误问题,保证同一时刻只能有一个线程去操作
from threading import Lock
# 加锁
acquire()
# 解锁
release()
# 创建一个全局互斥锁
lock = Lock()
def jia1():
lock.acquire() # 加锁
for i in range(1):
global a
a += 1
print('a的值为:', a)
lock.release()
def jia2():
lock.acquire() # 加锁
for i in range(b):
global a
a += 1
print('a的值为:', a)
lock.release()
if __name__ == '__main__':
one = Thread(target=jia1)
two = Thread(target=jia2)
one.start()
two.start()
'''
使用互斥锁使得多任务变成了单任务,影响了效率
互斥锁如果没有使用好会出现死锁
'''
执行的任务有参数
def func(a):
print('a的值为:', a)
time.sleep(2)
print('b')
def func2(b):
print('b的值为:', b)
time.sleep(2)
print('c')
if __name__ == '__main__':
f1 = Thread(target=func, args=('面向对象', ))
f2 = Thread(target=func2, args=('面向过程', ))
f1.start()
f2.start()
进程
运行一个程序就会有一个进程
打开一个程序至少就会有一个进程,他是操作系统进行资源分配的基本单元
进程的状态:就绪态(正在等CPU执行),执行态(CPU正在执行此功能),等待态(等待某些条件满足)
基本状态
print("a") # 程序开始,运行状态
name = input('b') # 用户输入,进行阻塞,等待态
进程语法结构
from multiprocessing import Process
# 创建一个子进程对象
'''
参数
target:调用对象,子进行要执行的任务
args:以元组的形式传值
kwargs:以字典的形式传值
方法
start():开启子进程
is_alive():判断子进程是否还活着,存货返回True,否则返回False
join():主进程等等紫禁城执行完
属性
name():当前进程的别名
pid():当前进行的进程号
'''
if __name__ == '__main__':
p1 = Process(target=one, name='小白')
p1.start()
p2 = Process(target=two)
p2.name = '小刘'
print(p1.name)
print(p2.name)
查看各个进程的控制台命令
# cmd
cd /d C:\Windows\System32
tasklist
# 查看当前进程
import os
os.getpid()
# 查看当前进程父进程
import os
os.getppid()
进程间不共享全局变量
li = []
def wdata():
print(li)
def rdata():
li.append(1)
print(li)
if __name__ == '__main__':
f1 = Process(target=wdata)
f2 = Process(target=rdata)
f1.start()
f1.join()
f2.start()
'''
输出:
f1:[]
f2:[1]
'''
进程间的通信(队列):资源传输
队列queue:实现多进程之间的数据传递,queue本身是一个消息队列程序
from queue import Queue
q = Queue(n) # n表示最多可接受三条消息
q.put() # 放入数据
q.get() # 取出数据
q.empty() # 判断队列是否为空
q.qsize() # 判断队列的长度
q.full() # 判断队列是否为满
# 进程操作队列
from multiprocessing import Queue
while True:
if q.empty():
break
else:
print(q.get())
进程池:当需要创建的进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是成百上千个目标,可以用multiprocessing模块提供的Pool方法。
'''
方法
p.apply_async():异步并阻塞,不用等待当前进程执行,随时根据系统调度来进行进程切换,如果用异步提交任务,等进程池处理完,用get()收集结果
p.close():关闭进程池
p.join():主进程阻塞,等待所有工作进程退出,只能在close()后调用
'''
from multiprocessing import Pool
import time
def work(a):
print("a")
time.sleep(2)
return a*3
if __name__ == '__main__':
# 定义一个进程池,最大进程数为3
li = []
p = Pool(3)
for i in range(6):
# 异步运行,每次最多三个子进程在异步执行
res = p.apply_async(work, args=(i, ))
li.append(res)
# 关闭进程池
p.close()
# 主进程阻塞,等待所有工作进程退出,只能在close()后调用
p.join()
# 使用get来获取apply_async()的结果
for i in li:
print(i.get())
协程
微线程,英文名coroutine,单线程下的并发。
模块
import greenlet
import gevent
# simple example
'''
在进行多个任务时,在第一个任务进行到一半的时候需要进行第二个任务,第二个任务进行到一半的时候要进行第三个任务等待 这种情况下,此简单例子中用yield表示 暂停运行
'''
import time
def work1():
while True:
yield 'a'
yield 'c'
time.sleep(1)
def work2():
while True:
yield 'b'
yield 'd'
time.sleep(1)
if __name__ == '__main__':
w1 = work1()
w2 = work2()
while True:
print(next(w1))
print(next(w2))
time.sleep(1)
使用场景
如果一个线程里面io操作(网络等待,文件操作)比较多,协程就比较适用,适合高并发处理
'''查看模块'''
pip list
'''安装模块'''
pip install module
'''写在模块'''
pip uninstall module
IO操作:input、output,常见的有文件操作和网络等待
greenlet模块
'''
用greenlet实现任务(手动)切换
使用的是一个c的switch()切换的模块
'''
from greenlet import greenlet
def eat():
print('eat 1')
print('e')
def study():
print('learn 1')
print('ok')
# 实例化一个协程对象 greenlet(任务名)
g1 = greenlet(eat)
g2 = greenlet(study)
g1.switch() # 切换到个g1中运行
g2.switch() # 切换到个g2中运行
gevent模块
'''
gevent实现任务(自动)切换
在gevent中用到的主要模式还是greenlet
'''
import gevent
# 创建协程对象
# gevent.spawn('function', 参数)
# join:阻塞,等待某个协程执行完毕
# joinall:参数是一个协程对象列表,会等待所有的协程都执行完毕再退出
# 注意:py文件不要和第三方模块、内置模块重名
# 如果没有遇到可以识别的IO操作,不会进行任务切换
def write():
print('a')
# 模拟的是gevent可以识别的IO操作
gevent.sleep(1)
print('b')
def listen():
print('c')
gevent.sleep(1)
print('d')
# 实例化一个协程对象
g1 = gevent.spawn(write)
g2 = gevent.spawn(listen)
g1.join() # 等待g1对象执行结束
g2.join() # 等待g2对象执行结束
def work(name):
for i in range(3):
gevent.sleep(1)
print(f'aa{name}, i的值为{i}')
gevent.joinall([
gevent.spawn(work, 'a'),
gevent.spawn(work, 'b')
])
# 以下两个相等
'''
1.
gevent.sleep(1)
2.
monkey.patch_all()
time.sleep(1)
'''
'''
1. 进程是资源分配的单元,线程是CPU调度的基本单元
2.
进程:切换需要的资源最大,效率最低
线程:切换需要的资源一般,效率一般
协程:切换需要的资源很小,效率最高
3.
多线程适合IO密集型操作(读写数据操作多,比如爬虫)
多进程适合CPU密集型操作(科学计算,计算圆周率,对视频进行高清解码)
'''
异步协程
正则
import re
result = re.match(正则表达式,要匹配的字符串)
# match从字符串的开始位置进行匹配,返回match对象,匹配不到返回None
result # 返回True或者None
result.group() # 返回固定的值
# group()用于返回分组的值
import re
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0) # 123abc456,返回整体
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1) # 123
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2) # abc
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3) # 456
匹配单个字符match
字符 | 功能 |
---|---|
. | 匹配任意一个字符(除了\n) |
[] | 匹配[]中的字符 |
\d或者[0-9] | 匹配数字 |
\D | 匹配非数字 |
\s | 匹配空白,即空格和tab |
\S | 匹配非空白 |
\w | 匹配单词字符,a-z,A-Z,0-9,_汉字 |
\W | 匹配非单词字符 |
匹配多个字符
字符 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现0次或者无1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,n} | 匹配前一个字符出现m到n次 |
匹配开头结尾
字符 | 功能 |
---|---|
^ | 匹配字符串开头,表示以什么开头;表示对什么取反 |
$ | 匹配字符串结尾 |
# 表示匹配不是ab的字符
res = re.match('[^ab]', 'abcd')
# 表示匹配ab
res = re.match('^ab', 'abcd')
res = re.match('.{2}e$', 'jie')
'''
表示对于.{2}匹配到的字符是否以e结尾
'''