lilst/set/元组的实现
序列
序列的abc继承关系
from collections import abc
Sequence 不可变
MutableSequence 不可变
序列的+、+=和extend
实现可切片的对象
切片操作会返回一个新的列表
bisect
import bisect
用来处理已排序的序列
array
import array
# array 在声明时必须指定元素的类型
my_array = array.array('i') # i 代表 signed int
my_array.append(1)
# 正常
my_array.append('abc')
# TypeError: an integer is required (got type str)
列表生成式(列表推导式)
# 提取 1-20 之间的奇数
odd_list = [i for i in range(21) if i % 2 == 1]
print(odd_list)
# [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
列表生成式性能高于列表操作
生成器表达式
odd_gen = (i for i in range(21) if i % 2 == 1)
print(odd_gen)
odd_list = list(odd_gen)
print(odd_list)
'''
<generator object <genexpr> at 0x0000022600C5F6C8>
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
'''
字典推导式
my_dict = {'1': 1, '2': 2,'3': 3}
reversed_dict = {value: key for key, value in my_dict.items()}
print(reversed_dict)
'''
{1: '1', 2: '2', 3: '3'}
'''
集合推导式
with语句
import contextlib
@contextlib.contextmanager
用装饰器将函数变成上下文管理器
mixin
mixin模式特点:
1. mixin类功能单一
2. 不和基类关联,可以与任意基类关联
基类不和mixin关联就能初始化成功
3. mixin中不要使用super
super函数
super().__init__()
super的调用顺序与__mro__顺序是一致的
python对象的自省机制
自省是通过一定的机制查询到对象的内部结构
通过__dict__查询属性
python的dict是用c语言实现的
对象.__dict__
dir(对象)
私有属性与数据封装
self.__xxxxxx
私有属性无法被实例对象直接获取
在继承关系中,子类也无法直接获取到私有属性
私有属性是python在执行过程中对私有属性的名称进行了重命名。
对象._类名.__xxxxxx
类方法、静态方法与实例方法
静态方法不需要传self
静态方法返回类的对象时是写上当前的类名,类方法返回类的对象时是写上cls,cls指向类本身
类属性与实例属性及调用顺序
类的多继承查找顺序
__mro__方法
不同的继承关系按不同顺序查找调用:c3算法
类变量和对象变量(实例变量)
类变量不需要初始化对象就可以访问,即可以直接通过类名访问
类不能访问实例变量
当类变量和实例变量同名时,先访问实例变量,再访问类变量
实例对象修改类变量时,会重新创建一个实例变量。
判断两个类是否属于同一类型
isinstance()可以进行继承关系的判断
type()判断的类与类之间的引用地址是否相等,不判断是否有继承关系
一个类在创建后,是存储在内存中的
鸭子类型
一种动物长得像鸭子,叫起来也像鸭子,那么这个动物就是鸭子
多个类中实现了相同名称的方法
魔法函数
对象
可以使用id()方法返回当前对象的唯一标识
type创建了int等,int等创建了具体的对象
__bases__: 查看基类
python中所有类都继承自object
type也是继承了object
object不继承任何类
object是由type创建的
type是由自身创建的
None也是对象,全局只有一个
迭代器
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
my_list = [1, 2, 3, 4, 5]
iterator = MyIterator(my_list)
for item in iterator:
print(item)
python的内存管理
Python的内存管理主要是自动的,也就是说,开发者不需要像在C或C++等语言中那样手动分配和释放内存。Python的垃圾收集机制负责自动回收不再使用的内存。这让Python开发者可以将更多的精力集中在实现程序功能上,而不是内存管理。
Python的内存管理主要涉及以下几个方面:
- 对象的创建:当在Python中创建一个对象(比如一个列表、字典、类实例等)时,Python运行时会在堆内存中分配一块空间来存储这个对象。这块空间的大小会根据对象的大小动态调整。
- 引用计数:Python使用引用计数来跟踪每个对象被引用的次数。每当一个对象被引用时,它的引用计数就会增加;每当一个引用离开作用域或被设为None时,引用计数就会减少。当引用计数减少到0时,表示没有任何引用指向这个对象,这个对象就会被垃圾收集器回收。
- 垃圾收集:Python的垃圾收集器会定期运行,回收那些引用计数为0的对象所占用的内存。Python使用了一种称为“分代收集”的策略,将内存中的对象分成几个“代”,每次垃圾收集时,只回收一部分代中的对象。这样可以提高垃圾收集的效率,减少因垃圾收集导致的程序暂停时间。
- 内存池:Python还会使用内存池来优化小对象的内存分配。Python会将大量创建和销毁的小对象(比如数字、字符串等)放入内存池中,以减少因频繁分配和回收内存导致的开销。
需要注意的是,虽然Python的自动内存管理机制很方便,但也有可能导致一些内存问题,比如内存泄漏和循环引用。因此,了解Python的内存管理机制对于理解和解决这些问题很有帮助。
Python中的生成器表达式和列表推导式有什么区别?
在Python中,生成器表达式和列表推导式都可以用来生成一个序列。然而,它们之间存在一些关键的区别:
-
内存使用:列表推导式在生成序列时会立即占用内存,特别是当序列非常大时,这可能会导致严重的内存问题。相反,生成器表达式是惰性的,它们只在需要时才生成值,这意味着它们在任何时候都只会占用一小部分内存,不论生成的序列有多大。
-
性能:由于上述的内存使用模式,生成器表达式通常在性能上优于列表推导式,特别是对于大序列。
-
语法:列表推导式使用方括号([]),而生成器表达式使用圆括号(())。例如,以下是它们分别的用法:
- 列表推导式:
[x**2 for x in range(10)]
- 生成器表达式:
(x**2 for x in range(10))
- 列表推导式:
-
可迭代次数:列表推导式生成的是一个完整的列表,可以反复迭代。而生成器表达式在每次迭代时都会生成新的值,所以不能重复迭代。如果你尝试再次迭代已经遍历过的生成器,你会得到一个空序列。
总的来说,如果你需要一次性的、较小的、可以重复迭代的序列,那么列表推导式可能是更好的选择。然而,如果你需要一个大型的、只需遍历一次的序列,那么生成器表达式通常是更好的选择,因为它们可以节省内存并提高性能。
lambda函数
在Python编程语言中,Lambda函数是一种小型匿名函数,可以使用一个语句来表示。Lambda函数是由关键字'lambda'后跟输入参数和冒号':',再接着是表达式或语句块组成的。这种函数的实际功能和命名函数类似,但由于其是匿名的,所以不需要使用'def'关键字进行定义。
Lambda函数主要在你需要一个小函数的地方使用,而你不打算在其他地方再次使用该函数。例如,它们经常用于短小的、临时需要的排序或过滤函数。
下面是一个Lambda函数的简单例子:
f = lambda x: x**2
print(f(5)) # 输出: 25
抽象基类
在Python中,抽象基类(Abstract Base Class,简称ABC)是一种特殊的类,它不能被实例化,只能被继承。抽象基类定义了一组可以被子类实现的接口,这些接口定义了某些行为或功能,但并不提供具体的实现。
Python的abc模块提供了创建抽象基类的工具。通过使用abc模块中的abstractmethod装饰器,可以将一个方法声明为抽象方法,这意味着该方法必须在任何直接或间接的子类中实现。如果子类没有提供这个抽象方法的实现,那么当实例化这个子类时,Python将会引发TypeError。
抽象基类的主要用途是定义接口和实现一些通用的功能。它们可以作为一种约定,确保所有实现特定功能的类都具有一致的接口。这种机制使得代码更加灵活,因为不同的类可以实现相同的接口,从而可以以通用的方式使用这些类。
下面是一个简单的例子来说明抽象基类的概念:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
def perimeter(self):
return 2 * 3.14 * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
在上面的例子中,Shape是一个抽象基类,它定义了两个抽象方法area和perimeter。Circle和Rectangle是Shape的子类,它们都实现了这两个抽象方法。现在,我们可以以通用的方式使用Circle和Rectangle类,例如计算它们的面积和周长,而不需要关心它们的具体实现。
抽象类的作用
1.类型判断
2.对于类进行方法的限制
hasattr
判断类是否有指定的方法
hasattr(company, '__len__')
继承和多态
class Shape:
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
shapes = [Circle(5), Rectangle(4, 6), Circle(3)]
for shape in shapes:
print("Area:", shape.area())
垃圾回收
Python的垃圾回收机制主要使用了引用计数和循环引用检测两种技术。
- 引用计数:Python内部使用引用计数,来保持追踪每一个对象的引用数。当一个对象的引用数变为0时,表示没有任何引用指向该对象,该对象变为不可达,因此它的内存就可以被释放。比如,当一个对象离开它的作用域或者显式地被del命令删除时,Python将减少它的引用计数。一旦这个计数到达0,垃圾回收器就会释放它的内存。
- 循环引用检测:引用计数存在一个问题,那就是它不能处理循环引用的情况。例如,如果两个对象互相引用,即使没有其他引用存在,它们的引用计数也永远不会是0。为了解决这个问题,Python引入了一个循环检测器,该检测器通过定期执行一个循环垃圾回收的算法,找出并清除引用计数无法处理的循环引用。循环检测器是一种更复杂的垃圾回收机制,只有在引用计数器无法处理的情况下才会启动。
另外,Python的垃圾回收机制也可以手动触发。例如,可以使用gc模块的collect方法强制启动垃圾回收。
需要注意的是,垃圾回收机制并不会立即回收内存,有一定的延迟性,这与Python采用的内存管理机制有关。Python采用的是分代回收策略,目的是为了提高垃圾回收的效率。
循环引用:Python 什么是Python中的循环引用|极客教程
is和==
is判断是否是同一对象,地址是否相同。==判断值是否相同。
@staticmethod和@classmethod
在Python编程语言中,@staticmethod和@classmethod都是装饰器,它们用于修改类的方法的行为。@staticmethod将一个方法转变为静态方法,这样的方法可以不依赖于类的实例,也就是说它们可以在没有创建类实例的情况下直接通过类名调用。@classmethod将一个方法转变为类方法,这样的方法必须至少有一个参数,并且第一个参数总是类本身,通常命名为cls。类方法可以通过类本身调用,也可以通过类的任何实例调用。
链接:python中:类中静态方法(@staticmethod),类方法(@classmethod)和实例方法(self)的使用与区别 - 知乎
线程池
新建线程系统需要分配资源,终止线程系统需要回收资源
如果可以重用线程,则可以减去新建/终止的开销
线程池的原理
使用线程池的好处
提升性能,重用了线程资源
适用场景:适合处理突发性大量请求或需要大量线程完成任务,但实际任务处理事件较短
防御功能:能有效避免因为线程创建过多,而导致系统负荷过大相应变慢等问题
线程安全问题以及解决方案
线程安全指某个函数、某个函数库在多线程环境中被调用时,能够正确地处理多个线程的之间的共享变量,使程序功能正确完成
由于线程的执行随时会发生切换,就造成了不可预料的结果,出现线程不安全
全局解释器锁
python是动态类型语言,边解释边执行
gil:python无法利用多核cpu并发执行
global interpreter lock 全局解释器锁
是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行
即便在多核心处理器上,使用gil的解释器也只允许同一时间执行一个线程
为什么有GIL
为了规避并发问题
为了解决多线程之间数据完整性和状态同步问题
简化了python对共享资源的管理
规避GIL带来的限制
在io期间,线程会释放gil,实现cpu和io的并行,因此多线程用于io密集型计算依然可以大幅提升速度
使用multiprocessing
并发编程
多线程 threading
优点:相比进程,更轻量级,占用资源少
缺点:
python的多线程同时只能使用一个cpu。
(1)相比进程:多线程只能并发执行,不能利用多cpu(GIL)
(2)相比协程:启动数目有限制,占用内存资源,有线程切换开销
场景:IO密集型计算
put和get都是阻塞的
多进程 multiprocessing
优点:可以利用多核cpu并行运算
缺点:占用资源最多,可启动数目比线程少
场景:cpu密集型计算
多协程 asyncio
优点:内存开销最小,启动协程数最多
缺点:支持的库有限制,代码实现复杂
场景:io密集型计算,超多任务进行
一个进程中可以启动多个线程
一个线程中可以启动多个协程
cpu密集型
也叫计算密集型,是指io在很短的时间内就可以完成,cpu需要大量的计算和处理,特点是cpu占用率相当高
io密集型
io密集型指的是系统运作大部分的情况是cpu在等io的读写,cpu占用率较低
闭包
函数名存放的是函数的地址
函数名也可以像变量一样赋值给其他变量
一个函数可以作为其他函数的参数
运行结果分别为11和12
调用闭包就相当于调用了内部函数
闭包可以保存外部函数的变量
修改闭包(内部函数)内使用的外部函数变量使用nonlocal关键字来完成。
不定长参数的函数
def func(*args, **kwargs)
装饰器
装饰器的作用:在不改变原有函数的源代码的情况下,给函数增加新的功能。
装饰器的本质是闭包
装饰器就是把一个函数当作参数传递给闭包中的外部函数,同时在闭包的内部函数中使用这个函数,并给他添加新的功能。
装饰有参数的函数:给闭包的内部函数添加被装饰的函数的参数
装饰带有返回值的函数:通过闭包的内部函数返回被装饰的函数的参数
多个装饰器的装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
带有参数的装饰器:带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数
装饰器的外部函数只能有一个参数
带有参数的装饰器:在原有的装饰器基础上再添加一个“外部函数”,这个“外部函数”的作用就是给原来的装饰器传递参数。
__call__方法:一个类实现了__call__方法,这个类创建的对象就是一个可调用对象,可以像调用函数一样进行调用
类装饰器:把要装饰的函数func当作类的__init__的参数,进而作为类的内部成员,在类的__call__函数中调用要装饰的函数。当要装饰的函数被装饰后,这个函数就变成了类的一个对象。
类的私有属性和方法
在python中,私有方法和属性的定义规则是以“__”双下划线开头。
property
把方法当作属性使用,
with和上下文管理器
一个类实现了__enter__和__exit__两个方法,通过该类创建的对象就称之为上下文管理器