1. 程式人生 > >黑馬程式設計師--python高階程式設計

黑馬程式設計師--python高階程式設計

第二部分:高階進階

傳值傳引數筆試題

def extendList(val,list=[]):
    list.append(val)
    return list
list1=extendList(10)
list2=extendList(123,['a','b','c'])
list3=extendList('a')
print(list1)
print(list2)
print(list3)
#---------------------------------
[10, 'a']
['a', 'b', 'c', 123]
[10, 'a']

list2呼叫時,初始化並傳入了2列表引數,list1和list3list2呼叫時沒有引數,使用了extentdList預設初始化的list,(是同一地址,且列表可變

)所以兩次分別把10和a新增到list,得到[10,'a']。返回給list1和list3的都是extendList初始化的list的地址,所以返回的值相同

1、模組呼叫

A.import math

B.from 模組名 import 函式名1,函式名2....

C.from modname import name1[, name2[, ... nameN]]
from fib import fibonacci
注意,不會把整個fib模組匯入到當前的名稱空間中,它只會將fib裡的fibonacci單個引入

D.as  取別名
import time as tt

每個Python檔案都可以作為一個模組,模組的名字就是檔案的名字。  xxx.py


包將有聯絡的模組組織在一起,即放到同一個資料夾下,並且在這個資料夾建立一個名字為__init__.py 檔案,那麼這個資料夾就稱之為包,__init__.py 控制著包的匯入行為
 

object是頂級基類

type也是一個類,同時type也是一個物件

魔法函式一覽

字串表示:
__repr__:開發模式下呼叫
__str__:print呼叫
集合序列相關:
__len__
__getitem__
__setitem__
__delitem__
__containes__
迭代相關:
__iter__
__next__
可呼叫:
__call__
上下文管理器:
__enter__
__exit__
數值轉換:
__abs__
__bool__
__int__
__float__
__hash__
__index__
元類相關:
__new__
__init__
屬性相關:
__getattr__、__setattr__
__getattribute__、setattribute__
__dir__
屬性描述:
__get__、__set__、__delete__
…………

鴨子型別

#函式類似
class Cat(object):
    def say(self):
        print('I am a cat')

class Dog(object):
    def say(self):
        print('I am a dog')


animals = [Dog,Cat]
for animal in animals:
    animal().say()

type和isinstance的區別

'''
sinstance() 與 type() 區別:
type() 不會認為子類是一種父類型別,不考慮繼承關係。
isinstance() 會認為子類是一種父類型別,考慮繼承關係。
如果要判斷兩個型別是否相同推薦使用 isinstance()。

'''
class A(object):
    pass
class B(A):
    pass
b = B()
print(isinstance(b, B))  # True
print(isinstance(b, A))  # True
print(type(b)) #<class '__main__.B'>
print(type(b) is B)  # True
print(type(b) is A)  # False

+=本質會呼叫MutableSequence類下的魔法函式__iadd__方法,該方法有呼叫了extend方法。

a = [1, 2, 3]
c = a + (4, 5, 6)
print(c)  # TypeError: can only concatenate list (not "tuple") to list
 
a = [1, 2, 3]
a += (4, 5, 6)
print(a)  # [1, 2, 3, 4, 5, 6]

生成器

ge = (i for i in range(1, 21) if i % 2 == 1)
print(ge)        # <generator object <genexpr> at 0x000001F09B0EEFC0>
print(type(ge))  # <class 'generator'>
print(list(ge))  # [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

python核心程式設計

在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator

    1、 要建立一個生成器,有很多種方法。第一種方法很簡單,只要把一個列表生成式的 [ ] 改成 ( )

 L = [ x*2 for x in range(5)]
 L
[0, 2, 4, 6, 8]

In [17]: G = ( x*2 for x in range(5))
 G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>

要打印出來, for 迴圈來迭代它

2、建立生成器,generator非常強大。如果推算的演算法比較複雜,用類似列表生成式的 for 迴圈無法實現的時候,還可以用函式來實現。

In [30]: def fib(times):
   ....:     n = 0
   ....:     a,b = 0,1
   ....:     while n<times:
   ....:         yield b
   ....:         a,b = b,a+b
   ....:         n+=1
   ....:     return 'done'
   ....: 

生成器的特點:

  1. 節約記憶體
  2. 迭代到下一次的呼叫時,所使用的引數都是第一次所保留下的,即是說,在整個所有函式呼叫的引數都是第一次所呼叫時保留的,而不是新建立的
  • 凡是可作用於 for 迴圈的物件都是 Iterable 型別;
  • 凡是可作用於 next() 函式的物件都是 Iterator 型別
  • 集合資料型別如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不過可以通過 iter() 函式獲得一個 Iterator 物件。

迭代器

迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的物件。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。

L = ['Adam', 'Lisa', 'Bart', 'Paul']
for index, name in enumerate(L):
    print index+1, '-', name

以直接作用於 for 迴圈的資料型別有以下幾種:

 一類是集合資料型別,如 list 、 tuple 、 dict 、 set 、 str 等;

 一類是 generator ,包括生成器和帶 yield 的generator function。

 這些可以直接作用於 for 迴圈的物件統稱為可迭代物件: Iterable 。

一個閉包的實際例子:

    內部函式對外部函式作用域裡變數的引用(非全域性變數),則稱內部函式為閉包。

def line_conf(a, b):
    def line(x):
        return a*x + b
    return line

line1 = line_conf(1, 1) #預設賦值,第一次引數被鎖住了
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
line1 = line_conf(1, 1) #預設賦值,第一次引數被鎖住了
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
由於閉包引用了外部函式的區域性變數,則外部函式的區域性變數沒有及時釋放,消耗記憶體

概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。

def w1(func):
    def inner():
        # 驗證1
        # 驗證2
        # 驗證3
        func()
    return inner

@w1
def f1():
    print('f1')

python直譯器就會從上到下解釋程式碼,步驟如下:

  1. def w1(func): ==>將w1函式載入到記憶體
  2. @w1

沒錯, 從表面上看直譯器僅僅會解釋這兩句程式碼,因為函式在 沒有被呼叫之前其內部程式碼不會被執行。

以後業務部門想要執行 f1 函式時,就會執行 新f1 函式,在新f1 函式內部先執行驗證,再執行原來的f1函式,然後將原來f1 函式的返回值返回給了業務呼叫者。

模組修改,要重新載入reload
避免迴圈匯入,雞生蛋,蛋生雞

  • is 是比較兩個引用是否指向了同一個物件(引用比較)。深拷貝就返回false
  • == 是比較兩個物件是否相等。

淺拷貝:拷貝引用,沒有拷貝內容,地址給了,指向同一地址。
深拷貝:對一個物件所有層次的拷貝,藉助copy.deepcopy模組。內容複製到新記憶體

列表拷貝可變,新記憶體。元祖不可變,copy.copy()指向同一位置(記憶體)。

垃圾回收

  • 小整數[-5,257)共用物件,常駐記憶體
  • 單個字元共用物件,常駐記憶體
  • 單個單詞,不可修改,預設開啟intern機制,共用物件,引用計數為0,則銷燬

編碼風格正確認知

  • 促進團隊合作
  • 減少bug處理
  • 提高可讀性,降低維護成本
  • 有助於程式碼審查
  • 養成習慣,有助於程式設計師自身的成長

系統程式設計:程序&&執行緒


程序,能夠完成多工,比如 在一臺電腦上能夠同時執行多個QQ
執行緒,能夠完成多工,比如 一個QQ中的多個聊天視窗
實現方式都是兩種
多工,時間片輪轉,優先順序呼叫。 併發:有處理多工能力,不一定同時。並行:同時處理多工。
fork返回值特殊,生出多程序。fork()呼叫一次,返回兩次,2返回值。   
子程序永遠返回0,而父程序返回子程序的ID。,父程序大於0;


多程序中,每個程序中所有資料(包括全域性變數)都各有擁有一份,互不影響
多程序如果是對齊,符合2的n,每人都要fork一次


伺服器程序很多,pid類似身份證,不會同多程序中,每個程序中所有資料(包括全域性變數)都各有擁有一份,互不影響,父程序、子程序執行順序沒有規律,完全取決於作業系統的排程演算法。總程序:2*n 次,一般不超過8次。
multiprocessing模組提供了一個Process類來代表一個程序物件,子程序可以用類來實現
while true 慎用
超級多工,Pool程序池 快取   apply堵塞式
使用多執行緒併發的操作,節約時間,執行順序無關
執行緒共享全域性變數,資料共享容易,列表傳參也共享。程序不能(誕生程序通訊)
os模組建立資料夾,os.mkdir(),print除錯
Queue可以用來解決百分比問題

程序池:

from multiprocessing import Pool
import os,time,random

def worker(msg):
    t_start = time.time()
    print("%s開始執行,程序號為%d"%(msg,os.getpid()))
    #random.random()隨機生成0~1之間的浮點數
    time.sleep(random.random()*2) 
    t_stop = time.time()
    print(msg,"執行完畢,耗時%0.2f"%(t_stop-t_start))

po=Pool(3) #定義一個程序池,最大程序數3
for i in range(0,10):
    #Pool.apply_async(要呼叫的目標,(傳遞給目標的引數元祖,))
    #每次迴圈將會用空閒出來的子程序去呼叫目標
    po.apply_async(worker,(i,))

print("----start----")
po.close() #關閉程序池,關閉後po不再接收新的請求
po.join() #等待po中所有子程序執行完成,必須放在close語句之後
print("-----end-----")

執行緒:
多執行緒併發的操作,花費時間要短很多
建立好的執行緒,需要呼叫start()方法來啟動
執行緒建立好了一起執行
在多執行緒開發中,全域性變數是多個執行緒都共享的資料,而區域性變數等是各自執行緒的,是非共享的
    同步(上鎖,有規律):同步就是協同步調,按預定的先後次序進行執行。如:你說完,我再說。 如程序、執行緒同步,可理解為程序或執行緒A和B一塊配合,A執行到一定程度時要依靠B的某個結果,於是停下來,示意B執行;B依言執行,再將結果給A;A再繼續操作。
     非同步:無約定

同步呼叫就是你 喊 你朋友吃飯 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你們一起去
非同步呼叫就是你 喊 你朋友吃飯 ,你朋友說知道了 ,待會忙完去找你 ,你就去做別的了。

     重構:重寫

一個ThreadLocal變數雖然是全域性變數,但每個執行緒都只能讀寫自己執行緒的獨立副本,互不干擾。ThreadLocal解決了引數在一個執行緒中各個函式之間互相傳遞的問題


對於全域性變數,在多執行緒中要格外小心,否則容易造成資料錯亂的情況發生
 

網路程式設計

TCP/IP協議族,現實4層,實現不同系統,機器通訊

網際層也稱為:網路層
網路介面層也稱為:鏈路層

一臺擁有IP地址的主機可以提供許多服務,比如HTTP(全球資訊網服務)、FTP(檔案傳輸)、SMTP(電子郵件)等,這些服務完全可以通過1個IP地址來實現。那麼,主機是怎樣區分不同的網路服務呢?顯然不能只靠IP地址,因為IP地址與網路服務的關係是一對多的關係。

埠,標記程序的東西(同臺電腦用pid),不同電腦通訊標識

ip地址:用來在網路中標記一臺電腦的一串數字,比如192.168.1.1;在本地區域網上是惟一的。

IP地址127.0.0.1~127.255.255.255用於迴路測試   網路號+多播號不能用(多播,有些能看有些不能,視訊會議)
聯網裝置都有ip
利用ip地址,協議,埠就可以標識網路的程序了,網路中的程序通訊就可以利用這個標誌與其它程序進行互動

socket   ip+埠+協議

迴圈儘可能程式碼少,節約

UDP通訊過程


TFTP客戶端

     下載:建立檔案—寫資料—關閉
      上傳:

網路通訊過程詳解

Packet Tracer 是由Cisco(著名網路公司,思科)公司釋出的一個輔助學習工具,

為學習思科網路課程的初學者去設計、配置、排除網路故障提供了網路模擬環境。

TCP通訊的整個過程,如下圖:

掩碼:二進位制的掩碼與ip按位與,得到網路號,(c類最後位255.255.255.0),3個255表示網路號,0表示主機號
網線只能連2臺電腦(網路多臺資料電訊號錯亂),多臺交換機淘汰了集線器(USB拓展)
路由器(Router)又稱閘道器裝置(Gateway)是用於連線多個邏輯上分開的網路
網絡卡:實體地址,實際地址
預設閘道器:
不在同一網段的pc,需要設定預設閘道器才能把資料傳送過去 通常情況下,都會把路由器預設閘道器
集線器:廣播,交換機:更智慧
tcp規定跨網不能直接通訊,路由器雙網絡卡雙ip,左手右手,可以連線不同網段,
ip地址通訊不會變化,mac地址會變化
ping TTL=128,經過一次路由器減一
cdn分發,各地放置伺服器
arp三方造假
nat:ip 內網和外網能上,路由表地址轉換,只能向外才能對映,自己搭建伺服器不能外網訪問,藉助花生殼。

家庭上網解析

邏輯地址轉換後共用一個路由器,路由器訪問dns,伺服器

正則表示式概述

re.match只匹配字串的開始,如果字串開始不符合正則表示式,則匹配失敗,函式返回None;而re.search匹配整個字串,直到找到一個匹配。(不過可以指定起始位置)

compile 函式用於編譯正則表示式,生成一個正則表示式( Pattern )物件,供 match() 和 search() 這兩個函式使用。

>>>import re
>>> pattern = re.compile(r'\d+')                    # 用於匹配至少一個數字
>>> m = pattern.match('one12twothree34four')        # 查詢頭部,沒有匹配
>>> print (m)

日期替換

>>> s = '2017-11-27'
>>> import re
>>> print(re.sub('(\d{4})-(\d{2})-(\d{2})',r'\2/\3/\1', s))
11/27/2017

\b 是指匹配一個單詞邊界,也就是指單詞和空格間的位置。

>>> import re
>>> ret = re.findall(r'o\b','hello nano$')
>>> print(ret)
['o', 'o']

什麼是 Socket?

Socket又稱"套接字",應用程式通常通過"套接字"向網路發出請求或者應答網路請求,使主機間或者一臺計算機上的程序間可以通訊。

import json

# Python 字典型別轉換為 JSON 物件
data = {
    'no': 1,
    'name': 'Runoob',
    'url': 'http://www.runoob.com'
}
json_str = json.dumps(data)
print("Python 原始資料:", repr(data))
print("JSON 物件:", json_str)
#------------------------------------
Python 原始資料: {'no': 1, 'name': 'Runoob', 'url': 'http://www.runoob.com'}
JSON 物件: {"no": 1, "name": "Runoob", "url": "http://www.runoob.com"}


# 寫入 JSON 資料
with open('data.json', 'w') as f:
    json.dump(data, f)
# 讀取資料
with open('data.json', 'r') as f:
    data = json.load(f)

獲取時間

import time
localtime = time.asctime( time.localtime(time.time()) )
print ("本地時間為 :", localtime)

WSGI介面

瞭解了HTTP協議和HTML文件,我們其實就明白了一個Web應用的本質就是:

  1. 瀏覽器傳送一個HTTP請求;

  2. 伺服器收到請求,生成一個HTML文件;

  3. 伺服器把HTML文件作為HTTP響應的Body傳送給瀏覽器;

  4. 瀏覽器收到HTTP響應,從HTTP Body取出HTML文件並顯示。

所以,最簡單的Web應用就是先把HTML用檔案儲存好,用一個現成的HTTP伺服器軟體,接收使用者請求,從檔案中讀取HTML,返回。Apache、Nginx、Lighttpd等這些常見的靜態伺服器就是幹這件事情的。

如果要動態生成HTML,就需要把上述步驟自己來實現。不過,接受HTTP請求、解析HTTP請求、傳送HTTP響應都是苦力活,如果我們自己來寫這些底層程式碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。

正確的做法是底層程式碼由專門的伺服器軟體實現,我們用Python專注於生成HTML文件。因為我們不希望接觸到TCP連線、HTTP原始請求和響應格式,所以,需要一個統一的介面,讓我們專心用Python編寫Web業務。

這個介面就是WSGI:Web Server Gateway Interface。

複雜的Web應用程式,光靠一個WSGI函式來處理還是太底層了,我們需要在WSGI之上再抽象出Web框架,進一步簡化Web開發。