Python 精选笔试面试习题—sorted 与 sort 单例模式、统计字符个数Count、垃圾回收、lambda函数、静态方法、类方法、实例方法、分布式锁、
1. 字典根據(jù)鍵從小到大排序?
In[38]: dic = {"name": "Tom", "age": 30, "country": "USA"}In[39]: sorted(dic.iteritems(), key=lambda x : x[0])
Out[39]: [('age', 30), ('country', 'USA'), ('name', 'Tom')]In[40]: dict(sorted(dic.iteritems(), key=lambda x : x[0]))
Out[40]: {'age': 30, 'country': 'USA', 'name': 'Tom'}
sorted 語法:
sorted(iterable[, cmp[, key[, reverse]]])
參數(shù)說明:
iterable:可迭代對象。cmp:比較的函數(shù),這個具有兩個參數(shù),參數(shù)的值都是從可迭代對象中取出,此函數(shù)必須遵守的規(guī)則為,大于則返回1,小于則返回-1,等于則返回0。key:主要是用來進行比較的元素,只有一個參數(shù),具體的函數(shù)的參數(shù)就是取自于可迭代對象中,指定可迭代對象中的一個元素來進行排序。reverse:排序規(guī)則,reverse = True 降序 , reverse = False 升序(默認)。
2. 單例模式
class Single(object):__isstance = None__first_init = Falsedef __new__(cls, *args, **kwargs):if not cls.__isstance:cls.__isstance = object.__new__(cls)return cls.__isstancedef __init__(self, name):if not self.__first_init:self.name = nameSingleton.__first_init = True
3. 統(tǒng)計字符串每個單詞出現(xiàn)的次數(shù)
In[41]: from collections import Counter
In[42]: s3 = "kjalfj;ldsjafl;hdsllfdhg;lahfbl;hl;ahlf;h"In[43]: Counter(s3)
Out[43]:
Counter({';': 6,'a': 4,'b': 1,'d': 3,'f': 5,'g': 1,'h': 6,'j': 3,'k': 1,'l': 9,'s': 2})
4. a=(1,)b=(1),c=(“1”) 分別是什么類型的數(shù)據(jù)?
In[46]: a = (1,)
In[47]: type(a)
Out[47]: tupleIn[48]: b = (1)
In[49]: type(b)
Out[49]: intIn[50]: c = ("1")
In[51]: type(c)
Out[51]: str
5. x=“abc”,y=“def”,z=[“d”,“e”,“f”],分別求出 x.join(y) 和 x.join(z) 返回的結(jié)果
join() 括號里面的是可迭代對象,x 插入可迭代對象中間,形成字符串,結(jié)果一致
In [1]: x = "abc"In [2]: y = "def"In [3]: z = ["d", "e", "f"]In [4]: x.join(y)
Out[4]: 'dabceabcf'In [5]: x.join(z)
Out[5]: 'dabceabcf'
6. 請說明 sort 和 sorted 對列表排序的區(qū)別
sort()與sorted()的不同在于,sort是在原位重新排列列表,而sorted()是產(chǎn)生一個新的列表。sorted(L)返回一個排序后的 L,不改變原始的L;L.sort()是對原始的 L 進行操作,調(diào)用后原始的 L 會改變,沒有返回值;所以a = a.sort()是錯的啦!a = sorted(a)才對。sorted()適用于任何可迭代容器,list.sort() 僅支持 list- 基于以上兩點,
sorted使用頻率比list.sort()更高些,所以 Python 中更高級的排序技巧便通過 sorted() 來演示
7. 對 foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4] 進行排序,使用 lambda 函數(shù)從小到大排序
In [6]: foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4]In [7]: sorted(foo, key=lambda x: x)
Out[7]: [-20, -5, -4, -4, -2, 0, 2, 4, 8, 8, 9]In [9]: sorted(foo, key=lambda x: x, reverse=True)
Out[9]: [9, 8, 8, 4, 2, 0, -2, -4, -4, -5, -20]
8. int(“1.4”)、int(1.4)的輸出結(jié)果?
In [10]: int("1.5")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-10-8d7b10d14577> in <module>()
----> 1 int("1.5")ValueError: invalid literal for int() with base 10: '1.5'In [11]: eval("1.5")
Out[11]: 1.5In [12]: int(1.5)
Out[12]: 1
9. Python 垃圾回收機制?
1.引用計數(shù)
In [13]: import sys...: s = "hello world"...: sys.getrefcount(s)...:
Out[13]: 3In [14]: s = "china"In [15]: sys.getrefcount(s)
Out[15]: 2In [16]: s = "beijing"In [17]: sys.getrefcount(s)
Out[17]: 2
2.分代技術(shù)
Python默認定義了三代對象集合,索引數(shù)越大,對象存活時間越長
Python中使用了某些啟發(fā)式算法(heuristics)來加速垃圾回收。例如,越晚創(chuàng)建的對象更有可能被回收。對象被創(chuàng)建之后,垃圾回收器會分配它們所屬的代(generation)。每個對象都會被分配一個代,而被分配更年輕代的對象是優(yōu)先被處理的。
3.引用循環(huán)
垃圾回收器會定時尋找這個循環(huán),并將其回收。舉個例子,假設有兩個對象o1和o2,而且符合o1.x == o2和o2.x == o1這兩個條件。如果o1和o2沒有其他代碼引
10. 什么是 lambda 函數(shù)?有什么好處?
lambda 函數(shù)是一個可以接收任意多個參數(shù)(包括可選參數(shù))并且返回單個表達式值的匿名函數(shù)
1、lambda 函數(shù)比較輕便,即用即刪除,很適合需要完成一項功能,但是此功能只在此一處使用,
連名字都很隨意的情況下;
2、匿名函數(shù),一般用來給 filter, map 這樣的函數(shù)式編程服務;
3、作為回調(diào)函數(shù),傳遞給某些應用,比如消息處理
11. 簡述 read、readline、readlines 的區(qū)別?
read 讀取整個文件
readline 讀取下一行,使用生成器方法
readlines 讀取整個文件到一個迭代器以供我們遍歷
12. Python 中單下劃線和雙下劃線使用
__foo__ 一種約定,Python 內(nèi)部的名字,用來區(qū)別其他用戶自定義的命名,以防沖突,就是例如 __init__(), __del__() , __call__() 這些特殊方法
_foo :一種約定,用來指定變量私有。程序員用來指定私有變量的一種方式。不能用 from module import * 導入,其他方面和公有一樣訪問;
__foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區(qū)別和其他類相同的命名,它無法直接像公有成員一樣隨便訪問,通過對象名._類名__xxx這樣的方式可以訪問.
12. 代碼描述靜態(tài)方法 (staticmethod),類方法(classmethod) 和實例方法
@staticmethod 和@classmethod 都可以直接類名.方法名()來調(diào)用,不用在實例化一個類。
class A(object):def foo(self,x):print "executing foo(%s,%s)"%(self,x)@classmethoddef class_foo(cls,x):print "executing class_foo(%s,%s)"%(cls,x)@staticmethoddef static_foo(x):print "executing static_foo(%s)"%xa=A()
@staticmethod 經(jīng)常有一些跟類有關(guān)系的功能但在運行時又不需要實例和類參與的情況下需要用到靜態(tài)方法
IND = 'ON'class Kls(object):def __init__(self, data):self.data = data@staticmethoddef check_ind():return (IND == 'ON')def do_reset(self):if self.check_ind():print('Reset done for:', self.data)def set_db(self):if self.check_ind():self.db = 'New db connection'print('DB connection made for: ', self.data)ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()
13. 新式類和舊式類的區(qū)別?
- Python 2.x 中默認都是經(jīng)典類,只有顯式繼承了 object 才是新式類,Python 3.x 中默認都是新式類,經(jīng)典類被移除,不必顯式的繼承 object。
- 新式類都從 object 繼承,經(jīng)典類不需要。
- 新式類的 MRO(method resolution order 基類搜索順序)算法采用 C3 算法廣度優(yōu)先搜索,而舊式類的 MRO 算法是采用深度優(yōu)先搜索。
- 新式類相同父類只執(zhí)行一次構(gòu)造函數(shù),經(jīng)典類重復執(zhí)行多次。
14. 請寫出一個在函數(shù)執(zhí)行后輸出日志的裝飾器
裝飾器就是一個函數(shù),它可以在不需要做任何代碼變動的前提下給一個函數(shù)增加額外功能,啟動裝飾的效果。 它經(jīng)常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權(quán)限校驗等場景。 下面是一個日志功能的裝飾器
from functools import wrapsdef do_log(func):@wraps(func)def wrapper(*args, **kw):if func.__name__ == "debug":msg = "debug {}".format(args[0])elif func.__name__ == "info":msg = "info {}".format(args[0])else:msg = "unknown {}".format(args[0])return func(msg, **kw)return wrapper@do_log
def debug(msg):print(msg)@do_log
def info(msg):print(msg)if __name__ == "__main__":debug("123")info("abc")
15. 請解釋一下協(xié)程的優(yōu)點
子程序切換不是線程切換,而是由程序自身控制
沒有線程切換的開銷,和多線程比,線程數(shù)量越多,協(xié)程的性能優(yōu)勢就越明顯
不需要多線程的鎖機制,因為只有一個線程,也不存在同時寫變量沖突,在協(xié)程中控制共享資源不加鎖
16. a=“hello” 和 b=“你好” 編碼成 bytes 類型
In [1]: a = b"hello"In [2]: b = bytes("hello", "utf-8")In [3]: c = "北京".encode("utf-8")In [4]: a, b, c
Out[4]: (b'hello', b'hello', b'\xe5\x8c\x97\xe4\xba\xac')
17. 寫一個函數(shù),接收整數(shù)參數(shù) n,返回一個函數(shù),函數(shù)的功能是把函數(shù)的參數(shù)和 n 相乘并把結(jié)果返回
主要考察閉包知識點:
1.必須有一個內(nèi)嵌函數(shù)
2.內(nèi)嵌函數(shù)必須引用外部函數(shù)中的變量
3.外部函數(shù)的返回值必須是內(nèi)嵌函數(shù)
In [5]: def f(n):...: def g(m):...: return n*m...: return g...: In [6]: f(3)(4)
Out[6]: 12In [7]: m = f(4)In [8]: m(5)
Out[8]: 20
18. Python 字典和 json 字符串相互轉(zhuǎn)化方法
在 Python 中使用 dumps 方法 將 dict 對象轉(zhuǎn)為 Json 對象,使用 loads 方法可以將 Json 對象轉(zhuǎn)為 dict 對象。
In [9]: dic = {'a': 10, 'b': "123", 'c': "hello world"}In [10]: import jsonIn [11]: json_str = json.dumps(dic)In [12]: json_str
Out[12]: '{"a": 10, "b": "123", "c": "hello world"}'In [13]: dic_loads = json.loads(json_str)In [14]: dic_loads
Out[14]: {'a': 10, 'b': '123', 'c': 'hello world'}In [15]: json.loads(str(dic))
---------------------------------------------------------------------------
JSONDecodeError Traceback (most recent JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)In [16]: json.loads(str(dic).replace("'", "\""))
Out[16]: {'a': 10, 'b': '123', 'c': 'hello world'}
如果直接使用 json.loads(str(dic))會報錯原因就是,單引號的字符串不符合 Json 的標準格式所以再次使用了 replace("’", “”")。
Json 的標準格式是不支持單引號型字符串的。
19. 分布式鎖
分布式鎖是控制分布式系統(tǒng)之間的同步訪問共享資源的一種方式。 對于分布式鎖的目標,我們必須首先明確三點:
- 任何一個時間點必須只能夠有一個客戶端擁有鎖。
- 不能夠有死鎖,也就是最終客戶端都能夠獲得鎖,盡管可能會經(jīng)歷失敗。
- 錯誤容忍性要好,只要有大部分的Redis實例存活,客戶端就應該能夠獲得鎖。 分布式鎖的條件 互斥性:分布式鎖需要保證在不同節(jié)點的不同線程的互斥 可重入性:同一個節(jié)點上的同一個線程如果獲取了鎖之后,能夠再次獲取這個鎖。 鎖超時:支持超時釋放鎖,防止死鎖 高效,高可用:加鎖和解鎖需要高效,同時也需要保證高可用防止分布式鎖失效,可以增加降級。 支持阻塞和非阻塞:可以實現(xiàn)超時獲取失敗,tryLock(long timeOut) 支持公平鎖和非公平鎖
分布式鎖的實現(xiàn)方案 1、數(shù)據(jù)庫實現(xiàn)(樂觀鎖) 2、基于zookeeper的實現(xiàn) 3、基于Redis的實現(xiàn)(推薦)
20. Python 實現(xiàn)分布式鎖
REDIS分布式鎖實現(xiàn)的方式:SETNX + GETSET,NX是Not eXists的縮寫,如SETNX命令就應該理解為:SET if Not eXists。 多個進程執(zhí)行以下Redis命令:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果 SETNX 返回1,說明該進程獲得鎖,SETNX將鍵 lock.foo 的值設置為鎖的超時時間(當前時間 + 鎖的有效時間)。 如果 SETNX 返回0,說明其他進程已經(jīng)獲得了鎖,進程不能進入臨界區(qū)。進程可以在一個循環(huán)中不斷地嘗試 SETNX 操作,以獲得鎖。
import time
import redis
from conf.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORDclass RedisLock:def __init__(self):self.conn = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, db=1)self._lock = 0self.lock_key = ""@staticmethoddef my_float(timestamp):"""Args:timestamp:Returns:float或者0如果取出的是None,說明原本鎖并沒人用,getset已經(jīng)寫入,返回0,可以繼續(xù)操作。"""if timestamp:return float(timestamp)else:#防止取出的值為None,轉(zhuǎn)換float報錯return 0@staticmethoddef get_lock(cls, key, timeout=10):cls.lock_key = f"{key}_dynamic_lock"while cls._lock != 1:timestamp = time.time() + timeout + 1cls._lock = cls.conn.setnx(cls.lock_key, timestamp)# if 條件中,可能在運行到or之后被釋放,也可能在and之后被釋放# 將導致 get到一個None,float失敗。if cls._lock == 1 or (time.time() > cls.my_float(cls.conn.get(cls.lock_key)) andtime.time() > cls.my_float(cls.conn.getset(cls.lock_key, timestamp))):breakelse:time.sleep(0.3)@staticmethoddef release(cls):if cls.conn.get(cls.lock_key) and time.time() < cls.conn.get(cls.lock_key):cls.conn.delete(cls.lock_key)def redis_lock_deco(cls):def _deco(func):def __deco(*args, **kwargs):cls.get_lock(cls, args[1])try:return func(*args, **kwargs)finally:cls.release(cls)return __decoreturn _deco@redis_lock_deco(RedisLock())
def my_func():print("myfunc() called.")time.sleep(20)if __name__ == "__main__":my_func()
21. 魔法函數(shù) _call_怎么使用?
_call_ 可以把類實例當做函數(shù)調(diào)用。 使用示例如下
class Bar:def __call__(self, *args, **kwargs):print('in call')if __name__ == '__main__':b = Bar()b()
22. Python 中的反射了解么?
Python 的反射機制設定較為簡單,一共有四個關(guān)鍵函數(shù)分別是 getattr、hasattr、setattr、delattr
hasattr()、getattr()、setattr() 的用法:
這三個方法屬于 Python 的反射機制里面的,
- hasattr 可以判斷一個對象是否含有某個屬性,
- getattr 可以充當 get 獲取對象屬性的作用。
- setattr 可以充當 person.name = "liming"的賦值操作。
代碼示例如下:
class Person():def __init__(self):self.name = "liming"self.age = 12def show(self):print(self.name)print(self.age)def set_name(self):setattr(Person, "sex", "男")def get_name(self):print(getattr(self, "name"))print(getattr(self, "age"))print(getattr(self, "sex"))def run():if hasattr(Person, "show"):print("判斷 Person 類是否含有 show 方法")Person().set_name()Person().get_name()if __name__ == '__main__':run()
23. Python 的魔法方法及用途
1 _init_():
類的初始化方法。它獲取任何傳給構(gòu)造器的參數(shù)(比如我們調(diào)用 x = SomeClass(10, ‘foo’) , __init__就會接到參數(shù) 10 和 ‘foo’ 。 __init__在 Python 的類定義中用的最多。
2 _new_:
__new__是對象實例化時第一個調(diào)用的方法,它只取下 cls 參數(shù),并把其他參數(shù)傳給 init 。 __new__很少使用,但是也有它適合的場景,尤其是當類繼承自一個像元組或者字符串這樣不經(jīng)常改變的類型的時候.
3 _del_:
__new__和 __init__是對象的構(gòu)造器, __del__是對象的銷毀器。它并非實現(xiàn)了語句 del x (因此該語句不等同于 x.del())。而是定義了當對象被垃圾回收時的行為。 當對象需要在銷毀時做一些處理的時候這個方法很有用,比如 socket 對象、文件對象。但是需要注意的是,當 Python 解釋器退出但對象仍然存活的時候,__del__并不會 執(zhí)行。 所以養(yǎng)成一個手工清理的好習慣是很重要的,比如及時關(guān)閉連接。
24. metaclass 作用?以及應用場景?
metaclass 即元類,metaclass 是類似創(chuàng)建類的模板,所有的類都是通過他來 create 的(調(diào)用new),這使得你可以自由的控制創(chuàng)建類的那個過程,實現(xiàn)你所需要的功能。 我們可以使用元類創(chuàng)建單例模式和實現(xiàn) ORM 模式。
可以使用元類實現(xiàn)一個單例模式,代碼如下
class Singleton(type):def __init__(self, *args, **kwargs):print("in __init__")self.__instance = Nonesuper(Singleton, self).__init__(*args, **kwargs)def __call__(self, *args, **kwargs):print("in __call__")if self.__instance is None:self.__instance = super(Singleton, self).__call__(*args, **kwargs)return self.__instanceclass Foo(metaclass=Singleton):pass # 在代碼執(zhí)行到這里的時候,元類中的__new__方法和__init__方法其實已經(jīng)被執(zhí)行了,而不是在 Foo 實例化的時候執(zhí)行。且僅會執(zhí)行一次。foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2)
25. 在 Python 中是如何管理內(nèi)存的?
- 垃圾回收:
Python 不像 C++,Java 等語言一樣,他們可以不用事先聲明變量類型而直接對變量進行賦值。對 Python 語言來講,對象的類型和內(nèi)存都是在運行時確定的。這也是為什么我們稱 Python 語言為動態(tài)類型的原因(這里我們把動態(tài)類型可以簡單的歸結(jié)為對變量內(nèi)存地址的分配是在運行時自動判斷變量類型并對變量進行賦值)。
- 引用計數(shù):
Python 采用了類似 Windows 內(nèi)核對象一樣的方式來對內(nèi)存進行管理。每一個對象,都維護這一個對指向該對對象的引用的計數(shù)。當變量被綁定在一個對象上的時候,該變量的引用計數(shù)就是 1,(還有另外一些情況也會導致變量引用計數(shù)的增加),系統(tǒng)會自動維護這些標簽,并定時掃描,當某標簽的引用計數(shù)變?yōu)?0 的時候,該對就會被回收。
26. 當退出 Python 時是否釋放所有內(nèi)存分配?
不是的,循環(huán)引用其他對象或引用自全局命名空間的對象的模塊,在 Python 退出時并非完全釋放。
另外,也不會釋放 c 庫保留的內(nèi)存部分。
27. Python 中遞歸的最大次數(shù),那如何突破呢?
Python 有遞歸次數(shù)限制,默認最大次數(shù)為 2000。
In [17]: import sysIn [18]: sys.getrecursionlimit()
Out[18]: 2000
通過下面的代碼可以突破這個限制
In [19]: sys.setrecursionlimit(2500)In [20]: sys.getrecursionlimit()
Out[20]: 2500
另外需要注意的是 sys.setrecursionlimit() 只是修改解釋器在解釋時允許的最大遞歸次數(shù),此外,限制最大遞歸次數(shù)的還和操作系統(tǒng)有關(guān)。
28. 一個包里有三個模塊,demo1.py、demo2.py、demo3.py,但使用 from tools import * 導入模塊時,如何保證只有 demo1、demo3 被導入了.
增加_init_.py 文件,并在文件中增加:
__all__ = ['demo1','demo3']
29. 代碼中經(jīng)常遇到的 *args, **kwargs 含義及用法
在函數(shù)定義中使用 *args 和 **kwargs 傳遞可變長參數(shù)。
*args 用來將參數(shù)打包成 tuple 給函數(shù)體調(diào)用。
**kwargs 打包關(guān)鍵字參數(shù)成 dict 給函數(shù)體調(diào)用。
29. Python 中會有函數(shù)或成員變量包含單下劃線前綴和結(jié)尾,和雙下劃線前綴結(jié)尾,區(qū)別是什么?
“單下劃線” 開始的成員變量叫做保護變量,意思是只有類對象和子類對象自己能訪問到這些變量; “雙下劃線” 開始的是私有成員,意思是只有類對象自己能訪問,連子類對象也不能訪問到這個數(shù)據(jù)。
以單下劃線開頭(_foo)的代表不能直接訪問的類屬性,需通過類提供的接口進行訪問,不能用“from xxx import *”而導入;以雙下劃線開頭的(__foo)代表類的私有成員;
以雙下劃線開頭和結(jié)尾的(_foo)代表 Python 里特殊方法專用的標識,如 _init()代表類的構(gòu)造函數(shù)。
代碼示例:
class Person:"""docstring for ClassName"""def __init__(self):self.__age = 12self._sex = 12def _sex(self):return "男"def set_age(self,age):self.__age = agedef get_age(self):return self.__age if __name__ == '__main__':p=Person()print(p._sex)#print(p.__age)#Python 自動將__age 解釋成 _Person__age,于是我們用 _Person__age 訪問,這次成功。print(p._Person__age)
30. IO 多路復用的作用?
阻塞 I/O 只能阻塞一個 I/O 操作,而 I/O 復用模型能夠阻塞多個 I/O 操作,所以才叫做多路復用。
I/O 多路復用是用于提升效率,單個進程可以同時監(jiān)聽多個網(wǎng)絡連接 IO。 在 IO 密集型的系統(tǒng)中, 相對于線程切換的開銷問題,IO 多路復用可以極大的提升系統(tǒng)效率。
1. 說一說多線程,多進程和協(xié)程的區(qū)別?
進程:
進程是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,
進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。每個進程都有自己的獨立內(nèi)存空間,
不同進程通過進程間通信來通信。由于進程比較重量,占據(jù)獨立的內(nèi)存,
所以上下文進程間的切換開銷(棧、寄存器、虛擬內(nèi)存、文件句柄等)比較大,但相對比較穩(wěn)定安全。
線程:
線程是進程的一個實體,是 CPU 調(diào)度和分派的基本單位,
它是比進程更小的能獨立運行的基本單位.
線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),
但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
線程間通信主要通過共享內(nèi)存,上下文切換很快,資源開銷較少,但相比進程不夠穩(wěn)定容易丟失數(shù)據(jù)。
協(xié)程:
協(xié)程是一種用戶態(tài)的輕量級線程,協(xié)程的調(diào)度完全由用戶控制。
協(xié)程擁有自己的寄存器上下文和棧。
協(xié)程調(diào)度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,
直接操作棧則基本沒有內(nèi)核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非常快。
區(qū)別: 進程與線程比較: 線程是指進程內(nèi)的一個執(zhí)行單元,也是進程內(nèi)的可調(diào)度實體。線程與進程的區(qū)別:
- 地址空間:線程是進程內(nèi)的一個執(zhí)行單元,進程內(nèi)至少有一個線程,它們共享進程的地址空間,
而進程有自己獨立的地址空間 - 資源擁有:進程是資源分配和擁有的單位,同一個進程內(nèi)的線程共享進程的資源
- 線程是處理器調(diào)度的基本單位,但進程不是
- 二者均可并發(fā)執(zhí)行
- 每個獨立的線程有一個程序運行的入口、順序執(zhí)行序列和程序的出口,
但是線程不能夠獨立執(zhí)行,必須依存在應用程序中,由應用程序提供多個線程執(zhí)行控制
協(xié)程與線程進行比較:
- 一個線程可以多個協(xié)程,一個進程也可以單獨擁有多個協(xié)程,這樣 Python 中則能使用多核 CPU。
- 線程進程都是同步機制,而協(xié)程則是異步
- 協(xié)程能保留上一次調(diào)用時的狀態(tài),每次過程重入時,就相當于進入上一次調(diào)用的狀態(tài)
2. 什么是并發(fā)和并行?
"并行是指同一時刻同時做多件事情,而并發(fā)是指同一時間間隔內(nèi)做多件事情”。
并發(fā)與并行是兩個既相似而又不相同的概念:并發(fā)性,又稱共行性,是指能處理多個同時性活動的能力;并行是指同時發(fā)生的兩個并發(fā)事件,具有并發(fā)的含義,而并發(fā)則不一定并行,也亦是說并發(fā)事件之間不一定要同一時刻發(fā)生。
并發(fā)的實質(zhì)是一個物理 CPU(也可以多個物理 CPU) 在若干道程序之間多路復用,并發(fā)性是對有限物理資源強制行使多用戶共享以提高效率。 并行性指兩個或兩個以上事件或活動在同一時刻發(fā)生。在多道程序環(huán)境下,并行性使多個程序同一時刻可在不同 CPU 上同時執(zhí)行。
并行,是每個 CPU 運行一個程序。
3. 解釋什么是異步非阻塞?
異步 異步與同步相對,當一個異步過程調(diào)用發(fā)出后,調(diào)用者在沒有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作。當這個調(diào)用完成后,一般通過狀態(tài)、通知和回調(diào)來通知調(diào)用者。對于異步調(diào)用,調(diào)用的返回并不受調(diào)用者控制。
非阻塞 非阻塞是這樣定義的,當線程遇到 I/O 操作時,不會以阻塞的方式等待 I/O 操作的完成或數(shù)據(jù)的返回,而只是將 I/O 請求發(fā)送給操作系統(tǒng),繼續(xù)執(zhí)行下一條語句。當操作系統(tǒng)完成 I/O 操作時,以事件的形式通知執(zhí)行 I/O 操作的線程,線程會在特定時候處理這個事件。簡答理解就是如果程序不會卡住,可以繼續(xù)執(zhí)行,就是說非阻塞的。
4. threading.local 的作用?
threading.local()這個方法是用來保存一個全局變量,但是這個全局變量只有在當前線程才能訪問,如果你在開發(fā)多線程應用的時候,需要每個線程保存一個單獨的數(shù)據(jù)供當前線程操作,可以考慮使用這個方法,簡單有效。代碼示例
import threading
import timea = threading.local()#全局對象def worker():a.x = 0for i in range(200):time.sleep(0.01)a.x += 1print(threading.current_thread(),a.x)for i in range(20):threading.Thread(target=worker).start()
總結(jié)
以上是生活随笔為你收集整理的Python 精选笔试面试习题—sorted 与 sort 单例模式、统计字符个数Count、垃圾回收、lambda函数、静态方法、类方法、实例方法、分布式锁、的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sklearn FutureWarnin
- 下一篇: RabbitMQ 入门系列(9)— P