前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python核心知识汇总(精编版)

python核心知识汇总(精编版)

作者头像
才浅Coding攻略
发布2022-12-12 17:01:56
1.4K0
发布2022-12-12 17:01:56
举报
文章被收录于专栏:才浅coding攻略

阿巩:

坚决不咕咕咕!!

你好啊,我是阿巩。转眼已连续更新一周了,可咱毕竟是讲Python的公众号,不来点Python基础干货就有些说不过去,就像茶馆里没有茶、犬舍里没有狗子、老婆饼里没有老婆(都什么乱七八糟的比喻?!)之前有写过篇万字长文,今天来根据面试常问的内容整理下,做个精编版。日拱一卒,让我们开始吧!

Python3标准数据类型:

  • 数字
  • 字符串
  • 列表
  • 元组
  • 集合
  • 字典

其中不可变类型:Number(数字)String(字符串)、Tuple(元组);

可变类型:List(列表)、Dictionary(字典)、Set(集合)。

可变/不可变对象

不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。

可变对象,该对象所指向的内存中的值可以被改变。当引用改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,即原地改变。

字符串

Python的字符串支持索引、切片和遍历等操作。

Python的字符串不可变,要想改变,只能通过创建新的字符串完成。

实现拼接字符串用str1+= str2即可。

常用函数:

  • string.split(separator),把字符串按照 separator 分割成子字符串,并返回一个分割后子字符串组合的列表;
  • string.strip(str),去掉首尾的 str 字符串;
  • string.lstrip(str),只去掉开头的 str 字符串;
  • string.rstrip(str),只去掉尾部的 str 字符串。

列表和元组

列表和元组,都是一个可以放置任意数据类型的有序集合。其中列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素;而元组是静态的,长度大小固定,无法增加删减或者改变。

list和tuple的内部实现都是array的形式,list因为可变,所以是一个over-allocate的array,tuple因为不可变,所以长度大小固定。

  • Python 中的列表和元组都支持负数索引,-1 表示最后一个元素,-2 表示倒数第二个元素,以此类推。
代码语言:javascript
复制
l = [1, 2, 3, 4]
l[-1]
4

tup = (1, 2, 3, 4)
tup[-1]
4
  • 列表和元组都支持切片操作
代码语言:javascript
复制
l = [1, 2, 3, 4]
l[1:3] # 返回列表中索引从1到2的子列表
[2, 3]

tup = (1, 2, 3, 4)
tup[1:3] # 返回元组中索引从1到2的子元组
(2, 3)
  • 列表和元组都可以随意嵌套
代码语言:javascript
复制
l = [[1, 2, 3], [4, 5]] # 列表的每一个元素也是一个列表

tup = ((1, 2, 3), (4, 5, 6)) # 元组的每一个元素也是一个元组
  • 两者也可以通过 list() 和 tuple() 函数相互转换
代码语言:javascript
复制
list((1, 2, 3))
[1, 2, 3]

tuple([1, 2, 3])
(1, 2, 3)

列表和元组常用的内置函数:

  • count(item) 表示统计列表 / 元组中 item 出现的次数。
  • index(item) 表示返回列表 / 元组中 item 第一次出现的索引。
  • list.reverse() 和 list.sort() 分别表示原地倒转列表和排序(注意,元组没有内置的这两个函数)。
  • reversed() 和 sorted() 同样表示对列表 / 元组进行倒转和排序,reversed() 返回一个倒转后的迭代器;sorted() 返回排好序的新列表。

字典和集合

字典是一系列由键(key)和值(value)配对组成的元素的集合。相比于列表和元组,字典的性能更优,特别是对于查找、添加和删除操作,字典都能在O(1)时间复杂度内完成。字典和集合的内部结构都是一张哈希表。

  • 创建:无论是键还是值,都可以是混合类型。
  • 查询:字典可以直接索引键,也可以使用 get(key, default) 函数来进行索引;集合并不支持索引操作,因为集合本质上是一个哈希表,和列表不一样。要判断一个元素在不在字典或集合内,可以用 value in dict/set 来判断。
  • 更新:字典增加、更新时指定键和对应的值对即可,删除可用pop() 操作;集合增加可用add()函数,删除可用remove()函数。
  • 排序:字典可使用函数sorted()并且指定键或值,进行升序或降序排序;集合排序直接调用 sorted(set) 即可。

合并字典

代码语言:javascript
复制
# 两个字典
dict1 = {'a': 10, 'b': 8}
dict2 = {'d': 6, 'c': 4}
# 方法一:
dict1.update(dict2)
print(dict1)
# 方法二:
dic = {**dict1, **dict2}
print(dic)
# 输出:{'a': 10, 'b': 8, 'd': 6, 'c': 4}

遍历字典

代码语言:javascript
复制
d = {'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'}
for k in d: # 遍历字典的键
    print(k)
# name
# dob
# gender

for v in d.values(): # 遍历字典的值
    print(v)
# jason
# 2000-01-01
# male    

for k, v in d.items(): # 遍历字典的键值对
    print('key: {}, value: {}'.format(k, v))
# key: name, value: jason
# key: dob, value: 2000-01-01
# key: gender, value: male

按键/值对字典排序

代码语言:javascript
复制
d1 = {3: 'three', 1: 'one', 2: 'two'}
# 按键排序
print(sorted(d1.items(), key=lambda k: k[0]))
# 输出:[(1, 'one'), (2, 'two'), (3, 'three')]

d2 = {'three': 3, 'one': 1, 'two': 2}
# 按值排序
print(sorted(d2.items(), key=lambda k: k[1]))
# 输出:[('one', 1), ('two', 2), ('three', 3)]

有序字典

collections模块的OrderedDict,所谓有序而非字典序,指的是元素的插入顺序。


Python3 数据结构

单链表节点

代码语言:javascript
复制
class SingleNode(object):
    """单链表的结点"""
    def __init__(self, item):
        #  item存放数据元素
        self.item = item
        #  next是下一个节点的标识
        self.next = None

双向链表节点

代码语言:javascript
复制
class Node(object):
    """双向链表节点"""
    def __init__(self, item):
        self.item = item
        self.next = None
        self.prev = None

将列表当作栈:列表的append()和pop()方法

代码语言:javascript
复制
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]

将列表当作队列:deque的append()和popleft()方法

代码语言:javascript
复制
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry")           
>>> queue.append("Graham")          
>>> queue.popleft()                 
'Eric'
>>> queue.popleft()                
'John'
>>> queue                           
deque(['Michael', 'Terry', 'Graham'])

列表推导式

提供了一种简便方法创建列表。

代码语言:javascript
复制
[i for i in range(10) if i % 2 == 0]
代码语言:javascript
复制
s = [x*y for x in range(1, 5) if x > 2 for y in range(1, 4) if y < 3]
print(s)
# 输出 [3, 6, 4, 8]
# 等同于:
for x in range(1, 5):
    if x > 2:
        for y in range(1, 4):
            if y < 3:
                x*y

输入和输出

Python3中input()函数接受任意标准输入数据,返回String类型。

格式化输出可使用f‘{}’来代替.format()

代码语言:javascript
复制
a = 1
b = 2
s = a + b
print(f'Sum of a and b is {s}')
# 输出:Sum of a and b is 3

文件输入和输出

  1. 用 open() 函数拿到文件的指针,其中第一个参数指定文件位置;第二个参数,如果是 'r'表示读取,如果是'w' 则表示写入,当然也可以用 'rw' ,表示读写都要。'a' 表示追加(append)即从原始文件的最末尾开始写入。
  2. 拿到指针后,通过 read() 函数,来读取文件的全部内容。
  3. 给 read 指定参数 size ,表示读取的最大长度。还可以通过 readline() 函数,每次读取一行,如果每行之间没有关联,这种做法也可以降低内存的压力。
  4. write() 函数,可以把参数中的字符串输出到文件中。

注意所有 I/O 都应该进行错误处理。

with open

with语句相当于在open加上try-except-finally,用with语句的好处就是到达语句末尾时会自动关闭文件。with语句实际上是一个非常通用的结构,允许你使用所谓的上下文管理器。上下文管理器是支持两个方法的对象:__enter__和 __exit__。

JSON序列化

JSON是一种轻量级的数据交换格式,它的设计意图是把所有事情都用设计的字符串来表示。实际应用中遇到多种数据类型混在一起的情况可使用JSON序列化处理:

  • json.dumps() 函数,接受 Python 的基本数据类型,然后将其序列化为 string。
  • json.loads() 函数,接受一个合法字符串,然后将其反序列化为 Python 的基本数据类型。

当开发一个第三方应用程序时,可以通过 JSON 将用户的个人配置输出到文件,方便下次程序启动时自动读取。这也是现在普遍运用的成熟做法。


条件与循环

Python 不支持 switch 语句,因此,当存在多个条件判断时,我们需要用elif实现。

for循环

Python 中的数据结构只要是可迭代的比如列表、集合等等,那么都可以通过下面这种方式遍历:

代码语言:javascript
复制
for item in <iterable>:
    ...

使用range() 函数,拿到索引,再去遍历访问集合中的元素。

代码语言:javascript
复制
l = [1, 2, 3, 4, 5, 6, 7]
for index in range(0, len(l)):
    if index < 5:
        print(l[index])        

当同时需要索引和元素时,使用Python 内置的函数 enumerate()。

代码语言:javascript
复制
l = [1, 2, 3, 4, 5, 6, 7]
for index, item in enumerate(l):
    if index < 5:
        print(item)  

while循环

代码语言:javascript
复制
l = [1, 2, 3, 4]
index = 0
while index < len(l):
    print(l[index])
    index += 1

异常处理

try:后放会出现异常的代码;except后方要捕获的异常,捕获所有异常用Exception;as后为别名;finally后为无论如何都会执行的代码。

代码语言:javascript
复制
def fun():
    try:
        print('try--start')
        a = 1/0
    except ValueError as ret:
        print(ret)
    finally:
        return 'finally'
print(fun())
# 输出:
try--start
finally

捕获多个异常

代码语言:javascript
复制
def model_exception(x, y):
    try:
        a = x / y
    except(ZeroDivisionError, NameError, TypeError):
        print('one of ZeroDivisionError or NameError or TypeError happend')
# 调用函数结果
model_exception(2, 0)

函数

代码语言:javascript
复制
def name(param1, param2, ..., paramN):
    statements
    return/yield value # optional

python支持函数嵌套

代码语言:javascript
复制
def connect_DB():
    def get_DB_configuration():
        ...
        return host, username, password
    conn = connector.connect(get_DB_configuration())
    return conn

我们只能通过调用外部函数 connect_DB() 来访问get_DB_configuration()

闭包

和嵌套函数类似,不同在于外部函数返回的是一个函数,而不是一个具体的值。返回的函数通常赋予一个变量,这个变量可以在后面被继续执行调用。

不定长参数

当参数个数不确定时使用不定长参数,有两种类型分别为*args和**kwargs。

加了*的参数会以元组tuple的形式导入,而**的参数以字典形式导入。

匿名函数

python使用lambda来创建匿名函数。

代码语言:javascript
复制
sum = lambda arg1, arg2: arg1 + arg2

函数式编程

map() 、filter()、reduce()

map()函数:对iterable中的每个对象都运行func函数,最后返回一个新的可遍历的集合。

代码语言:javascript
复制
l = [1, 2, 3, 4, 5]
new_list = map(lambda x: x * 2, l) # [2, 4, 6, 8, 10]

filter()函数:对 iterable 中的每个元素,都使用 func 判断并返回 True 或者 False,最后将返回 True 的元素组成一个新的可遍历的集合。

代码语言:javascript
复制
l = [1, 2, 3, 4, 5]
new_list = filter(lambda x: x % 2 == 0, l) # [2, 4]

reduce()函数:它通常用来对一个集合做一些累积操作。比如要计算某个列表元素的乘积。

代码语言:javascript
复制
l = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, l) # 1*2*3*4*5 = 120

迭代器和生成器

在 Python 中一切皆对象,对象的抽象就是类,而对象的集合就是容器。列表(list: [0, 1, 2]),元组(tuple: (0, 1, 2)),字典(dict: {0:0, 1:1, 2:2}),集合(set: set([0, 1, 2]))都是容器。对于容器,你可以很直观地想象成多个元素在一起的单元;而不同容器的区别,正是在于内部数据结构的实现方法。

容器是可迭代对象,可迭代对象调用 iter() 函数,可以得到一个迭代器。迭代器可以通过 next() 函数来得到下一个元素,从而支持遍历。调用next()方法后,你要么得到这个容器的下一个对象,要么得到一个 StopIteration 的错误。

代码语言:javascript
复制
class MyNumbers:
    def __iter__(self):
        self.a = 1
        return self

    def __next__(self):
        x = self.a
        self.a += 1
        return x  

myclass = MyNumbers()
myiter = iter(myclass)

生成器是一种特殊的迭代器,它包含yield。每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行(yield就有点像断点)。

可以使用yield来读取文件,如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用,而yield利用固定长度的缓冲区来不断读取文件内容。

代码语言:javascript
复制
def read_file(fpath): 
    BLOCK_SIZE = 1024 
    with open(fpath, 'rb') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return

python中的参数传递方式

引用传递,不是指向一个具体的内存地址,而是指向一个具体的对象。

  • 如果对象是可变的,当其改变时,所有指向这个对象的变量都会改变。
  • 如果对象不可变,简单的赋值只能改变其中一个变量的值,其余变量则不受影响。

通过一个函数来改变某个变量的值,通常有两种方法:第一种直接将可变数据类型(比如列表,字典,集合)当作参数传入,直接在其上修改;第二种是创建一个新变量,来保存修改后的值,然后将其返回给原变量。在实际工作中,我们更倾向于使用后者,因为其表达清晰明了,不易出错。

python变量及其赋值

  • 变量的赋值,只是表示让变量指向了某个对象,并不表示拷贝对象给变量;而一个对象,可以被多个变量所指向。
  • 可变对象(列表,字典,集合等等)的改变,会影响所有指向该对象的变量。
  • 对于不可变对象(字符串、整型、元组等等),所有指向该对象的变量的值总是一样的,也不会改变。但是通过某些操作(+= 等等)更新不可变对象的值时,会返回一个新的对象。
  • 变量可以被删除,但是对象无法被删除。

面向对象编程

四要素:封装、继承、多态、抽象。

  • 封装就是把功能封装抽象的方法和其他属性和方法,使得代码更加模块化,代码复用度更高;
  • 继承使得子类不仅拥有自己的属性和方法,还能使用父类的属性和方法;
  • 多态可以实现函数重写,使得相同方法具有不同功能。
  • 抽象不同子类的相同方法和属性形成父类,在通过继承,多态,封装使得代码更加紧凑,简洁易读

封装是基础。抽象和多态依赖于继承实现。

构造函数:用def __init__(self, args...)声明,第一个参数self代表当前对象的引用,其他参数是在对象化时需要传入的属性值;构造函数在一个对象生成时(即实例化时)会被自动调用。

__init__用来初始化,__new__用来生成一个实例。

成员函数:是正常的类的函数,第一个参数必须是self;可通过此函数来实现查询或修改类的属性等功能。

静态函数:属于当前类的命名空间下,且对第一个参数没有要求;一般用来做一些简单独立的任务,既方便测试也能优化代码结构;一般使用装饰器@staticmethod来声明。

类函数:类函数的第一个参数一般为cls,表示必须传一个类进来;最常用的功能是实现不同的init构造函数;需要装饰器@classmethod来声明。

元类:MetaClass是创建类的类,元类允许我们控制类的生成,比如修改类的属性等。

元类最常见的场景是ORM中。

装饰器

在不修改原函数的情况下,为函数增加功能。

编写一个记录耗时操作的装饰器:

代码语言:javascript
复制
# 用函数编写一个装饰器
import time
def log_time(func):  # 接收一个函数作为参数
def _log(*args, **kwargs):
        beg = time.time()
        res = func(*args, **kwargs)
        print(f'use time: {time.time() - beg}')
        return res
    return _log
@log_time  # @ 为装饰器的语法糖
def mysleep():
    time.sleep(1)
# mysleep()
# 等价于:
newsleep = log_time(mysleep)
# newsleep()
# 用类编写一个装饰器
class LogTime:
def __call__(self, func):
def _log(*args, **kwargs):
            beg = time.time()
            res = func(*args, **kwargs)
            print(f'use time: {time.time() - beg}')
            return res
return _log
@LogTime()  # 初始化装饰器类的实例,所以此处添加‘()’
def mysleep2():
    time.sleep(1)
# mysleep2()
# 给装饰器增加参数
# 使用类装饰器方便实现装饰器参数
class LogTimeParams:
def __init__(self, use_int=False):
        self.use_int = use_int
def __call__(self, func):
def _log(*args, **kwargs):
            beg = time.time()
            res = func(*args, **kwargs)
if self.use_int:
                print(f'use time: {int(time.time()-beg)}')
else:
                print(f'use time: {time.time()-beg}')
return res
        return _log
        
@LogTimeParams(True)
def mysleep():
    time.sleep(1)
mysleep()

类装饰器主要依赖于函数__call__(),每当调用一个类的示例时,函数__call__()就会被执行一次。

LRU cache缓存装饰器,在 Python 中的表示形式是@lru_cache。@lru_cache会缓存进程中的函数参数和结果,当缓存满了以后,会删除 least recenly used 的数据

编写一个用于身份认证的装饰器:

代码语言:javascript
复制
import functools
def authenticate(func):
    @functools.wraps(func)
def wrapper(*args, **kwargs):
        request = args[0]
if check_user_logged_in(request): # 如果用户处于登录状态
return func(*args, **kwargs) # 执行函数post_comment() 
else:
            raise Exception('Authentication failed')
return wrapper
@authenticate
def post_comment(request, ...)
    ...

设计模式

工厂模式:

代码语言:javascript
复制
"""
什么是工厂模式?
    解决对象创建问题
    解耦对象的创建和使用
    包括工厂方法和抽象工厂
"""
# 工厂模式例子
class DogToy:
    def speak(self):
        print('wang wang')

class CatToy:
    def speak(self):
        print('miao miao')

def toy_factory(toy_type):
    if toy_type == 'dog':
        return DogToy()
    if toy_type == 'cat':
        return CatToy()

单例模式:

代码语言:javascript
复制
"""
什么是单例模式?
    一个类只能创建同一个实例,无论创建多少个实例,都是同一个对象
    Python的模块其实就是单例的,只会import一次,在模块中定义的全局变量就是单例的
    使用共享同一个实例的方式创建单例模式    
"""
class Singleton:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            _instance = super().__new__(cls, *args, **kwargs)
            cls._instance = _instance
        return cls._instance

class Myclass(Singleton):
    pass

c1 = Myclass()
c2 = Myclass()
assert c1 is c2  # c1和c2是同一个实例

Python对象的比较和拷贝

'==' 与 'is'

'=='操作符比较对象之间的值是否相等。执行a == b相当于是去执行a.__eq__(b),而 Python 大部分的数据类型都会去重载__eq__这个函数,其内部的处理通常会复杂一些。

比较操作符'is'效率优于'==',因为'is'操作符无法被重载,执行'is'操作只是简单的获取对象的 ID,并进行比较;而'=='操作符则会递归地遍历对象的所有值,并逐一比较。

浅拷贝和深拷贝

浅拷贝,是指重新分配一块内存,创建新对象,其内容非原对象本身的引用,而是原对象内第一层对象的引用。浅拷贝有三种形式:切片操作、工厂函数、copy 模块中的 copy 函数。

深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。深拷贝只有一种形式,copy 模块中的 deepcopy()函数。深拷贝和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因此,它的时间和空间开销要高。

对于元组,使用 tuple() 或者切片操作符':'不会创建一份浅拷贝,相反,它会返回一个指向相同元组的引用。

拷贝注意点

对于非容器类型,如数字、字符,以及其他的“原子”类型,没有拷贝一说,产生的都是原对象的引用。

如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝。


Python协程

协程是实现并发编程的一种方式,是用户态的线程,由用户决定在什么地方交出控制权,切换到下一个任务。以下为使用协程写异步爬虫程序:

代码语言:javascript
复制
import asyncio

async def crawl_page(url):
    print('crawling {}'.format(url))
    sleep_time = int(url.split('_')[-1])
    await asyncio.sleep(sleep_time)
    print('OK {}'.format(url))

async def main(urls):
    tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
    for task in tasks:
        await task

%time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 输出 ##########

crawling url_1
crawling url_2
crawling url_3
crawling url_4
OK url_1
OK url_2
OK url_3
OK url_4
Wall time: 3.99 s

协程执行的三步:

  1. 通过await来调用。
  2. 通过asyncio.create_task()来创建任务。
  3. 使用asynic.run来触发运行。

并发和并行

并发,通过线程和任务之间互相切换的方式实现,但同一时刻,只允许有一个线程或任务执行。通常应用于 I/O 操作频繁的场景,比如从网站上下载多个文件,I/O 操作的时间可能会比 CPU 运行处理的时间长得多。

并行,则是指多个进程同时执行。更多应用于 CPU heavy 的场景,比如 MapReduce 中的并行计算,为了加快运行速度,一般会用多台机器、多个处理器来完成。

单线程与多线程性能比较

代码语言:javascript
复制
#单线程实现方式
def download_all(sites):
    for site in sites:
        download_one(site)

#多线程实现方式
def download_all(sites):    
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:                    
        executor.map(download_one, sites)

这里创建了一个线程池,总共有 5 个线程可以分配使用。executer.map() 与前面所讲的 Python 内置的 map() 函数类似,表示对 sites 中的每一个元素,并发地调用函数 download_one()。


并发编程之Asycio

Sync(同步)和Async(异步)

Sync同步,是指操作一个接一个地执行,下一个操作必须等上一个操作完成后才能执行。

Async异步,是指不同操作间可以相互交替执行,如果其中的某个操作被 block 了,程序并不会等待,而是会找出可执行的操作继续执行。

Asyncio 工作原理

Asyncio 是单线程的,但其内部 event loop 的机制,可以让它并发地运行多个不同的任务,并且比多线程享有更大的自主控制权。

假设任务只有两个状态:一是预备状态;二是等待状态。event loop 会维护两个任务列表,分别对应这两种状态;并且选取预备状态的一个任务,使其运行,一直到这个任务把控制权交还给 event loop 为止。当任务把控制权交还给 event loop 时,event loop 会根据其是否完成,把任务放到预备或等待状态的列表,然后遍历等待状态列表的任务,查看他们是否完成。如果完成,则将其放到预备状态的列表;如果未完成,则继续放在等待状态的列表。周而复始,直到所有任务完成。

Asyncio 用法

Asyncio 版本的函数 download_all():

代码语言:javascript
复制
tasks = [asyncio.create_task(download_one(site)) for site in sites]
await asyncio.gather(*task)

如何选择多线程还是Asyncio

  • 如果是 I/O bound,并且 I/O 操作很慢,需要很多任务 / 线程协同实现,那么使用 Asyncio 更合适。
  • 如果是 I/O bound,但是 I/O 操作很快,只需要有限数量的任务 / 线程,那么使用多线程就可以了。
  • 如果是 CPU bound,则需要使用多进程来提高程序运行效率。

Python多进程和多线程如何创建

  • 多进程:multiprocessing.Process类
  • 多线程:threading.Thread类

Python GIL

GIL全局解释器锁,每一个 Python 线程,在 CPython 解释器中执行时,都会先锁住自己的线程,阻止别的线程执行。

由于CPython解释器的内存管理并不是线程安全的,为了保护多线程下对Python对象的访问引入了GIL锁。

GIL的影响:同一时间只能有一个线程执行字节码,CPU密集程序难以利用多核优势。IO期间由于会释放锁,对IO密集程序影响不大。

如何规避GIL影响

  • CPU密集可以使用多进程+进程池
  • IO密集使用多线程/协程
  • 将关键性能代码放到C中实现

为什么有了GIL还要关注线程安全:python中只有原子操作是可以保证线程安全的,即一个操作如果是一个字节码指令可以完成就是原子的。


Python的内存管理机制

python的内存管理机制包括内存池机制垃圾回收机制

Python的垃圾回收机制

以引用计数为主,标记清除和分代回收为辅,其中标记清除和分代回收解决循环引用的问题。

引用计数

函数内部声明的局部变量,在函数返回后,局部变量的引用会注销掉;此时变量指代对象的引用数为 0,Python 便会执行垃圾回收。

s.getrefcount() 这个函数,可以查看一个变量的引用次数。

手动释放内存方法:先调用” del 变量名 “删除对象的引用;然后强制调用 gc.collect(),清除没有引用的对象。

标记清除

标记清除算法:遍历并标记一个有向图,在遍历结束后,未被标记的节点即为不可达节点,需要进行垃圾回收。(实现方法:dfs (深度优先搜索)遍历,从起点开始遍历,对遍历到的节点做个记号。遍历完成后,再对所有节点扫一遍,没有被做记号的,就是需要垃圾回收的。)

只有容器类对象才有可能产生循环引用。

分代回收

Python 将所有对象分为三代。刚刚创立的对象是第 0 代;经过一次垃圾回收后,依然存在的对象,便会依次从上一代挪到下一代。而每一代启动自动垃圾回收的阈值,则是可以单独指定的。当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。 gc.get_threshold()可查看三代阈值。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 才浅coding攻略 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档