以下內容出自小程式「程式設計面試題庫」,本文首發於公眾號「zone7」
0 遇到過得反爬蟲策略以及解決方法?
1.通過headers反爬蟲 2.基於使用者行為的發爬蟲:(同一IP短時間內訪問的頻率) 3.動態網頁反爬蟲(通過ajax請求資料,或者通過JavaScript生成) 4.對部分資料進行加密處理的(資料是亂碼)
解決方法:
對於基本網頁的抓取可以自定義headers,新增headers的資料 使用多個代理ip進行抓取或者設定抓取的頻率降低一些, 動態網頁的可以使用selenium + phantomjs 進行抓取 對部分資料進行加密的,可以使用selenium進行截圖,使用python自帶的pytesseract庫進行識別,但是比較慢最直接的方法是找到加密的方法進行逆向推理。
1 urllib 和 urllib2 的區別?
- urllib 和urllib2都是接受URL請求的相關模組,但是urllib2可以接受一個Request類的例項來設定URL請求的headers,urllib僅可以接受URL。urllib不可以偽裝你的User-Agent字串。
- urllib提供urlencode()方法用來GET查詢字串的產生,而urllib2沒有。這是為何urllib常和urllib2一起使用的原因。
2 列舉網路爬蟲所用到的網路資料包,解析包?
- 網路資料包 urllib、urllib2、requests
- 解析包 re、xpath、beautiful soup、lxml
3 簡述一下爬蟲的步驟?
- 確定需求;
- 確定資源;
- 通過url獲取網站的返回資料;
- 定位資料;
- 儲存資料。
4 遇到反爬機制怎麼處理?
反爬機制:
headers方向 判斷User-Agent、判斷Referer、判斷Cookie。 將瀏覽器的headers資訊全部新增進去 注意:Accept-Encoding;gzip,deflate需要註釋掉
5 常見的HTTP方法有哪些?
- GET:請求指定的頁面資訊,返回實體主體;
- HEAD:類似於get請求,只不過返回的響應中沒有具體的內容,用於捕獲報頭;
- POST:向指定資源提交資料進行處理請求(比如表單提交或者上傳檔案),。資料被包含在請求體中。
- PUT:從客戶端向服務端傳送資料取代指定的文件的內容;
- DELETE:請求刪除指定的頁面;
- CONNNECT:HTTP1.1協議中預留給能夠將連線方式改為管道方式的代理伺服器;
- OPTIONS:允許客戶端檢視伺服器的效能; TRACE:回顯伺服器的請求,主要用於測試或者診斷。
6 說一說redis-scrapy中redis的作用?
它是將scrapy框架中Scheduler替換為redis資料庫,實現佇列管理共享。
優點:
- 可以充分利用多臺機器的頻寬;
- 可以充分利用多臺機器的IP地址。
7 遇到的反爬蟲策略以及解決方法?
- 通過headers反爬蟲:自定義headers,新增網頁中的headers資料。
- 基於使用者行為的反爬蟲(封IP):可以使用多個代理IP爬取或者將爬取的頻率降低。
- 動態網頁反爬蟲(JS或者Ajax請求資料):動態網頁可以使用 selenium + phantomjs 抓取。
- 對部分資料加密處理(資料亂碼):找到加密方法進行逆向推理。
8 如果讓你來防範網站爬蟲,你應該怎麼來提高爬取的難度 ?
- 判斷headers的User-Agent;
- 檢測同一個IP的訪問頻率;
- 資料通過Ajax獲取;
- 爬取行為是對頁面的原始檔爬取,如果要爬取靜態網頁的html程式碼,可以使用jquery去模仿寫html。
9 scrapy分為幾個組成部分?分別有什麼作用?
分為5個部分;Spiders(爬蟲類),Scrapy Engine(引擎),Scheduler(排程器),Downloader(下載器),Item Pipeline(處理管道)。
- Spiders:開發者自定義的一個類,用來解析網頁並抓取指定url返回的內容。
- Scrapy Engine:控制整個系統的資料處理流程,並進行事務處理的觸發。
- Scheduler:接收Engine發出的requests,並將這些requests放入到處理列隊中,以便之後engine需要時再提供。
- Download:抓取網頁資訊提供給engine,進而轉發至Spiders。
- Item Pipeline:負責處理Spiders類提取之後的資料。 比如清理HTML資料、驗證爬取的資料(檢查item包含某些欄位)、查重(並丟棄)、將爬取結果儲存到資料庫中
10 簡述一下scrapy的基本流程?
scrapy分為9個步驟:
- Spiders需要初始的start_url或則函式stsrt_requests,會在內部生成Requests給Engine;
- Engine將requests傳送給Scheduler;
- Engine從Scheduler那獲取requests,交給Download下載;
- 在交給Dowmload過程中會經過Downloader Middlewares(經過process_request函式);
- Dowmloader下載頁面後生成一個response,這個response會傳給Engine,這個過程中又經過了Downloader Middlerwares(經過process_request函式),在傳送中出錯的話經過process_exception函式;
- Engine將從Downloader那傳送過來的response傳送給Spiders處理,這個過程經過Spiders Middlerwares(經過process_spider_input函式);
- Spiders處理這個response,返回Requests或者Item兩個型別,傳給Engine,這個過程又經過Spiders Middlewares(經過porcess_spider_output函式);
- Engine接收返回的資訊,如果使Item,將它傳給Items Pipeline中;如果是Requests,將它傳給Scheduler,繼續爬蟲;
- 重複第三步,直至沒有任何需要爬取的資料
11 python3.5語言中enumerate的意思是
對於一個可迭代的(iterable)/可遍歷的物件(如列表、字串),enumerate將其組成一個索引序列,利用它可以同時獲得索引和值 enumerate多用於在for迴圈中得到計數
12 你是否瞭解谷歌的無頭瀏覽器?
無頭瀏覽器即headless browser,是一種沒有介面的瀏覽器。既然是瀏覽器那麼瀏覽器該有的東西它都應該有,只是看不到介面而已。
Python中selenium模組中的PhantomJS即為無介面瀏覽器(無頭瀏覽器):是基於QtWebkit的無頭瀏覽器。
13 scrapy和scrapy-redis的區別?
scrapy是一個爬蟲通用框架,但不支援分散式,scrapy-redis是為了更方便的實現scrapy分散式爬蟲,而提供了一些以redis為基礎的元件
為什麼會選擇redis資料庫?
因為redis支援主從同步,而且資料都是快取在記憶體中,所以基於redis的分散式爬蟲,對請求和資料的高頻讀取效率非常高
什麼是主從同步?
在Redis中,使用者可以通過執行SLAVEOF命令或者設定slaveof選項,讓一個伺服器去複製(replicate)另一個伺服器,我們稱呼被複制的伺服器為主伺服器(master),而對主伺服器進行復制的伺服器則被稱為從伺服器(slave),當客戶端向從伺服器傳送SLAVEOF命令,要求從伺服器複製主伺服器時,從伺服器首先需要執行同步操作,也即是,將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態
14 scrapy的優缺點?為什麼要選擇scrapy框架?
優點:
採取可讀性更強的xpath代替正則 強大的統計和log系統 同時在不同的url上爬行 支援shell方式,方便獨立除錯 寫middleware,方便寫一些統一的過濾器 通過管道的方式存入資料庫
缺點:
基於python爬蟲框架,擴充套件性比較差,基於twisted框架,執行中exception是不會幹掉reactor,並且非同步框架出錯後是不會停掉其他任務的,資料出錯後難以察覺
15 scrapy和requests的使用情況?
requests 是 polling 方式的,會被網路阻塞,不適合爬取大量資料
scapy 底層是非同步框架 twisted ,併發是最大優勢
16 描述一下scrapy框架的執行機制?
從start_urls裡面獲取第一批url傳送請求,請求由請求引擎給排程器入請求對列,獲取完畢後,排程器將請求對列交給下載器去獲取請求對應的響應資源,並將響應交給自己編寫的解析方法做提取處理,如果提取出需要的資料,則交給管道處理,如果提取出url,則繼續執行之前的步驟,直到多列裡沒有請求,程式結束。
17 寫爬蟲使用多程式好,還是用多執行緒好?
IO密集型程式碼(檔案處理、網路爬蟲等),多執行緒能夠有效提升效率(單執行緒下有IO操作會進行IO等待,造成不必要的時間浪費,而開啟多執行緒能線上程A等待時,自動切換到執行緒B,可以不浪費CPU的資源,從而能提升程式執行效率)。在實際的資料採集過程中,既考慮網速和響應的問題,也需要考慮自身機器的硬體情況,來設定多程式或多執行緒
18 常見的反爬蟲和應對方法?
- 基於使用者行為,同一個ip段時間多次訪問同一頁面 利用代理ip,構建ip池
- 請求頭裡的user-agent 構建user-agent池(作業系統、瀏覽器不同,模擬不同使用者)
- 動態載入(抓到的資料和瀏覽器顯示的不一樣),js渲染 模擬ajax請求,返回json形式的資料
- selenium / webdriver 模擬瀏覽器載入
- 對抓到的資料進行分析
- 加密引數欄位 會話跟蹤【cookie】 防盜鏈設定【Referer
19 分散式爬蟲主要解決什麼問題?
面對海量待抓取網頁,只有採用分散式架構,才有可能在較短時間內完成一輪抓取工作。
它的開發效率是比較快而且簡單的。
20 如何提高爬取效率?
爬蟲下載慢主要原因是阻塞等待發往網站的請求和網站返回
1,採用非同步與多執行緒,擴大電腦的cpu利用率;
2,採用訊息佇列模式
3,提高頻寬
複製程式碼
21 說說什麼是爬蟲協議?
Robots協議(也稱為爬蟲協議、爬蟲規則、機器人協議等)也就是robots.txt,網站通過robots協議告訴搜尋引擎哪些頁面可以抓取,哪些頁面不能抓取。
Robots協議是網站國際網際網路界通行的道德規範,其目的是保護網站資料和敏感資訊、確保使用者個人資訊和隱私不被侵犯。因其不是命令,故需要搜尋引擎自覺遵守。
22 如果對方網站反爬取,封IP了怎麼辦?
- 放慢抓取熟速度,減小對目標網站造成的壓力,但是這樣會減少單位時間內的資料抓取量
- 使用代理IP(免費的可能不穩定,收費的可能不划算)
23 有一個jsonline格式的檔案file
def get_lines():
with open('file.txt','rb') as f:
return f.readlines()
if __name__ == '__main__':
for e in get_lines():
process(e) # 處理每一行資料
複製程式碼
現在要處理一個大小為10G的檔案,但是記憶體只有4G,如果在只修改get_lines 函式而其他程式碼保持不變的情況下,應該如何實現?需要考慮的問題都有那些?
def get_lines():
with open('file.txt','rb') as f:
for i in f:
yield i
複製程式碼
Pandaaaa906提供的方法
from mmap import mmap
def get_lines(fp):
with open(fp,"r+") as f:
m = mmap(f.fileno(), 0)
tmp = 0
for i, char in enumerate(m):
if char==b"\n":
yield m[tmp:i+1].decode()
tmp = i+1
if __name__=="__main__":
for i in get_lines("fp_some_huge_file"):
print(i)
複製程式碼
要考慮的問題有:記憶體只有4G無法一次性讀入10G檔案,需要分批讀入分批讀入資料要記錄每次讀入資料的位置。分批每次讀取資料的大小,太小會在讀取操作花費過多時間。 stackoverflow.com/questions/3…
24 補充缺失的程式碼
def print_directory_contents(sPath):
"""
這個函式接收資料夾的名稱作為輸入引數
返回該資料夾中檔案的路徑
以及其包含資料夾中檔案的路徑
"""
import os
for s_child in os.listdir(s_path):
s_child_path = os.path.join(s_path, s_child)
if os.path.isdir(s_child_path):
print_directory_contents(s_child_path)
else:
print(s_child_path)
複製程式碼
25 輸入日期, 判斷這一天是這一年的第幾天?
import datetime
def dayofyear():
year = input("請輸入年份: ")
month = input("請輸入月份: ")
day = input("請輸入天: ")
date1 = datetime.date(year=int(year),month=int(month),day=int(day))
date2 = datetime.date(year=int(year),month=1,day=1)
return (date1-date2).days+1
複製程式碼
26 打亂一個排好序的list物件alist?
import random
alist = [1,2,3,4,5]
random.shuffle(alist)
print(alist)
複製程式碼
27 現有字典 d= {'a':24,'g':52,'i':12,'k':33}請按value值進行排序?
sorted(d.items(),key=lambda x:x[1])
複製程式碼
28 字典推導式
d = {key:value for (key,value) in iterable}
複製程式碼
29 請反轉字串 "aStr"?
print("aStr"[::-1])
複製程式碼
30 將字串 "k:1 |k1:2|k2:3|k3:4",處理成字典 {k:1,k1:2,
str1 = "k:1|k1:2|k2:3|k3:4"
def str2dict(str1):
dict1 = {}
for iterms in str1.split('|'):
key,value = iterms.split(':')
dict1[key] = value
return dict1
#字典推導式
d = {k:int(v) for t in str1.split("|") for k, v in (t.split(":"), )}
複製程式碼
31 請按alist中元素的age由大到小排序
alist = [{'name':'a','age':20},{'name':'b','age':30},{'name':'c','age':25}]
def sort_by_age(list1):
return sorted(alist,key=lambda x:x['age'],reverse=True)
複製程式碼
32 下面程式碼的輸出結果將是什麼?
list = ['a','b','c','d','e']
print(list[10:])
複製程式碼
程式碼將輸出[],不會產生IndexError錯誤,就像所期望的那樣,嘗試用超出成員的個數的index來獲取某個列表的成員。例如,嘗試獲取list[10]和之後的成員,會導致IndexError。然而,嘗試獲取列表的切片,開始的index超過了成員個數不會產生IndexError,而是僅僅返回一個空列表。這成為特別讓人噁心的疑難雜症,因為執行的時候沒有錯誤產生,導致Bug很難被追蹤到。
33 寫一個列表生成式,產生一個公差為11的等差數列
print([x*11 for x in range(10)])
複製程式碼
34 給定兩個列表,怎麼找出他們相同的元素和不同的元素?
list1 = [1,2,3]
list2 = [3,4,5]
set1 = set(list1)
set2 = set(list2)
print(set1 & set2)
print(set1 ^ set2)
複製程式碼
35 請寫出一段python程式碼實現刪除list裡面的重複元素?
l1 = ['b','c','d','c','a','a']
l2 = list(set(l1))
print(l2)
複製程式碼
用list類的sort方法:
l1 = ['b','c','d','c','a','a']
l2 = list(set(l1))
l2.sort(key=l1.index)
print(l2)
複製程式碼
也可以這樣寫:
l1 = ['b','c','d','c','a','a']
l2 = sorted(set(l1),key=l1.index)
print(l2)
複製程式碼
也可以用遍歷:
l1 = ['b','c','d','c','a','a']
l2 = []
for i in l1:
if not i in l2:
l2.append(i)
print(l2)
複製程式碼
36 給定兩個list A,B ,請用找出A,B中相同與不同的元素
A,B 中相同元素: print(set(A)&set(B))
A,B 中不同元素: print(set(A)^set(B))
複製程式碼
37 python新式類和經典類的區別?
a. 在python裡凡是繼承了object的類,都是新式類
b. Python3裡只有新式類
c. Python2裡面繼承object的是新式類,沒有寫父類的是經典類
d. 經典類目前在Python裡基本沒有應用
38 python中內建的資料結構有幾種?
a. 整型 int、 長整型 long、浮點型 float、 複數 complex
b. 字串 str、 列表 list、 元祖 tuple
c. 字典 dict 、 集合 set
d. Python3 中沒有 long,只有無限精度的 int
39 python如何實現單例模式?請寫出兩種實現方式?
第一種方法:使用裝飾器
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class Foo(object):
pass
foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2) # True
複製程式碼
第二種方法:使用基類 New 是真正建立例項物件的方法,所以重寫基類的new 方法,以此保證建立物件的時候只生成一個例項
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
class Foo(Singleton):
pass
foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2) # True
複製程式碼
第三種方法:元類,元類是用於建立類物件的類,類物件建立例項物件時一定要呼叫call方法,因此在呼叫call時候保證始終只建立一個例項即可,type是python的元類
class Singleton(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
# Python2
class Foo(object):
__metaclass__ = Singleton
# Python3
class Foo(metaclass=Singleton):
pass
foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2) # True
複製程式碼
40 反轉一個整數,例如-123 --> -321
class Solution(object):
def reverse(self,x):
if -10<x<10:
return x
str_x = str(x)
if str_x[0] !="-":
str_x = str_x[::-1]
x = int(str_x)
else:
str_x = str_x[1:][::-1]
x = int(str_x)
x = -x
return x if -2147483648<x<2147483647 else 0
if __name__ == '__main__':
s = Solution()
reverse_int = s.reverse(-120)
print(reverse_int)
複製程式碼
41 設計實現遍歷目錄與子目錄,抓取.pyc檔案?
第一種方法:
import os
def get_files(dir,suffix):
res = []
for root,dirs,files in os.walk(dir):
for filename in files:
name,suf = os.path.splitext(filename)
if suf == suffix:
res.append(os.path.join(root,filename))
print(res)
get_files("./",'.pyc')
複製程式碼
第二種方法:
import os
def pick(obj):
if ob.endswith(".pyc"):
print(obj)
def scan_path(ph):
file_list = os.listdir(ph)
for obj in file_list:
if os.path.isfile(obj):
pick(obj)
elif os.path.isdir(obj):
scan_path(obj)
if __name__=='__main__':
path = input('輸入目錄')
scan_path(path)
複製程式碼
第三種方法
from glob import iglob
def func(fp, postfix):
for i in iglob(f"{fp}/**/*{postfix}", recursive=True):
print(i)
if __name__ == "__main__":
postfix = ".pyc"
func("K:\Python_script", postfix)
複製程式碼
42 Python-遍歷列表時刪除元素的正確做法
遍歷在新在列表操作,刪除時在原來的列表操作
a = [1,2,3,4,5,6,7,8]
print(id(a))
print(id(a[:]))
for i in a[:]:
if i>5:
pass
else:
a.remove(i)
print(a)
print('-----------')
print(id(a))
複製程式碼
#filter
a=[1,2,3,4,5,6,7,8]
b = filter(lambda x: x>5,a)
print(list(b))
複製程式碼
列表解析
a=[1,2,3,4,5,6,7,8]
b = [i for i in a if i>5]
print(b)
複製程式碼
倒序刪除 因為列表總是‘向前移’,所以可以倒序遍歷,即使後面的元素被修改了,還沒有被遍歷的元素和其座標還是保持不變的
a=[1,2,3,4,5,6,7,8]
print(id(a))
for i in range(len(a)-1,-1,-1):
if a[i]>5:
pass
else:
a.remove(a[i])
print(id(a))
print('-----------')
print(a)
複製程式碼
43 字串的操作題目
全字母短句 PANGRAM 是包含所有英文字母的句子,比如:A QUICK BROWN FOX JUMPS OVER THE LAZY DOG. 定義並實現一個方法 get_missing_letter, 傳入一個字串採納數,返回引數字串變成一個 PANGRAM 中所缺失的字元。應該忽略傳入字串引數中的大小寫,返回應該都是小寫字元並按字母順序排序(請忽略所有非 ACSII 字元)
下面示例是用來解釋,雙引號不需要考慮:
(0)輸入: "A quick brown for jumps over the lazy dog"
返回: ""
(1)輸入: "A slow yellow fox crawls under the proactive dog"
返回: "bjkmqz"
(2)輸入: "Lions, and tigers, and bears, oh my!"
返回: "cfjkpquvwxz"
(3)輸入: ""
返回:"abcdefghijklmnopqrstuvwxyz"
def get_missing_letter(a):
s1 = set("abcdefghijklmnopqrstuvwxyz")
s2 = set(a)
ret = "".join(sorted(s1-s2))
return ret
print(get_missing_letter("python"))
複製程式碼
44 可變型別和不可變型別
1,可變型別有list,dict.不可變型別有string,number,tuple.
2,當進行修改操作時,可變型別傳遞的是記憶體中的地址,也就是說,直接修改記憶體中的值,並沒有開闢新的記憶體。
3,不可變型別被改變時,並沒有改變原記憶體地址中的值,而是開闢一塊新的記憶體,將原地址中的值複製過去,對這塊新開闢的記憶體中的值進行操作。
45 is和==有什麼區別?
is:比較的是兩個物件的id值是否相等,也就是比較倆物件是否為同一個例項物件。是否指向同一個記憶體地址
== : 比較的兩個物件的內容/值是否相等,預設會呼叫物件的eq()方法
46 求出列表所有奇數並構造新列表
a = [1,2,3,4,5,6,7,8,9,10]
res = [ i for i in a if i%2==1]
print(res)
複製程式碼
47 用一行python程式碼寫出1+2+3+10248
from functools import reduce
#1.使用sum內建求和函式
num = sum([1,2,3,10248])
print(num)
#2.reduce 函式
num1 = reduce(lambda x,y :x+y,[1,2,3,10248])
print(num1)
複製程式碼
48 Python中變數的作用域?(變數查詢順序)
函式作用域的LEGB順序
1.什麼是LEGB?
L: local 函式內部作用域
E: enclosing 函式內部與內嵌函式之間
G: global 全域性作用域
B: build-in 內建作用
python在函式裡面的查詢分為4種,稱之為LEGB,也正是按照這是順序來查詢的
49 字串 "123"
轉換成 123
,不使用內建api,例如 int()
方法一: 利用 str
函式
def atoi(s):
num = 0
for v in s:
for j in range(10):
if v == str(j):
num = num * 10 + j
return num
複製程式碼
方法二: 利用 ord
函式
def atoi(s):
num = 0
for v in s:
num = num * 10 + ord(v) - ord('0')
return num
複製程式碼
方法三: 利用 eval
函式
def atoi(s):
num = 0
for v in s:
t = "%s * 1" % v
n = eval(t)
num = num * 10 + n
return num
複製程式碼
方法四: 結合方法二,使用 reduce
,一行解決
from functools import reduce
def atoi(s):
return reduce(lambda num, v: num * 10 + ord(v) - ord('0'), s, 0)
複製程式碼
50 Given an array of integers
給定一個整數陣列和一個目標值,找出陣列中和為目標值的兩個數。你可以假設每個輸入只對應一種答案,且同樣的元素不能被重複利用。示例:給定nums = [2,7,11,15],target=9 因為 nums[0]+nums[1] = 2+7 =9,所以返回[0,1]
class Solution:
def twoSum(self,nums,target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
d = {}
size = 0
while size < len(nums):
if target-nums[size] in d:
if d[target-nums[size]] <size:
return [d[target-nums[size]],size]
else:
d[nums[size]] = size
size = size +1
solution = Solution()
list = [2,7,11,15]
target = 9
nums = solution.twoSum(list,target)
print(nums)
複製程式碼
給列表中的字典排序:假設有如下list物件,alist=[{"name":"a","age":20},{"name":"b","age":30},{"name":"c","age":25}],將alist中的元素按照age從大到小排序 alist=[{"name":"a","age":20},{"name":"b","age":30},{"name":"c","age":25}]
alist_sort = sorted(alist,key=lambda e: e.__getitem__('age'),reverse=True)
複製程式碼
51 python程式碼實現刪除一個list裡面的重複元素
def distFunc1(a):
"""使用集合去重"""
a = list(set(a))
print(a)
def distFunc2(a):
"""將一個列表的資料取出放到另一個列表中,中間作判斷"""
list = []
for i in a:
if i not in list:
list.append(i)
#如果需要排序的話用sort
list.sort()
print(list)
def distFunc3(a):
"""使用字典"""
b = {}
b = b.fromkeys(a)
c = list(b.keys())
print(c)
if __name__ == "__main__":
a = [1,2,4,2,4,5,7,10,5,5,7,8,9,0,3]
distFunc1(a)
distFunc2(a)
distFunc3(a)
複製程式碼
52 統計一個文字中單詞頻次最高的10個單詞?
import re
# 方法一
def test(filepath):
distone = {}
with open(filepath) as f:
for line in f:
line = re.sub("\W+", " ", line)
lineone = line.split()
for keyone in lineone:
if not distone.get(keyone):
distone[keyone] = 1
else:
distone[keyone] += 1
num_ten = sorted(distone.items(), key=lambda x:x[1], reverse=True)[:10]
num_ten =[x[0] for x in num_ten]
return num_ten
# 方法二
# 使用 built-in 的 Counter 裡面的 most_common
import re
from collections import Counter
def test2(filepath):
with open(filepath) as f:
return list(map(lambda c: c[0], Counter(re.sub("\W+", " ", f.read()).split()).most_common(10)))
複製程式碼
53 請寫出一個函式滿足以下條件
該函式的輸入是一個僅包含數字的list,輸出一個新的list,其中每一個元素要滿足以下條件:
1、該元素是偶數
2、該元素在原list中是在偶數的位置(index是偶數)
def num_list(num):
return [i for i in num if i %2 ==0 and num.index(i)%2==0]
num = [0,1,2,3,4,5,6,7,8,9,10]
result = num_list(num)
print(result)
複製程式碼
54 使用單一的列表生成式來產生一個新的列表
該列表只包含滿足以下條件的值,元素為原始列表中偶數切片
list_data = [1,2,5,8,10,3,18,6,20]
res = [x for x in list_data[::2] if x %2 ==0]
print(res)
複製程式碼
55 用一行程式碼生成[1,4,9,16,25,36,49,64,81,100]
[x * x for x in range(1,11)]
複製程式碼
56 輸入某年某月某日,判斷這一天是這一年的第幾天?
import datetime
y = int(input("請輸入4位數字的年份:"))
m = int(input("請輸入月份:"))
d = int(input("請輸入是哪一天"))
targetDay = datetime.date(y,m,d)
dayCount = targetDay - datetime.date(targetDay.year -1,12,31)
print("%s是 %s年的第%s天。"%(targetDay,y,dayCount.days))
複製程式碼
57 兩個有序列表,l1,l2,對這兩個列表進行合併不可使用extend
def loop_merge_sort(l1,l2):
tmp = []
while len(l1)>0 and len(l2)>0:
if l1[0] <l2[0]:
tmp.append(l1[0])
del l1[0]
else:
tmp.append(l2[0])
del l2[0]
while len(l1)>0:
tmp.append(l1[0])
del l1[0]
while len(l2)>0:
tmp.append(l2[0])
del l2[0]
return tmp
複製程式碼
58 給定一個任意長度陣列,實現一個函式
讓所有奇數都在偶數前面,而且奇數升序排列,偶數降序排序,如字串'1982376455',變成'1355798642'
# 方法一
def func1(l):
if isinstance(l, str):
l = [int(i) for i in l]
l.sort(reverse=True)
for i in range(len(l)):
if l[i] % 2 > 0:
l.insert(0, l.pop(i))
print(''.join(str(e) for e in l))
# 方法二
def func2(l):
print("".join(sorted(l, key=lambda x: int(x) % 2 == 0 and 20 - int(x) or int(x))))
複製程式碼
59 寫一個函式找出一個整數陣列中,第二大的數
def find_second_large_num(num_list):
"""
找出陣列第2大的數字
"""
# 方法一
# 直接排序,輸出倒數第二個數即可
tmp_list = sorted(num_list)
print("方法一\nSecond_large_num is :", tmp_list[-2])
# 方法二
# 設定兩個標誌位一個儲存最大數一個儲存次大數
# two 儲存次大值,one 儲存最大值,遍歷一次陣列即可,先判斷是否大於 one,若大於將 one 的值給 two,將 num_list[i] 的值給 one,否則比較是否大於two,若大於直接將 num_list[i] 的值給two,否則pass
one = num_list[0]
two = num_list[0]
for i in range(1, len(num_list)):
if num_list[i] > one:
two = one
one = num_list[i]
elif num_list[i] > two:
two = num_list[i]
print("方法二\nSecond_large_num is :", two)
# 方法三
# 用 reduce 與邏輯符號 (and, or)
# 基本思路與方法二一樣,但是不需要用 if 進行判斷。
from functools import reduce
num = reduce(lambda ot, x: ot[1] < x and (ot[1], x) or ot[0] < x and (x, ot[1]) or ot, num_list, (0, 0))[0]
print("方法三\nSecond_large_num is :", num)
if __name__ == '__main___':
num_list = [34, 11, 23, 56, 78, 0, 9, 12, 3, 7, 5]
find_second_large_num(num_list)
複製程式碼
60 閱讀一下程式碼他們的輸出結果是什麼?
def multi():
return [lambda x : i*x for i in range(4)]
print([m(3) for m in multi()])
複製程式碼
正確答案是[9,9,9,9],而不是[0,3,6,9]產生的原因是Python的閉包的後期繫結導致的,這意味著在閉包中的變數是在內部函式被呼叫的時候被查詢的,因為,最後函式被呼叫的時候,for迴圈已經完成, i 的值最後是3,因此每一個返回值的i都是3,所以最後的結果是[9,9,9,9]
61 統計一段字串中字元出現的次數
# 方法一
def count_str(str_data):
"""定義一個字元出現次數的函式"""
dict_str = {}
for i in str_data:
dict_str[i] = dict_str.get(i, 0) + 1
return dict_str
dict_str = count_str("AAABBCCAC")
str_count_data = ""
for k, v in dict_str.items():
str_count_data += k + str(v)
print(str_count_data)
# 方法二
from collections import Counter
print("".join(map(lambda x: x[0] + str(x[1]), Counter("AAABBCCAC").most_common())))
複製程式碼
62 Python中類方法、類例項方法、靜態方法有何區別?
類方法: 是類物件的方法,在定義時需要在上方使用 @classmethod 進行裝飾,形參為cls,表示類物件,類物件和例項物件都可呼叫
類例項方法: 是類例項化物件的方法,只有例項物件可以呼叫,形參為self,指代物件本身;
靜態方法: 是一個任意函式,在其上方使用 @staticmethod 進行裝飾,可以用物件直接呼叫,靜態方法實際上跟該類沒有太大關係
63 遍歷一個object的所有屬性,並print每一個屬性名?
class Car:
def __init__(self,name,loss): # loss [價格,油耗,公里數]
self.name = name
self.loss = loss
def getName(self):
return self.name
def getPrice(self):
# 獲取汽車價格
return self.loss[0]
def getLoss(self):
# 獲取汽車損耗值
return self.loss[1] * self.loss[2]
Bmw = Car("寶馬",[60,9,500]) # 例項化一個寶馬車物件
print(getattr(Bmw,"name")) # 使用getattr()傳入物件名字,屬性值。
print(dir(Bmw)) # 獲Bmw所有的屬性和方法
複製程式碼
64 寫一個類,並讓它儘可能多的支援操作符?
class Array:
__list = []
def __init__(self):
print "constructor"
def __del__(self):
print "destruct"
def __str__(self):
return "this self-defined array class"
def __getitem__(self,key):
return self.__list[key]
def __len__(self):
return len(self.__list)
def Add(self,value):
self.__list.append(value)
def Remove(self,index):
del self.__list[index]
def DisplayItems(self):
print "show all items---"
for item in self.__list:
print item
複製程式碼
65 關於Python記憶體管理,下列說法錯誤的是 B
A,變數不必事先宣告 B,變數無須先建立和賦值而直接使用
C,變數無須指定型別 D,可以使用del釋放資源
66 Python的記憶體管理機制及調優手段?
記憶體管理機制: 引用計數、垃圾回收、記憶體池
引用計數:引用計數是一種非常高效的記憶體管理手段,當一個Python物件被引用時其引用計數增加1,
當其不再被一個變數引用時則計數減1,當引用計數等於0時物件被刪除。弱引用不會增加引用計數
垃圾回收:
1.引用計數
引用計數也是一種垃圾收集機制,而且也是一種最直觀、最簡單的垃圾收集技術。當Python的某個物件的引用計數降為0時,說明沒有任何引用指向該物件,該物件就成為要被回收的垃圾了。比如某個新建物件,它被分配給某個引用,物件的引用計數變為1,如果引用被刪除,物件的引用計數為0,那麼該物件就可以被垃圾回收。不過如果出現迴圈引用的話,引用計數機制就不再起有效的作用了。
2.標記清除
調優手段
1.手動垃圾回收
2.調高垃圾回收閾值
3.避免迴圈引用
67 記憶體洩露是什麼?如何避免?
記憶體洩漏指由於疏忽或錯誤造成程式未能釋放已經不再使用的記憶體。記憶體洩漏並非指記憶體在物理上的消失,而是應用程式分配某段記憶體後,由於設計錯誤,導致在釋放該段記憶體之前就失去了對該段記憶體的控制,從而造成了記憶體的浪費。
有__del__()
函式的物件間的迴圈引用是導致記憶體洩露的主凶。不使用一個物件時使用: del object 來刪除一個物件的引用計數就可以有效防止記憶體洩露問題。
通過Python擴充套件模組gc 來檢視不能回收的物件的詳細資訊。
可以通過 sys.getrefcount(obj) 來獲取物件的引用計數,並根據返回值是否為0來判斷是否記憶體洩露
68 python常見的列表推導式?
[表示式 for 變數 in 列表] 或者 [表示式 for 變數 in 列表 if 條件]
69 簡述read、readline、readlines的區別?
read 讀取整個檔案
readline 讀取下一行
readlines 讀取整個檔案到一個迭代器以供我們遍歷
70 什麼是Hash(雜湊函式)?
雜湊函式(英語:Hash function)又稱雜湊演算法、雜湊函式,是一種從任何一種資料中建立小的數字“指紋”的方法。雜湊函式把訊息或資料壓縮成摘要,使得資料量變小,將資料的格式固定下來。該函式將資料打亂混合,重新建立一個叫做雜湊值(hash values,hash codes,hash sums,或hashes)的指紋。雜湊值通常用一個短的隨機字母和數字組成的字串來代表
71 python函式過載機制?
函式過載主要是為了解決兩個問題。 1。可變引數型別。 2。可變引數個數。
另外,一個基本的設計原則是,僅僅當兩個函式除了引數型別和引數個數不同以外,其功能是完全相同的,此時才使用函式過載,如果兩個函式的功能其實不同,那麼不應當使用過載,而應當使用一個名字不同的函式。
好吧,那麼對於情況 1 ,函式功能相同,但是引數型別不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何型別的引數,如果函式的功能相同,那麼不同的引數型別在 python 中很可能是相同的程式碼,沒有必要做成兩個不同函式。
那麼對於情況 2 ,函式功能相同,但引數個數不同,python 如何處理?大家知道,答案就是預設引數。對那些缺少的引數設定為預設引數即可解決問題。因為你假設函式功能相同,那麼那些缺少的引數終歸是需要用的。
好了,鑑於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函式過載了。
72 手寫一個判斷時間的裝飾器
import datetime
class TimeException(Exception):
def __init__(self, exception_info):
super().__init__()
self.info = exception_info
def __str__(self):
return self.info
def timecheck(func):
def wrapper(*args, **kwargs):
if datetime.datetime.now().year == 2019:
func(*args, **kwargs)
else:
raise TimeException("函式已過時")
return wrapper
@timecheck
def test(name):
print("Hello {}, 2019 Happy".format(name))
if __name__ == "__main__":
test("backbp")
複製程式碼
73 使用Python內建的filter()方法來過濾?
list(filter(lambda x: x % 2 == 0, range(10)))
複製程式碼
74 編寫函式的4個原則
1.函式設計要儘量短小
2.函式宣告要做到合理、簡單、易於使用
3.函式引數設計應該考慮向下相容
4.一個函式只做一件事情,儘量保證函式語句粒度的一致性
75 函式呼叫引數的傳遞方式是值傳遞還是引用傳遞?
Python的引數傳遞有:位置引數、預設引數、可變引數、關鍵字引數。
函式的傳值到底是值傳遞還是引用傳遞、要分情況:
不可變引數用值傳遞:像整數和字串這樣的不可變物件,是通過拷貝進行傳遞的,因為你無論如何都不可能在原處改變不可變物件。
可變引數是引用傳遞:比如像列表,字典這樣的物件是通過引用傳遞、和C語言裡面的用指標傳遞陣列很相似,可變物件能在函式內部改變。
76 如何在function裡面設定一個全域性變數
globals() # 返回包含當前作用餘全域性變數的字典。
global 變數 設定使用全域性變數
複製程式碼
77 對預設引數的理解 ?
預設引數指在呼叫函式的時候沒有傳入引數的情況下,呼叫預設的引數,在呼叫函式的同時賦值時,所傳入的引數會替代預設引數。
*args是不定長引數,它可以表示輸入引數是不確定的,可以是任意多個。
**kwargs是關鍵字引數,賦值的時候是以鍵值對的方式,引數可以是任意多對在定義函式的時候
不確定會有多少引數會傳入時,就可以使用兩個引數
78 帶引數的裝飾器?
帶定長引數的裝飾器
def new_func(func):
def wrappedfun(username, passwd):
if username == 'root' and passwd == '123456789':
print('通過認證')
print('開始執行附加功能')
return func()
else:
print('使用者名稱或密碼錯誤')
return
return wrappedfun
@new_func
def origin():
print('開始執行函式')
origin('root','123456789')
複製程式碼
帶不定長引數的裝飾器
def new_func(func):
def wrappedfun(*parts):
if parts:
counts = len(parts)
print('本系統包含 ', end='')
for part in parts:
print(part, ' ',end='')
print('等', counts, '部分')
return func()
else:
print('使用者名稱或密碼錯誤')
return func()
return wrappedfun
複製程式碼
79 為什麼函式名字可以當做引數用?
Python中一切皆物件,函式名是函式在記憶體中的空間,也是一個物件
80 Python中pass語句的作用是什麼?
在編寫程式碼時只寫框架思路,具體實現還未編寫就可以用pass進行佔位,是程式不報錯,不會進行任何操作。
81 有這樣一段程式碼,print c會輸出什麼,為什麼?
a = 10
b = 20
c = [a]
a = 15
複製程式碼
答:10對於字串,數字,傳遞是相應的值
82 交換兩個變數的值?
a, b = b, a
複製程式碼
83 map函式和reduce函式?
map(lambda x: x * x, [1, 2, 3, 4]) # 使用 lambda
# [1, 4, 9, 16]
reduce(lambda x, y: x * y, [1, 2, 3, 4]) # 相當於 ((1 * 2) * 3) * 4
# 24
複製程式碼
84 回撥函式,如何通訊的?
回撥函式是把函式的指標(地址)作為引數傳遞給另一個函式,將整個函式當作一個物件,賦值給呼叫的函式。
85 Python主要的內建資料型別都有哪些? print dir( ‘a ’) 的輸出?
內建型別:布林型別,數字,字串,列表,元組,字典,集合
輸出字串'a'的內建方法
86 map(lambda x:xx,[y for y in range(3)])的輸出?
[0, 1, 4]
複製程式碼
87 hasattr() getattr() setattr() 函式使用詳解?
hasattr(object,name)函式:
判斷一個物件裡面是否有name屬性或者name方法,返回bool值,有name屬性(方法)返回True,否則返回False。
class function_demo(object):
name = 'demo'
def run(self):
return "hello function"
functiondemo = function_demo()
res = hasattr(functiondemo, "name") # 判斷物件是否有name屬性,True
res = hasattr(functiondemo, "run") # 判斷物件是否有run方法,True
res = hasattr(functiondemo, "age") # 判斷物件是否有age屬性,False
print(res)
複製程式碼
getattr(object, name[,default])函式:
獲取物件object的屬性或者方法,如果存在則列印出來,如果不存在,列印預設值,預設值可選。注意:如果返回的是物件的方法,則列印結果是:方法的記憶體地址,如果需要執行這個方法,可以在後面新增括號().
functiondemo = function_demo()
getattr(functiondemo, "name")# 獲取name屬性,存在就列印出來 --- demo
getattr(functiondemo, "run") # 獲取run 方法,存在列印出方法的記憶體地址
getattr(functiondemo, "age") # 獲取不存在的屬性,報錯
getattr(functiondemo, "age", 18)# 獲取不存在的屬性,返回一個預設值
複製程式碼
setattr(object, name, values)函式:
給物件的屬性賦值,若屬性不存在,先建立再賦值
class function_demo(object):
name = "demo"
def run(self):
return "hello function"
functiondemo = function_demo()
res = hasattr(functiondemo, "age") # 判斷age屬性是否存在,False
print(res)
setattr(functiondemo, "age", 18) # 對age屬性進行賦值,無返回值
res1 = hasattr(functiondemo, "age") # 再次判斷屬性是否存在,True
複製程式碼
綜合使用
class function_demo(object):
name = "demo"
def run(self):
return "hello function"
functiondemo = function_demo()
res = hasattr(functiondemo, "addr") # 先判斷是否存在
if res:
addr = getattr(functiondemo, "addr")
print(addr)
else:
addr = getattr(functiondemo, "addr", setattr(functiondemo, "addr", "北京首都"))
print(addr)
複製程式碼
88 一句話解決階乘函式?
reduce(lambda x,y : x*y,range(1,n+1))
複製程式碼
89 對設計模式的理解,簡述你瞭解的設計模式?
設計模式是經過總結,優化的,對我們經常會碰到的一些程式設計問題的可重用解決方案。一個設計模式並不像一個類或一個庫那樣能夠直接作用於我們的程式碼,反之,設計模式更為高階,它是一種必須在特定情形下實現的一種方法模板。 常見的是工廠模式和單例模式
90 請手寫一個單例
#python2
class A(object):
__instance = None
def __new__(cls,*args,**kwargs):
if cls.__instance is None:
cls.__instance = objecet.__new__(cls)
return cls.__instance
else:
return cls.__instance
複製程式碼
91 單例模式的應用場景有那些?
單例模式應用的場景一般發現在以下條件下: 資源共享的情況下,避免由於資源操作時導致的效能或損耗等,如日誌檔案,應用配置。 控制資源的情況下,方便資源之間的互相通訊。如執行緒池等,1,網站的計數器 2,應用配置 3.多執行緒池 4資料庫配置 資料庫連線池 5.應用程式的日誌應用...
92 用一行程式碼生成[1,4,9,16,25,36,49,64,81,100]
print([x*x for x in range(1, 11)])
複製程式碼
93 對裝飾器的理解,並寫出一個計時器記錄方法執行效能的裝飾器?
裝飾器本質上是一個callable object ,它可以讓其他函式在不需要做任何程式碼變動的前提下增加額外功能,裝飾器的返回值也是一個函式物件。
import time
from functools import wraps
def timeit(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.clock()
ret = func(*args, **kwargs)
end = time.clock()
print('used:',end-start)
return ret
return wrapper
@timeit
def foo():
print('in foo()'foo())
複製程式碼
94 解釋以下什麼是閉包?
在函式內部再定義一個函式,並且這個函式用到了外邊函式的變數,那麼將這個函式以及用到的一些變數稱之為閉包。
95 函式裝飾器有什麼作用?
裝飾器本質上是一個callable object,它可以在讓其他函式在不需要做任何程式碼的變動的前提下增加額外的功能。裝飾器的返回值也是一個函式的物件,它經常用於有切面需求的場景。比如:插入日誌,效能測試,事務處理,快取。許可權的校驗等場景,有了裝飾器就可以抽離出大量的與函式功能本身無關的雷同程式碼併發並繼續使用。 詳細參考:manjusaka.itscoder.com/2018/02/23/…
96 生成器,迭代器的區別?
迭代器是遵循迭代協議的物件。使用者可以使用 iter() 以從任何序列得到迭代器(如 list, tuple, dictionary, set 等)。另一個方法則是建立一個另一種形式的迭代器 —— generator 。要獲取下一個元素,則使用成員函式 next()(Python 2)或函式 next() function (Python 3) 。當沒有元素時,則引發 StopIteration 此例外。若要實現自己的迭代器,則只要實現 next()(Python 2)或 __next__
()( Python 3)
生成器(Generator),只是在需要返回資料的時候使用yield語句。每次next()被呼叫時,生成器會返回它脫離的位置(它記憶語句最後一次執行的位置和所有的資料值)
區別: 生成器能做到迭代器能做的所有事,而且因為自動建立iter()和next()方法,生成器顯得特別簡潔,而且生成器也是高效的,使用生成器表示式取代列表解析可以同時節省記憶體。除了建立和儲存程式狀態的自動方法,當發生器終結時,還會自動丟擲StopIteration異常。
97 X是什麼型別?
X= (i for i in range(10)) X是 generator型別
98 請用一行程式碼 實現將1-N 的整數列表以3為單位分組
N =100
print ([[x for x in range(1,100)] [i:i+3] for i in range(0,100,3)])
複製程式碼
99 Python中yield的用法?
yield就是儲存當前程式執行狀態。你用for迴圈的時候,每次取一個元素的時候就會計算一次。用yield的函式叫generator,和iterator一樣,它的好處是不用一次計算所有元素,而是用一次算一次,可以節省很多空間,generator每次計算需要上一次計算結果,所以用yield,否則一return,上次計算結果就沒了