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 語(yǔ)法:
sorted(iterable[, cmp[, key[, reverse]]])
參數(shù)說(shuō)明:
iterable:可迭代對(duì)象。cmp:比較的函數(shù),這個(gè)具有兩個(gè)參數(shù),參數(shù)的值都是從可迭代對(duì)象中取出,此函數(shù)必須遵守的規(guī)則為,大于則返回1,小于則返回-1,等于則返回0。key:主要是用來(lái)進(jìn)行比較的元素,只有一個(gè)參數(shù),具體的函數(shù)的參數(shù)就是取自于可迭代對(duì)象中,指定可迭代對(duì)象中的一個(gè)元素來(lái)進(jìn)行排序。reverse:排序規(guī)則,reverse = True 降序 , reverse = False 升序(默認(rèn))。
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)計(jì)字符串每個(gè)單詞出現(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() 括號(hào)里面的是可迭代對(duì)象,x 插入可迭代對(duì)象中間,形成字符串,結(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. 請(qǐng)說(shuō)明 sort 和 sorted 對(duì)列表排序的區(qū)別
sort()與sorted()的不同在于,sort是在原位重新排列列表,而sorted()是產(chǎn)生一個(gè)新的列表。sorted(L)返回一個(gè)排序后的 L,不改變?cè)嫉腖;L.sort()是對(duì)原始的 L 進(jìn)行操作,調(diào)用后原始的 L 會(huì)改變,沒(méi)有返回值;所以a = a.sort()是錯(cuò)的啦!a = sorted(a)才對(duì)。sorted()適用于任何可迭代容器,list.sort() 僅支持 list- 基于以上兩點(diǎn),
sorted使用頻率比list.sort()更高些,所以 Python 中更高級(jí)的排序技巧便通過(guò) sorted() 來(lái)演示
7. 對(duì) foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4] 進(jìn)行排序,使用 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 垃圾回收機(jī)制?
1.引用計(jì)數(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默認(rèn)定義了三代對(duì)象集合,索引數(shù)越大,對(duì)象存活時(shí)間越長(zhǎng)
Python中使用了某些啟發(fā)式算法(heuristics)來(lái)加速垃圾回收。例如,越晚創(chuàng)建的對(duì)象更有可能被回收。對(duì)象被創(chuàng)建之后,垃圾回收器會(huì)分配它們所屬的代(generation)。每個(gè)對(duì)象都會(huì)被分配一個(gè)代,而被分配更年輕代的對(duì)象是優(yōu)先被處理的。
3.引用循環(huán)
垃圾回收器會(huì)定時(shí)尋找這個(gè)循環(huán),并將其回收。舉個(gè)例子,假設(shè)有兩個(gè)對(duì)象o1和o2,而且符合o1.x == o2和o2.x == o1這兩個(gè)條件。如果o1和o2沒(méi)有其他代碼引
10. 什么是 lambda 函數(shù)?有什么好處?
lambda 函數(shù)是一個(gè)可以接收任意多個(gè)參數(shù)(包括可選參數(shù))并且返回單個(gè)表達(dá)式值的匿名函數(shù)
1、lambda 函數(shù)比較輕便,即用即刪除,很適合需要完成一項(xiàng)功能,但是此功能只在此一處使用,
連名字都很隨意的情況下;
2、匿名函數(shù),一般用來(lái)給 filter, map 這樣的函數(shù)式編程服務(wù);
3、作為回調(diào)函數(shù),傳遞給某些應(yīng)用,比如消息處理
11. 簡(jiǎn)述 read、readline、readlines 的區(qū)別?
read 讀取整個(gè)文件
readline 讀取下一行,使用生成器方法
readlines 讀取整個(gè)文件到一個(gè)迭代器以供我們遍歷
12. Python 中單下劃線和雙下劃線使用
__foo__ 一種約定,Python 內(nèi)部的名字,用來(lái)區(qū)別其他用戶自定義的命名,以防沖突,就是例如 __init__(), __del__() , __call__() 這些特殊方法
_foo :一種約定,用來(lái)指定變量私有。程序員用來(lái)指定私有變量的一種方式。不能用 from module import * 導(dǎo)入,其他方面和公有一樣訪問(wèn);
__foo:這個(gè)有真正的意義:解析器用_classname__foo來(lái)代替這個(gè)名字,以區(qū)別和其他類相同的命名,它無(wú)法直接像公有成員一樣隨便訪問(wèn),通過(guò)對(duì)象名._類名__xxx這樣的方式可以訪問(wèn).
12. 代碼描述靜態(tài)方法 (staticmethod),類方法(classmethod) 和實(shí)例方法
@staticmethod 和@classmethod 都可以直接類名.方法名()來(lái)調(diào)用,不用在實(shí)例化一個(gè)類。
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)系的功能但在運(yùn)行時(shí)又不需要實(shí)例和類參與的情況下需要用到靜態(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 中默認(rèn)都是經(jīng)典類,只有顯式繼承了 object 才是新式類,Python 3.x 中默認(rèn)都是新式類,經(jīng)典類被移除,不必顯式的繼承 object。
- 新式類都從 object 繼承,經(jīng)典類不需要。
- 新式類的 MRO(method resolution order 基類搜索順序)算法采用 C3 算法廣度優(yōu)先搜索,而舊式類的 MRO 算法是采用深度優(yōu)先搜索。
- 新式類相同父類只執(zhí)行一次構(gòu)造函數(shù),經(jīng)典類重復(fù)執(zhí)行多次。
14. 請(qǐng)寫出一個(gè)在函數(shù)執(zhí)行后輸出日志的裝飾器
裝飾器就是一個(gè)函數(shù),它可以在不需要做任何代碼變動(dòng)的前提下給一個(gè)函數(shù)增加額外功能,啟動(dòng)裝飾的效果。 它經(jīng)常用于有切面需求的場(chǎng)景,比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場(chǎng)景。 下面是一個(gè)日志功能的裝飾器
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. 請(qǐng)解釋一下協(xié)程的優(yōu)點(diǎn)
子程序切換不是線程切換,而是由程序自身控制
沒(méi)有線程切換的開(kāi)銷,和多線程比,線程數(shù)量越多,協(xié)程的性能優(yōu)勢(shì)就越明顯
不需要多線程的鎖機(jī)制,因?yàn)橹挥幸粋€(gè)線程,也不存在同時(shí)寫變量沖突,在協(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. 寫一個(gè)函數(shù),接收整數(shù)參數(shù) n,返回一個(gè)函數(shù),函數(shù)的功能是把函數(shù)的參數(shù)和 n 相乘并把結(jié)果返回
主要考察閉包知識(shí)點(diǎn):
1.必須有一個(gè)內(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 對(duì)象轉(zhuǎn)為 Json 對(duì)象,使用 loads 方法可以將 Json 對(duì)象轉(zhuǎn)為 dict 對(duì)象。
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))會(huì)報(bào)錯(cuò)原因就是,單引號(hào)的字符串不符合 Json 的標(biāo)準(zhǔn)格式所以再次使用了 replace("’", “”")。
Json 的標(biāo)準(zhǔn)格式是不支持單引號(hào)型字符串的。
19. 分布式鎖
分布式鎖是控制分布式系統(tǒng)之間的同步訪問(wèn)共享資源的一種方式。 對(duì)于分布式鎖的目標(biāo),我們必須首先明確三點(diǎn):
- 任何一個(gè)時(shí)間點(diǎn)必須只能夠有一個(gè)客戶端擁有鎖。
- 不能夠有死鎖,也就是最終客戶端都能夠獲得鎖,盡管可能會(huì)經(jīng)歷失敗。
- 錯(cuò)誤容忍性要好,只要有大部分的Redis實(shí)例存活,客戶端就應(yīng)該能夠獲得鎖。 分布式鎖的條件 互斥性:分布式鎖需要保證在不同節(jié)點(diǎn)的不同線程的互斥 可重入性:同一個(gè)節(jié)點(diǎn)上的同一個(gè)線程如果獲取了鎖之后,能夠再次獲取這個(gè)鎖。 鎖超時(shí):支持超時(shí)釋放鎖,防止死鎖 高效,高可用:加鎖和解鎖需要高效,同時(shí)也需要保證高可用防止分布式鎖失效,可以增加降級(jí)。 支持阻塞和非阻塞:可以實(shí)現(xiàn)超時(shí)獲取失敗,tryLock(long timeOut) 支持公平鎖和非公平鎖
分布式鎖的實(shí)現(xiàn)方案 1、數(shù)據(jù)庫(kù)實(shí)現(xiàn)(樂(lè)觀鎖) 2、基于zookeeper的實(shí)現(xiàn) 3、基于Redis的實(shí)現(xiàn)(推薦)
20. Python 實(shí)現(xiàn)分布式鎖
REDIS分布式鎖實(shí)現(xiàn)的方式:SETNX + GETSET,NX是Not eXists的縮寫,如SETNX命令就應(yīng)該理解為:SET if Not eXists。 多個(gè)進(jìn)程執(zhí)行以下Redis命令:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果 SETNX 返回1,說(shuō)明該進(jìn)程獲得鎖,SETNX將鍵 lock.foo 的值設(shè)置為鎖的超時(shí)時(shí)間(當(dāng)前時(shí)間 + 鎖的有效時(shí)間)。 如果 SETNX 返回0,說(shuō)明其他進(jìn)程已經(jīng)獲得了鎖,進(jìn)程不能進(jìn)入臨界區(qū)。進(jìn)程可以在一個(gè)循環(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,說(shuō)明原本鎖并沒(méi)人用,getset已經(jīng)寫入,返回0,可以繼續(xù)操作。"""if timestamp:return float(timestamp)else:#防止取出的值為None,轉(zhuǎn)換float報(bào)錯(cuò)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 條件中,可能在運(yùn)行到or之后被釋放,也可能在and之后被釋放# 將導(dǎo)致 get到一個(gè)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í)例當(dāng)做函數(shù)調(diào)用。 使用示例如下
class Bar:def __call__(self, *args, **kwargs):print('in call')if __name__ == '__main__':b = Bar()b()
22. Python 中的反射了解么?
Python 的反射機(jī)制設(shè)定較為簡(jiǎn)單,一共有四個(gè)關(guān)鍵函數(shù)分別是 getattr、hasattr、setattr、delattr
hasattr()、getattr()、setattr() 的用法:
這三個(gè)方法屬于 Python 的反射機(jī)制里面的,
- hasattr 可以判斷一個(gè)對(duì)象是否含有某個(gè)屬性,
- getattr 可以充當(dāng) get 獲取對(duì)象屬性的作用。
- setattr 可以充當(dāng) 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__就會(huì)接到參數(shù) 10 和 ‘foo’ 。 __init__在 Python 的類定義中用的最多。
2 _new_:
__new__是對(duì)象實(shí)例化時(shí)第一個(gè)調(diào)用的方法,它只取下 cls 參數(shù),并把其他參數(shù)傳給 init 。 __new__很少使用,但是也有它適合的場(chǎng)景,尤其是當(dāng)類繼承自一個(gè)像元組或者字符串這樣不經(jīng)常改變的類型的時(shí)候.
3 _del_:
__new__和 __init__是對(duì)象的構(gòu)造器, __del__是對(duì)象的銷毀器。它并非實(shí)現(xiàn)了語(yǔ)句 del x (因此該語(yǔ)句不等同于 x.del())。而是定義了當(dāng)對(duì)象被垃圾回收時(shí)的行為。 當(dāng)對(duì)象需要在銷毀時(shí)做一些處理的時(shí)候這個(gè)方法很有用,比如 socket 對(duì)象、文件對(duì)象。但是需要注意的是,當(dāng) Python 解釋器退出但對(duì)象仍然存活的時(shí)候,__del__并不會(huì) 執(zhí)行。 所以養(yǎng)成一個(gè)手工清理的好習(xí)慣是很重要的,比如及時(shí)關(guān)閉連接。
24. metaclass 作用?以及應(yīng)用場(chǎng)景?
metaclass 即元類,metaclass 是類似創(chuàng)建類的模板,所有的類都是通過(guò)他來(lái) create 的(調(diào)用new),這使得你可以自由的控制創(chuàng)建類的那個(gè)過(guò)程,實(shí)現(xiàn)你所需要的功能。 我們可以使用元類創(chuàng)建單例模式和實(shí)現(xiàn) ORM 模式。
可以使用元類實(shí)現(xiàn)一個(gè)單例模式,代碼如下
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í)行到這里的時(shí)候,元類中的__new__方法和__init__方法其實(shí)已經(jīng)被執(zhí)行了,而不是在 Foo 實(shí)例化的時(shí)候執(zhí)行。且僅會(huì)執(zhí)行一次。foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2)
25. 在 Python 中是如何管理內(nèi)存的?
- 垃圾回收:
Python 不像 C++,Java 等語(yǔ)言一樣,他們可以不用事先聲明變量類型而直接對(duì)變量進(jìn)行賦值。對(duì) Python 語(yǔ)言來(lái)講,對(duì)象的類型和內(nèi)存都是在運(yùn)行時(shí)確定的。這也是為什么我們稱 Python 語(yǔ)言為動(dòng)態(tài)類型的原因(這里我們把動(dòng)態(tài)類型可以簡(jiǎn)單的歸結(jié)為對(duì)變量?jī)?nèi)存地址的分配是在運(yùn)行時(shí)自動(dòng)判斷變量類型并對(duì)變量進(jìn)行賦值)。
- 引用計(jì)數(shù):
Python 采用了類似 Windows 內(nèi)核對(duì)象一樣的方式來(lái)對(duì)內(nèi)存進(jìn)行管理。每一個(gè)對(duì)象,都維護(hù)這一個(gè)對(duì)指向該對(duì)對(duì)象的引用的計(jì)數(shù)。當(dāng)變量被綁定在一個(gè)對(duì)象上的時(shí)候,該變量的引用計(jì)數(shù)就是 1,(還有另外一些情況也會(huì)導(dǎo)致變量引用計(jì)數(shù)的增加),系統(tǒng)會(huì)自動(dòng)維護(hù)這些標(biāo)簽,并定時(shí)掃描,當(dāng)某標(biāo)簽的引用計(jì)數(shù)變?yōu)?0 的時(shí)候,該對(duì)就會(huì)被回收。
26. 當(dāng)退出 Python 時(shí)是否釋放所有內(nèi)存分配?
不是的,循環(huán)引用其他對(duì)象或引用自全局命名空間的對(duì)象的模塊,在 Python 退出時(shí)并非完全釋放。
另外,也不會(huì)釋放 c 庫(kù)保留的內(nèi)存部分。
27. Python 中遞歸的最大次數(shù),那如何突破呢?
Python 有遞歸次數(shù)限制,默認(rèn)最大次數(shù)為 2000。
In [17]: import sysIn [18]: sys.getrecursionlimit()
Out[18]: 2000
通過(guò)下面的代碼可以突破這個(gè)限制
In [19]: sys.setrecursionlimit(2500)In [20]: sys.getrecursionlimit()
Out[20]: 2500
另外需要注意的是 sys.setrecursionlimit() 只是修改解釋器在解釋時(shí)允許的最大遞歸次數(shù),此外,限制最大遞歸次數(shù)的還和操作系統(tǒng)有關(guān)。
28. 一個(gè)包里有三個(gè)模塊,demo1.py、demo2.py、demo3.py,但使用 from tools import * 導(dǎo)入模塊時(shí),如何保證只有 demo1、demo3 被導(dǎo)入了.
增加_init_.py 文件,并在文件中增加:
__all__ = ['demo1','demo3']
29. 代碼中經(jīng)常遇到的 *args, **kwargs 含義及用法
在函數(shù)定義中使用 *args 和 **kwargs 傳遞可變長(zhǎng)參數(shù)。
*args 用來(lái)將參數(shù)打包成 tuple 給函數(shù)體調(diào)用。
**kwargs 打包關(guān)鍵字參數(shù)成 dict 給函數(shù)體調(diào)用。
29. Python 中會(huì)有函數(shù)或成員變量包含單下劃線前綴和結(jié)尾,和雙下劃線前綴結(jié)尾,區(qū)別是什么?
“單下劃線” 開(kāi)始的成員變量叫做保護(hù)變量,意思是只有類對(duì)象和子類對(duì)象自己能訪問(wèn)到這些變量; “雙下劃線” 開(kāi)始的是私有成員,意思是只有類對(duì)象自己能訪問(wèn),連子類對(duì)象也不能訪問(wèn)到這個(gè)數(shù)據(jù)。
以單下劃線開(kāi)頭(_foo)的代表不能直接訪問(wèn)的類屬性,需通過(guò)類提供的接口進(jìn)行訪問(wèn),不能用“from xxx import *”而導(dǎo)入;以雙下劃線開(kāi)頭的(__foo)代表類的私有成員;
以雙下劃線開(kāi)頭和結(jié)尾的(_foo)代表 Python 里特殊方法專用的標(biāo)識(shí),如 _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 自動(dòng)將__age 解釋成 _Person__age,于是我們用 _Person__age 訪問(wèn),這次成功。print(p._Person__age)
30. IO 多路復(fù)用的作用?
阻塞 I/O 只能阻塞一個(gè) I/O 操作,而 I/O 復(fù)用模型能夠阻塞多個(gè) I/O 操作,所以才叫做多路復(fù)用。
I/O 多路復(fù)用是用于提升效率,單個(gè)進(jìn)程可以同時(shí)監(jiān)聽(tīng)多個(gè)網(wǎng)絡(luò)連接 IO。 在 IO 密集型的系統(tǒng)中, 相對(duì)于線程切換的開(kāi)銷問(wèn)題,IO 多路復(fù)用可以極大的提升系統(tǒng)效率。
1. 說(shuō)一說(shuō)多線程,多進(jìn)程和協(xié)程的區(qū)別?
進(jìn)程:
進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),
進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。每個(gè)進(jìn)程都有自己的獨(dú)立內(nèi)存空間,
不同進(jìn)程通過(guò)進(jìn)程間通信來(lái)通信。由于進(jìn)程比較重量,占據(jù)獨(dú)立的內(nèi)存,
所以上下文進(jìn)程間的切換開(kāi)銷(棧、寄存器、虛擬內(nèi)存、文件句柄等)比較大,但相對(duì)比較穩(wěn)定安全。
線程:
線程是進(jìn)程的一個(gè)實(shí)體,是 CPU 調(diào)度和分派的基本單位,
它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位.
線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),
但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源。
線程間通信主要通過(guò)共享內(nèi)存,上下文切換很快,資源開(kāi)銷較少,但相比進(jìn)程不夠穩(wěn)定容易丟失數(shù)據(jù)。
協(xié)程:
協(xié)程是一種用戶態(tài)的輕量級(jí)線程,協(xié)程的調(diào)度完全由用戶控制。
協(xié)程擁有自己的寄存器上下文和棧。
協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來(lái)的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧,
直接操作棧則基本沒(méi)有內(nèi)核切換的開(kāi)銷,可以不加鎖的訪問(wèn)全局變量,所以上下文的切換非常快。
區(qū)別: 進(jìn)程與線程比較: 線程是指進(jìn)程內(nèi)的一個(gè)執(zhí)行單元,也是進(jìn)程內(nèi)的可調(diào)度實(shí)體。線程與進(jìn)程的區(qū)別:
- 地址空間:線程是進(jìn)程內(nèi)的一個(gè)執(zhí)行單元,進(jìn)程內(nèi)至少有一個(gè)線程,它們共享進(jìn)程的地址空間,
而進(jìn)程有自己獨(dú)立的地址空間 - 資源擁有:進(jìn)程是資源分配和擁有的單位,同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程的資源
- 線程是處理器調(diào)度的基本單位,但進(jìn)程不是
- 二者均可并發(fā)執(zhí)行
- 每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口,
但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制
協(xié)程與線程進(jìn)行比較:
- 一個(gè)線程可以多個(gè)協(xié)程,一個(gè)進(jìn)程也可以單獨(dú)擁有多個(gè)協(xié)程,這樣 Python 中則能使用多核 CPU。
- 線程進(jìn)程都是同步機(jī)制,而協(xié)程則是異步
- 協(xié)程能保留上一次調(diào)用時(shí)的狀態(tài),每次過(guò)程重入時(shí),就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài)
2. 什么是并發(fā)和并行?
"并行是指同一時(shí)刻同時(shí)做多件事情,而并發(fā)是指同一時(shí)間間隔內(nèi)做多件事情”。
并發(fā)與并行是兩個(gè)既相似而又不相同的概念:并發(fā)性,又稱共行性,是指能處理多個(gè)同時(shí)性活動(dòng)的能力;并行是指同時(shí)發(fā)生的兩個(gè)并發(fā)事件,具有并發(fā)的含義,而并發(fā)則不一定并行,也亦是說(shuō)并發(fā)事件之間不一定要同一時(shí)刻發(fā)生。
并發(fā)的實(shí)質(zhì)是一個(gè)物理 CPU(也可以多個(gè)物理 CPU) 在若干道程序之間多路復(fù)用,并發(fā)性是對(duì)有限物理資源強(qiáng)制行使多用戶共享以提高效率。 并行性指兩個(gè)或兩個(gè)以上事件或活動(dòng)在同一時(shí)刻發(fā)生。在多道程序環(huán)境下,并行性使多個(gè)程序同一時(shí)刻可在不同 CPU 上同時(shí)執(zhí)行。
并行,是每個(gè) CPU 運(yùn)行一個(gè)程序。
3. 解釋什么是異步非阻塞?
異步 異步與同步相對(duì),當(dāng)一個(gè)異步過(guò)程調(diào)用發(fā)出后,調(diào)用者在沒(méi)有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作。當(dāng)這個(gè)調(diào)用完成后,一般通過(guò)狀態(tài)、通知和回調(diào)來(lái)通知調(diào)用者。對(duì)于異步調(diào)用,調(diào)用的返回并不受調(diào)用者控制。
非阻塞 非阻塞是這樣定義的,當(dāng)線程遇到 I/O 操作時(shí),不會(huì)以阻塞的方式等待 I/O 操作的完成或數(shù)據(jù)的返回,而只是將 I/O 請(qǐng)求發(fā)送給操作系統(tǒng),繼續(xù)執(zhí)行下一條語(yǔ)句。當(dāng)操作系統(tǒng)完成 I/O 操作時(shí),以事件的形式通知執(zhí)行 I/O 操作的線程,線程會(huì)在特定時(shí)候處理這個(gè)事件。簡(jiǎn)答理解就是如果程序不會(huì)卡住,可以繼續(xù)執(zhí)行,就是說(shuō)非阻塞的。
4. threading.local 的作用?
threading.local()這個(gè)方法是用來(lái)保存一個(gè)全局變量,但是這個(gè)全局變量只有在當(dāng)前線程才能訪問(wèn),如果你在開(kāi)發(fā)多線程應(yīng)用的時(shí)候,需要每個(gè)線程保存一個(gè)單獨(dú)的數(shù)據(jù)供當(dāng)前線程操作,可以考慮使用這個(gè)方法,簡(jiǎn)單有效。代碼示例
import threading
import timea = threading.local()#全局對(duì)象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函数、静态方法、类方法、实例方法、分布式锁、的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: sklearn FutureWarnin
- 下一篇: 女性不孕的常见症状是什么