2019 年經典Python 面試 100 問

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 簡述一下爬蟲的步驟?

  1. 確定需求;

  2. 確定資源;

  3. 通過url獲取網站的返回數據;

  4. 定位數據;

  5. 存儲數據。

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數據庫,實現隊列管理共享。

優點:

  1. 可以充分利用多臺機器的帶寬;

  2. 可以充分利用多臺機器的IP地址。

7 遇到的反爬蟲策略以及解決方法?

  1. 通過headers反爬蟲:自定義headers,添加網頁中的headers數據。

  2. 基於用戶行爲的反爬蟲(封IP):可以使用多個代理IP爬取或者將爬取的頻率降低。

  3. 動態網頁反爬蟲(JS或者Ajax請求數據):動態網頁可以使用 selenium + phantomjs 抓取。

  4. 對部分數據加密處理(數據亂碼):找到加密方法進行逆向推理。

8 如果讓你來防範網站爬蟲,你應該怎麼來提高爬取的難度 ?

  1. 判斷headers的User-Agent;

  2. 檢測同一個IP的訪問頻率;

  3. 數據通過Ajax獲取;

  4. 爬取行爲是對頁面的源文件爬取,如果要爬取靜態網頁的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個步驟:

  1. Spiders需要初始的start_url或則函數stsrt_requests,會在內部生成Requests給Engine;

  2. Engine將requests發送給Scheduler;

  3. Engine從Scheduler那獲取requests,交給Download下載;

  4. 在交給Dowmload過程中會經過Downloader Middlewares(經過process_request函數);

  5. Dowmloader下載頁面後生成一個response,這個response會傳給Engine,這個過程中又經過了Downloader Middlerwares(經過process_request函數),在傳送中出錯的話經過process_exception函數;

  6. Engine將從Downloader那傳送過來的response發送給Spiders處理,這個過程經過Spiders Middlerwares(經過process_spider_input函數);

  7. Spiders處理這個response,返回Requests或者Item兩個類型,傳給Engine,這個過程又經過Spiders Middlewares(經過porcess_spider_output函數);

  8. Engine接收返回的信息,如果使Item,將它傳給Items Pipeline中;如果是Requests,將它傳給Scheduler,繼續爬蟲;

  9. 重複第三步,直至沒有任何需要爬取的數據

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 常見的反爬蟲和應對方法?

  1. 基於用戶行爲,同一個ip段時間多次訪問同一頁面 利用代理ip,構建ip池

  2. 請求頭裏的user-agent 構建user-agent池(操作系統、瀏覽器不同,模擬不同用戶)

  3. 動態加載(抓到的數據和瀏覽器顯示的不一樣),js渲染 模擬ajax請求,返回json形式的數據

  4. selenium / webdriver 模擬瀏覽器加載

  5. 對抓到的數據進行分析

  6. 加密參數字段 會話跟蹤【cookie】 防盜鏈設置【Referer

19 分佈式爬蟲主要解決什麼問題?

面對海量待抓取網頁,只有採用分佈式架構,纔有可能在較短時間內完成一輪抓取工作。

它的開發效率是比較快而且簡單的。

20 如何提高爬取效率?

爬蟲下載慢主要原因是阻塞等待發往網站的請求和網站返回

    1,採用異步與多線程,擴大電腦的cpu利用率;

    2,採用消息隊列模式

    3,提高帶寬

21 說說什麼是爬蟲協議?

Robots協議(也稱爲爬蟲協議、爬蟲規則、機器人協議等)也就是robots.txt,網站通過robots協議告訴搜索引擎哪些頁面可以抓取,哪些頁面不能抓取。

Robots協議是網站國際互聯網界通行的道德規範,其目的是保護網站數據和敏感信息、確保用戶個人信息和隱私不被侵犯。因其不是命令,故需要搜索引擎自覺遵守。

22 如果對方網站反爬取,封IP了怎麼辦?

  1. 放慢抓取熟速度,減小對目標網站造成的壓力,但是這樣會減少單位時間內的數據抓取量

  2. 使用代理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文件,需要分批讀入分批讀入數據要記錄每次讀入數據的位置。分批每次讀取數據的大小,太小會在讀取操作花費過多時間。
https://stackoverflow.com/questions/30294146/python-fastest-way-to-process-large-file

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",處理成字典 

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,它可以在讓其他函數在不需要做任何代碼的變動的前提下增加額外的功能。裝飾器的返回值也是一個函數的對象,它經常用於有切面需求的場景。比如:插入日誌,性能測試,事務處理,緩存。權限的校驗等場景,有了裝飾器就可以抽離出大量的與函數功能本身無關的雷同代碼併發並繼續使用。
詳細參考:https://manjusaka.itscoder.com/2018/02/23/something-about-decorator/

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,上次計算結果就沒了。

 


另外本人還開設了個人公衆號:JiandaoStudio ,會在公衆號內定期發佈行業信息,以及各類免費代碼、書籍、大師課程資源。

 

                                            

掃碼關注本人微信公衆號,有驚喜奧!公衆號每天定時發送精緻文章!回覆關鍵詞可獲得海量各類編程開發學習資料!

例如:想獲得Python入門至精通學習資料,請回復關鍵詞Python即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章