python-4


1,裝飾器:decorator(又叫語法糖)
本質是函數,功能是用來裝飾的,裝飾其他函數:就是為其他函數添加附加功能。
原則:a,不能修改被裝飾的函數的源代碼
      b,函數的調用方式也不能修改
說明:盡管加了裝飾函數,對原函數沒有任何影響,原函數感覺不到裝飾器的存在,原函數該怎么運行還怎么運行。

2、基本常識:
a,函數即“變量”:函數就是變量。

b,高階函數滿足兩個條件之一就是,

之一:把一個函數名當做實參傳給另外一個函數 (在不修改被裝飾函數的源代碼的情況下為其添加裝飾功能)
例子:

def bar():
    print('111111111')
def test1(func):
    print(func)
test1(bar)
------------>
運行后打印出來的是時內存地址:
<function bar at 0x0000000000000D521453> 這個內存地址里存的就是對應變量的內容
之二:返回值中包含函數名,返回值也可以字符串,數字,列表,函數名,函數執行。

c,嵌套函數(在一個函數的內容主體內又定義了一個新的函數)
def bar():
    def foo():
        print('In the foo')
        return 'aa'
    print('In the bar')
    return foo()
bar()

運行結果如下:
In the bar
In the foo

d,匿名函數:沒有名稱的函數(注意,此時的內存回收機制),他在內存中保存機制:此時的門牌號
如:

calc = lambda x:x*3
calc(3)

e,高階函數+嵌套函數=裝飾器

實例1:函數的調用,不是裝飾器
def bar(): ---------> 存到內存里的一個名稱,相當於門牌號,只用通過這個門牌號才能里面具體的內容
    print('in the bar') -------> 存到內存里的對應門牌號的具體的內容,實實在在的存下來。python的內存機制,當門牌號沒有時才會回收里面的具體的內容,才是釋放內存,有多個門牌號時,所有的門牌都回收才會釋放該內存
def foo():
    print('in the foo')
    bar()
foo()

實例2:python的內存回收機制
    x = 1 直到python的程序結束,才會回收x =1這個內存,要么在程序中定義del來結束這個變量,否則就是直到程序結束才回收。del清除的就是1這個具體的內容的門牌號,內核里里有個內存清除器,當它到點啟動時,發現這個1沒有人引用了,此時才會清空1的這個內容。這就是內存回收機制

實例3:

def bar():
    pass   --------> pass :當代碼運行到這一行的時候。跳過。相當於執行了一個空值。
bar
---->
<function bar at 0X000000000000D50158> 此時就是內存地址
bar() 此時就是基於內存地址在調用,即調用該函數(變量)的具體內容

實例4:高階函數之將一個函數作為另外一個函數的實參傳進去:
def bar():
    pass
    def test1(func):
        print(func)   --------> 打印出來的是內存地址
        func()    --------> 調用基於func內存地址的具體內容。
test1(bar)

實例5:高階函數之將一個函數作為另外一個函數的實參傳進去::
import time
def bar():
    print(555555)
    def test1(func):     -------> 此處的func就是形參的參數名,會被后面的實參的bar代替
        start_time=time.time()
        func()         --------> 運行bar函數
        stop_time=time.time()
        print("the func run time is %s" %(stop_time-start_time))
test1(bar)

實例6:高階函數之將一個函數作為另外一個函數的返回值傳進去::
import time
def bar():
    time.sleep(3)
    print('in the bar')
def test2(func):
    print(func)    --------> 打印出來內存地址
    return func     --------> 將func這個參數作為返回值
print(test2(bar))    --------> 打印一個運行后的函數就是打印他的返回值(即return后的內容)!!!!!!!
----------->
解說:

test2(bar) 相當於將bar的內存地址傳給test2了
test2(bar())相當於將bar的變量內容傳給test2了


import time
def bar():
    time.sleep(3)
    print('in the bar')
def test2(func):
    print(func)   -------> 打印出來內存地址
    return func    --------> 將bar的內存地址返回回來
t=test2(bar)   --------> 將test2(bar)運行后的返回值再賦值給t這個變量!!!!!!
print(t)
----->
<function bar at 0X000000000000D50158>
<function bar at 0X000000000000D50158>
注意:這里的bar函數並沒有運行。只是調用了bar這個函數的名稱而已。所以只有上面的內存地址顯示出來。

---------------------------------------------------------------------------------------------------------------------
import time
    def bar():
    time.sleep(3)
    print('in the bar')
def test2(func):
    print(func)   --------> 打印出來內存地址
    return func   --------> 將bar的內存地址返回回來
t=test2(bar)   -------->這一步的運行結果就是打印了bar()函數的內存地址,並將該內存地址賦值給了t
t()  ---- 運行bar,注意bar只是門牌號,運行t這個內存地址的主體內容,就是運行bar()這個實體。

----->
<function bar at 0X000000000000D50158>
in the bar
-----------------------------------------------------------------------------------------------------------------------
import time
def bar():
    time.sleep(3)
    print('in the bar')
def test2(func):
    print(func)   ---------> 打印出來內存地址
    return func   ---------> 將bar的內存地址作為返回值
bar=test2(bar)   ---------> 重新給bar賦值
bar()    -----------> 再次運行bar
總結:返回值中包含函數名(不修改函數的調用方式)

------>
<function bar at 0x00000000004C3E18>
In the bar.

總結:該例中兩次給bar賦值,一次是定義成一個函數,第二次是定義成了內存地址賦值。但是該內存地址的主體內容仍然沒變。故仍然可以運行。

-----------------------------------------------------------------------------------------------------------------------
實例7,嵌套函數:在一個函數體內,用def去申明一個新的變量
def foo():
    print('000000')
    def bar():      ---------> 在一個函數體內定義一個新的函數,其作用域也就在這個函數范圍內,這就是嵌套函數。出來外面這個函數就失效。
    print('11111111111')
    bar()
foo()
注意:與函數調用不同:下例為函數的調用

def test1():
    print('2222222222')
def test2():
    print("3333333333")
    test1()    ----------> 函數的調用,不是嵌套函數。
print(test2)    ----------> 僅僅打印test2的內存地址
print(test2())    ----------> 分兩步運行:1,運行的結果就是先運行test2()函數;2,最后打印test2()的返回值

--------->
<function test2 at 0x00000000021DF730>
222222222222222222233333
2222222222
None    ----------> 因為函數中沒有定義返回值,所以返回值為None.


補充說明:
def test1():
    print(2222222222)
    return 'xxxxxx'
print(test1())   ----------> 分兩步執行:1,test1()函數執行,2,打印該函數的執行結果
------->
2222222222
xxxxxx


--------------------------------------------------------------------------------------------------
實例8,@timer 的作用:

實例9,
import time

def timer(func):   ----------------> timer(test1) ,將test1的內存地址傳給func了,
    def deco():    ----------------> 嵌套函數
        start_time=time.time()
        func()     ---------------> 運行test1()
        stop_time=time.time()
        print("the func run time is %s" %(stop_time-start_time)) --------> 打印出test1 的運行時間
    return deco   -------------> 高階函數,返回這個函數(或者叫變量)的內存地址

def test1():
    time.sleep(3)
    print('in the test1')

test1=timer(test1)
test1() ------------- 此時實際執行的就是deco的函數

-------> 等3秒后出現以下結果
in the test1
the func time is 3.00001785546222

裝飾器的用法:@,給哪個函數加,就在哪個函數的上頭加上@

實例10,
import time
def timer(func):   --------->  timer(test1) ,將test1的內存地址傳給func了,
    def deco():   --------->  嵌套函數
        start_time=time.time()
        func()    -------->  運行test1()
        stop_time=time.time()
        print("the func run time is %s" %(stop_time-start_time))  --------> 打印出test1 的運行時間
    return deco   --------> 高階函數,返回這個函數(或者叫變量)的內存地址
@timer   --------> 作用相當於:test1=timer(test1)
def test1():
    time.sleep(3)
    print('in the test1')
@timer
def test2():
    time.sleep(3)
    print('in the test2')
test1()
test2()

實例11:

裝飾器:可以滿足日常90%的需求:針對任意的傳入的參數

import time
def timer(func):
    def deco(*args,**args):
        start_time=time.time()
        func(*args,**args)
        stop_time=time.time()
        print("the func run time is %s" %(stop_time-start_time))
    return deco
@timer
def test1():
    time.sleep(3)
    print('in the test1')
@timer
def test2():
    time.sleep(3)
    print('in the test2')
test1()
test2()

------------------------------------------------------------------------------------------------------------------------------------------------------------------
實例12:

模擬網站,一個頁面就是一個函數
import time
user,passwd ='alex','abac123'
def auth(func):
    def wrapper(*args,**kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and passwd == password:
            print("user has passed authz")
            func(*args,**args)
        else:
            exit
    return wrapper
def index():
    print("welcome to index page")
@auth
def home():
    print("welcome to home page")
    return "from home"
@auth
def bbs():
    print("welcome to bbs page")
index()
home()
bbs()

------------------------------------------------------------------------------------------------------ 

3、迭代器&生成器

3.1、 列表生成式(在cmd的python命令行里執行),使代碼更簡潔。
[i*2 for i in range(10)]
等同於:
a = []
for i in range(10):
    a.append(i*2)
print(a)

3.2、 通過列表生成式,我們可以直接創建一個列表。

    但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。

生成器特點:

(1)、生成器只有在調用時候才會生成相應的數據。並且生成器省內存的方法就是只保留一個值
(2)、只記錄當前位置
(3)、只有一個next方法:__next__() 雙下划線


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

在cmd的python命令下運行:
生成1個1百萬的列表:
>>>[i*2 fro i in range(1000000)]

此時就會打印
...
賦值的形式:
>>>a = [i*2 fro i in range(1000000)]   后面的數字越大,則這一步的時間越長。
>>>len(a) 可以查看
調用方式:
>>>a[1000]

>>>c = ( i*2 for i in range(100000000))    ---->馬上出來,因為它不生成具體的每一個數字,只生成一個內存地址,此時,只有你訪問這個地址,它才會生成。不訪問的話,它都不會存在。和列表的區別,只有在調用的時候才會生成。不調用的時候不會生成。
>>>print(c)
<generator object <genexpr> at 0x000000000000D4E308>
訪問的范式:不能通過列表的下標去取值
生成器取值只能是一個一個的去取
>>> c._ _next_ _() --------------- 實際中next用得並不多,實際中用循環去調取數據。
5122
>>> c._ _next_ _()
5124


3.3、利用函數來做生成器
generator非常強大,如果用推算的算法比較復雜,用類型表達式的for循環無法實現的時候,還可用函數來實現

斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b     --------->此時a、b是同時賦值的
    n = n + 1
return 'done'
注意,賦值語句:
a, b = b, a + b
相當於:
t = (b, a + b) # t是一個tuple
a = t[0]
b = t[1]
>>> fib(10) 調用:意思是生成10個這個序列
1
1
2
3
5
8
13
21
34
55
done

要把fib函數變成generator,只需要把print(b)改為yield b就可以了:
def fib(max):
    n,a,b = 0,0,1
    while n < max:
        #print(b)
        yield b
        a,b = b,a+b   ==========》 注意這個書寫方式,是兩個等式同時賦值的意思。即 a=b,b=a+b 同時進行賦值!!!。
    n += 1
    return 'done'
print(fib(100))
運行后:
<generator object fib at 0x000000000000D4E308>
這就是定義generator的另一種方法。如果一個函數定義中包含yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator。

 

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

pytharm里面是這樣的:
<generator object f at 0x0000000001DC68E0>


    這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的 yield 語句處繼續執行。
作用:上面的f函數是可以在程序外來隨時中斷的,又可以隨時的恢復啟用,中間可以夾雜別的程序。
當f程序是個很大的很慢的程序時,就可以在這中間夾雜別的程序了


data = fib(10)
print(data)

print(data.__next__())
print(data.__next__())
print("干點別的事")    ---------->  可以跳出程序外做別的任務。然后又可以隨時進入在生成器程序。
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())

#輸出
<generator object fib at 0x101be02b0>
1
1
干點別的事
2
3
5
8
13

在上面fib的例子,我們在循環過程中不斷調用yield,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來。

同樣的,把函數改成generator后,我們基本上從來不會用next()來獲取下一個返回值,而是直接使用for循環來迭代:

 

def f(max):
    n,a,b=0,0,1
    while n<max:
        yield b
        a,b = b,a+b
        n = n+1
    return "done"
for n in f(10):
    print(n)
1
1
2
3
5
8

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

用for循環調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:

def f(max):
    n,a,b=0,0,1
    while n<max:
        yield b
        a,b = b,a+b
        n = n+1
        return "done"
for n in f(10):
    print(n)
g = fib(6)
while True:
    try:
    x = next(g)
    print('g:', x)
    except StopIteration as e:
        print('Generator return value:', e.value)
        break

意思是:

while True :一直循環下去
try:一直抓值,
except ... 直到抓取到特定的StopIteration這個錯誤的時候才執行其下面的語句:print和break操作。e代表上文代碼里定義的返回值:"done",

g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

關於如何捕獲錯誤,后面的錯誤處理還會詳細講解

----------------------------------------------------------------------------------

例:

def f(max):
    n,a,b =0,0,1
    while n<max:
        yield b
        a,b = b,a+b
        n =n+1
    return "done"
g = f(10)
print("========== start loop ==========")
for i in g:
    print(i)


def f(max):
    n,a,b =0,0,1
    while n<max:
        yield b
        a,b = b,a+b
        n =n+1
    return "done"
g = f(10)

------>

print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

運行:報錯一個"done"的異常錯誤。因為取值超過了定義的10次,已經取不到值了

-----------------------------------------------------------------------------------------------------------------------------------------------------------
改進方法:引進try:

def f(max):
    n,a,b =0,0,1
    while n<max:
        yield b
        a,b = b,a+b
        n =n+1
    return "done"   ---------> 就是超值范圍后的報錯信息,異常的時候打印的消息。
g = f(10)

while True:    --------> 死循環
    try:   --------> s是一種不斷的抓的方法
    x = next(g)
    print("g:",x)
    except StopIteration as e:
        print("Generator return value:",e.value)
        break

意思是try下面的代碼一旦出這個錯:StopIteration (出來別的任何錯都不處理)就執行:print("Generator return value:",e.value) 和break 跳出循環


總結:代碼帶有yield的就不叫函數了。就是一個生成器了。
yield作用:使得函數保存當前位置,並退出該函數,下次任何再調用函數的時候再回來

 

-------------------------------------------------------------------------------

應用:通過yield實現在單線程的情況下實現並發運算的效果

實例1:

def con(name):
    print("%s prepare to eat box."%name)
    while True:
        baozi = yield
        print("box %s coming,and eated by %s"%(baozi,name))
c =con("chen")
c.__next__()
c.__next__()
運行:
chen prepare to eat box.
box None coming,and eated by chen

-----------------------------------------------------------------------------------------------------------------------------------------------
例2:
def con(name):
    print("%s prepare to eat box."%name)
    while True:
        baozi = yield
        print("box %s coming,and eated by %s"%(baozi,name))
c =con("chen")
c.__next__()
b1 = "韭菜的"
c.send(b1)
--->
chen prepare to eat box.
box 韭菜的 coming,and eated by chen

解釋:

send 可以給yield傳值同時調用該值
next僅僅是調用yield的值。

--------------------------------------------------------------------------------

例3:
def con(name):
    print("%s prepare to eat box."%name)
    while True:
        baozi = yield
        print("box %s coming,and eated by %s"%(baozi,name))
c =con("chen")
c.__next__()
b1 = "韭菜的"
c.send(b1)
c.__next__()

---->
chen prepare to eat box.
box 韭菜的 coming,and eated by chen
box None coming,and eated by chen

---------------------------------------------------------------------------------------------

迭代器:

    迭代器是訪問集合元素的一種方式。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退,不過這也沒什么,因為人們很少在迭代途中往后退。另外,迭代器的一大優點是不要求事先准備好整個迭代過程中所有的元素迭代器僅僅在迭代到某個元素時才計算該元素而在這之前或之后,元素可以不存在或者被銷毀。這個特點使得它特別適合用於遍歷一些巨大的或是無限的集合,比如幾個G的文件

特點:
1.訪問者不需要關心迭代器內部的結構,僅需通過next()方法不斷去取下一個內容
2.不能隨機訪問集合中的某個值,只能從頭到尾依次訪問
3.訪問到一半時不能往回退
4.便於循環比較大的數據集合,節省內存

>>> a = iter([1,2,3,4,5])
>>> a
<list_iterator object at 0x101402630>
>>> a.__next__()
1
>>> a.__next__()
2
>>> a.__next__()
3
>>> a.__next__()
4
>>> a.__next__()
5
>>> a.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

 

可以直接作用於for循環的數據類型有以下幾種:
一類是集合數據類型,如list、tuple、dict、set、str等;
一類是generator,包括生成器和帶yield的generator function。
這些可以直接作用於for循環的對象統稱為可迭代對象:Iterable。可以使用isinstance()判斷一個對象是否是Iterable對象:

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)   ----------> 單個的數字不是可迭代對象,數字的列表就是
False

而生成器不但可以作用於for循環,還可以被next()函數不斷調用並返回下一個值,直到最后拋出StopIteration錯誤表示無法繼續返回下一個值了。

可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator
判斷:一個函數里有next的方法的就叫迭代器(反之,沒有這個方法的就不叫迭代器)
>>> a =[1,2,3]
>>> dir(a)   -------> 查看有無next方法


生成器肯定就是迭代器(因為生成器有next方法):
>>> from collection import Iterator
>>> isinstance((x for x in range(5)).Iterator)
True

但迭代器不一定就是生成器

可以使用isinstance()判斷一個對象是否是Iterator對象:

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator) ------》判斷列表是否是
False
>>> isinstance({}, Iterator) —————》 字典
False
>>> isinstance('abc', Iterator) --------》字符串
False

生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
>>>a=[1,2,3]
>>>iter(a)
>>>b=iter(a) ---------> 此時的b就是一個迭代器了,可以通過next的方法取值了。
>>>b.__next__()
>>>b.__next__()
>>>b.__next__()

把list、dict、str等Iterable變成Iterator可以使用iter()函數:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
你可能會問,為什么list、dict、str等數據類型不是Iterator?
這是因為Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。
Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
小結
凡是可作用於for循環的對象都是Iterable類型;
凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
Python的for循環本質上就是通過不斷調用next()函數實現的,例如:

for x in [1, 2, 3, 4, 5]:
    pass
實際上完全等價於:

# 首先獲得Iterator對象:
it = iter([1, 2, 3, 4, 5])  # 循環:
while True:
    try:   # 獲得下一個值:
    x = next(it)
    except StopIteration:    # 遇到StopIteration就退出循環
        break

 

例:range(10) 在python3.0里就是一個迭代器

>>> range(10) -------> python3.0
range(1.10)
>>> range(10) -------> python2.0 變成跌代器:xrange(10)
[0,1,2...9]

本質:3.0里面的for循環就是通過next的方法去取值的。
for line in f.readlines():
xxx
這個也是使用迭代的方法。一次讀一行。

以后通過socket傳文件的額時候可以用這個,因為底層很多的都是迭代器封裝的。

 


內置方法(內置參數)

pycharm里演示:

abs():取絕對值

 

all():
   Return True if all elements of the iterable are true (or if the iterable is empty). Equivalent to:

def all(iterable):
    for element in iterable:
       if not element:
            return False
    return True

實例:
print(all[0,12,-45345])   ------> 因為0為非真,故返回假
print(all[12210,12,-45345])

any() 如果可迭代對象的任意的一個數據為真,就返回真,(有一個數據為真就返回真)。列表為空就返回假
def any(iterable):
for element in iterable:
if element:
return True
return False

實例:
print(any[0,12,-45345])
print(all[])
print(all[0])


ascii(): 沒用:將一個內存對象變成一個可打印的字符串

實例:print(ascii([1,3,"是否發生李開復"]))

a=[1,3,"是否發生李開復"]
print(a,type(a))
--------------------------------------------------------------------------------------
bin() 將一個整數轉換成二進制(1進制轉二進制)
>>>bin(1)
>>>bin(234432143)
--------------------------------------------------------------------------------------
bool() 布爾判斷。0就是FALSE,1就是true
>>>bool(1)
>>>bool(0)
--------------------------------------------------------------------------------------
bytearray() 字節數組,將二進制變成列表的形式可以修改了。用不上,知道有這個東西就行了

a=bytes("abcde",encoding="utf-8")
print(a)
print(a.capitalize(),a)

理解:字符串不可以修改,列表可以修改。

b=bytearray("abcde",encoding="utf-8")
b[0]
print(b[0]) ---------> 97 ascii碼里a這個字符對應的就是97
print(b[1]) ---------> 98 ascii碼里b這個字符對應的就是98

b[1] = 100
print(b)

b[1] = 50
print(b) -----> bytearary(b"xxxxxx")

---------------------------------------------------------------------------------------
callable(object) 判斷對象是否可以調用:在這個對象后面可以加()的就是屬於可以調用

比如:
print(callable([1,2,3])) ----> false
def a():pass
print(callable(a)) -----> true


--------------------------------------------------------------------------------

chr() ---> 反映數字!!對應的ASCII碼的字符
chr(97)

---------------------------------------------------------------------------------
ord() ----->反映字符在ascii碼里對應的數字
ord(a)

---------------------------------------------------------------------------------
classmothod()類方法

---------------------------------------------------------------------------------
compile() 底層的,將代碼用於編譯的。用不上

---------------------------------------------------------------------------------
complex() 復數,用不上

---------------------------------------------------------------------------------
delattr( ) 面向對象


-----------------------------------------------------------------------------------
dir():查看某個對象的所有方法:

a= {1,2,212}
dir(a) ------->結果里面有兩個下划線的除了next都是內置方法,我們不能用,其他的方法可以用

-----------------------------------------------------------------------------------
divmod() 相除並返回商和余數

divmod(5,3)
-->
(1,2)


-----------------------------------------------------------------------------------------
filter(function,iterable):對傳入的值按照function的方法過濾下在處理,從一組數據里面過濾出你想要的。

例:
a=filter(lambda n:n>5,range(10))
for i in a:
print(a)

----------------------------------------------------------------------------------------
map(function,iterable) 對傳入的值都安裝function的方法來處理

a=map(lambda n:n**n,range(10)) == a=[lambda i:i*2 for i in range(10)]
for i in a:
print(i)

-----------------------------------------------------------------------------------------
reduce():是一個標注庫里的模塊了,逐漸相加

import functools
a = functools.reduce(lambda x,y:x+y,range(10))
print(a)
___>45

0+1+2+3+4+5+6+7+8+9==45

---------------------------------------------------
frozenset([iterable]) 不可變的

a= [1,2,3,4323,4,24,234,234,,124,13,21,3]
a=frozenset([1,2,3,4323,4,24,234,234,,124,13,21,3])
此時的a就不可變了。沒有了改,增,刪的方法了

----------------------------------------------------
globals() 返回當前程序所有變量的key.value的格式

print(globals()) 用得少

-----------------------------------------------------

解釋:哈希算法:固定映射關系:
張三 =“xxx” --------》 python自動給"張飛" 映射成 1,
李四 = “xxx” --------》 ..........................2,

下次尋址:尋張三,就安裝對應表找1,等於就是尋找:1,2,3,4,5,6...等

而找1000000里面的值的方法:
假如找5000,在100萬里打半,50萬 》5千
再判斷:25萬 》 5千
再打半判斷:12.5萬 > 5千
........... 6.75萬 ....
3.875 ....
............1.9875 ...
............ 1萬 > 5千
。。。。。。5 qian = 5qian 找到了。等於在100萬里找個數通過7次就找到了


例:
hash(10) ----》打印出16進制
hash(“Jack”)

----------------------------------------------------------------------------
locals() -----> 用不上

----------------------------------------------------------------------------

max() 返回列表里的最大值
min() 返回列表里的最小值

-----------------------------------------------------------------------------
oct() 轉8進制,沒什么用

oct(1)
--->0o1
oct(9)
--->
0o11

-----------------------------------------------------------------------------
pow(x,y), x的y次方

pow(2,3)
--》8

----------------------------------------------------------------------------------
round(1.2342342342) --- 保留2位小數點
--->
1.33

----------------------------------------------------------------------------------
setattr() 非常重要的!!


----------------------------------------------------------------------------------
slice() 切片 ---- 沒用

例:
d = range(20)
d[slice(2,5)]

語法等於:
d[2,5]

-----------------------------------------------------------------------------

sorted()

例:
a = {
6:2,
8:0,
1:4,
9:11
}

print(a)
因為字典本身即是無序的,通過sort()可以排序,默認安裝key來排序
print(sorted(a.iteams())

安裝value來排序:
print(sorted(a.items(),key=lambda x:x[1])) x代表:字典里的元素 key:value

-----------------------------------------------------------------------------
zip() 拉鏈,

a=[1,2,3,4]
b=['a','b','c','d']

zip(a,b)
print(zip(a,b))
for i in zip(a,b):
print(i)

當a,b 的元素個數不一樣的時候,按照最少的那個來組合

 

---------------------------------------------------------------------------------------------------

json 和pickle
用於序列化的兩個模塊
json,用於字符串和 python數據類型間進行轉換
pickle,用於python特有的類型 和 python的數據類型間進行轉換
Json模塊提供了四個功能:dumps、dump、loads、load
pickle模塊提供了四個功能:dumps、dump、loads、load


序列化:將內存對象變成字符串

例:將字典存成字符串:
info = {
name="alex",
age=22,
}
f= open('text','w')
f.write(str(info))
f.close

 

反序列化: 將字符串變成內存對象

d = f.read()
f.close()
print(d)

 

d = eval(f.read())
f.close()
print(d['age'])

----->22

 

---------------------------------------------------------

# 序列化dumps: 將內存上的數據寫到硬盤上

info = {
name="alex",
age=22,
}


f = open('text','w')
print(json.dumps(info))

---------------------------------------------------------
反序列化loads:將內存的數據對象存到硬盤上了
import json
f= open("text",'r')
d = json.loads(f.read())
print(d["age"])

右鍵運行:反序列化:
22


例:
import json
def sayhi(name):
print("hello",name)

info = {
'name':"alex",
"age":22,
"func":sayhi
}

f = open("text","w")
f.write(json.dumps(info))
f.close()

報錯:該內存地址不是一個json的可序列化的數據,json只能處理簡單的數據,比如:字典,列表,字符串等
因為json是所有的語言里都通用的。作用在於在不同語言之間進行交互。


處理復雜的可序列化的數據:用pikle:
import json
def sayhi(name):
print("hello",name)

info = {
'name':"alex",
"age":22,
"func":sayhi
}

f = open("text","wb")
print()
f.write(pickle.dumps(info))
f.close()

 

反序列化:
d = pickle.loads(f.read)
print(d['age'])

 


f=open('text',"wb")
pickle.dump(info,f) == f.write(pickle.dumps(info))
f.close()

d = pickle.load(f) == d = pickle.loads(f.read())
print(d['func']("alex"))

 

要點:json的序列化,反序列化
pickle的序列化,反序列化

引申:dump兩次、三次

 

------------------------------------------------------------------------------------------------
軟件目錄開發規范:


為什么要設計好目錄結構

"設計項目目錄結構",就和"代碼編碼風格"一樣,屬於個人風格問題。對於這種風格上的規范,一直都存在兩種態度:

一類同學認為,這種個人風格問題"無關緊要"。理由是能讓程序work就好,風格問題根本不是問題。
另一類同學認為,規范化能更好的控制程序結構,讓程序具有更高的可讀性。
我是比較偏向於后者的,因為我是前一類同學思想行為下的直接受害者。我曾經維護過一個非常不好讀的項目,其實現的邏輯並不復雜,但是卻耗費了我非常長的時間去理解它想表達的意思。從此我個人對於提高項目可讀性、可維護性的
要求就很高了。"項目目錄結構"其實也是屬於"可讀性和可維護性"的范疇,我們設計一個層次清晰的目錄結構,就是為了達到以下兩點:

可讀性高: 不熟悉這個項目的代碼的人,一眼就能看懂目錄結構,知道程序啟動腳本是哪個,測試目錄在哪兒,配置文件在哪兒等等。從而非常快速的了解這個項目。
可維護性高: 定義好組織規則后,維護者就能很明確地知道,新增的哪個文件和代碼應該放在什么目錄之下。這個好處是,隨着時間的推移,代碼/配置的規模增加,項目結構不會混亂,仍然能夠組織良好。
所以,我認為,保持一個層次清晰的目錄結構是有必要的。更何況組織一個良好的工程目錄,其實是一件很簡單的事兒。

目錄組織方式

關於如何組織一個較好的Python工程目錄結構,已經有一些得到了共識的目錄結構。在Stackoverflow的這個問題上,能看到大家對Python目錄結構的討論。

這里面說的已經很好了,我也不打算重新造輪子列舉各種不同的方式,這里面我說一下我的理解和體會。

假設你的項目名為foo, 我比較建議的最方便快捷目錄結構這樣就足夠了:

Foo/
|-- bin/
| |-- foo ----------》 執行這里的文件,去調用下面的main.py的主程序,從而實現整個python程序運行。
|-- conf/
|
|-- foo/
| |-- tests/ ------------------>測試的
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py ---------------> 正式的
|
|-- docs/ --------------> 文檔
| |-- conf.py
| |-- abc.rst
|
|-- setup.py -----------> 安裝程序
|-- requirements.txt --------> 依賴哪些別的軟件
|-- README
簡要解釋一下:

bin/: 存放項目的一些可執行文件,當然你可以起名script/之類的也行。
foo/: 存放項目的所有源代碼。(1) 源代碼中的所有模塊、包都應該放在此目錄。不要置於頂層目錄。(2) 其子目錄tests/存放單元測試代碼; (3) 程序的入口最好命名為main.py。
docs/: 存放一些文檔。
setup.py: 安裝、部署、打包的腳本。
requirements.txt: 存放軟件依賴的外部Python包列表。
README: 項目說明文件。
除此之外,有一些方案給出了更加多的內容。比如LICENSE.txt,ChangeLog.txt文件等,我沒有列在這里,因為這些東西主要是項目開源的時候需要用到。如果你想寫一個開源軟件,目錄該如何組織,可以參考這篇文章。

下面,再簡單講一下我對這些目錄的理解和個人要求吧。

關於README的內容

這個我覺得是每個項目都應該有的一個文件,目的是能簡要描述該項目的信息,讓讀者快速了解這個項目。

它需要說明以下幾個事項:

1、軟件定位,軟件的基本功能。
2、運行代碼的方法: 安裝環境、啟動命令等。
3、簡要的使用說明。
4、代碼目錄結構說明,更詳細點可以說明軟件的基本原理。
5、常見問題說明。

我覺得有以上幾點是比較好的一個README。在軟件開發初期,由於開發過程中以上內容可能不明確或者發生變化,並不是一定要在一開始就將所有信息都補全。但是在項目完結的時候,是需要撰寫這樣的一個文檔的。
可以參考Redis源碼中Readme的寫法,這里面簡潔但是清晰的描述了Redis功能和源碼結構。

關於requirements.txt和setup.py

setup.py

一般來說,用setup.py來管理代碼的打包、安裝、部署問題。業界標准的寫法是用Python流行的打包工具setuptools來管理這些事情。這種方式普遍應用於開源項目中。不過這里的核心思想不是用標准化的工具來解決這些問題,而是說,
一個項目一定要有一個安裝部署工具,能快速便捷的在一台新機器上將環境裝好、代碼部署好和將程序運行起來。

這個我是踩過坑的。

我剛開始接觸Python寫項目的時候,安裝環境、部署代碼、運行程序這個過程全是手動完成,遇到過以下問題:

安裝環境時經常忘了最近又添加了一個新的Python包,結果一到線上運行,程序就出錯了。
Python包的版本依賴問題,有時候我們程序中使用的是一個版本的Python包,但是官方的已經是最新的包了,通過手動安裝就可能裝錯了。
如果依賴的包很多的話,一個一個安裝這些依賴是很費時的事情。
新同學開始寫項目的時候,將程序跑起來非常麻煩,因為可能經常忘了要怎么安裝各種依賴。
setup.py可以將這些事情自動化起來,提高效率、減少出錯的概率。"復雜的東西自動化,能自動化的東西一定要自動化。"是一個非常好的習慣。

setuptools的文檔比較龐大,剛接觸的話,可能不太好找到切入點。學習技術的方式就是看他人是怎么用的,可以參考一下Python的一個Web框架,flask是如何寫的: setup.py

當然,簡單點自己寫個安裝腳本(deploy.sh)替代setup.py也未嘗不可。

requirements.txt

這個文件存在的目的是:

方便開發者維護軟件的包依賴。將開發過程中新增的包添加進這個列表中,避免在setup.py安裝依賴時漏掉軟件包。
方便讀者明確項目使用了哪些Python包。
這個文件的格式是每一行包含一個包依賴的說明,通常是flask>=0.10這種格式,要求是這個格式能被pip識別,這樣就可以簡單的通過 pip install -r requirements.txt來把所有Python包依賴都裝好了。具體格式說明: 點這里。

 

關於配置文件的使用方法

注意,在上面的目錄結構中,沒有將conf.py放在源碼目錄下,而是放在docs/目錄下。

很多項目對配置文件的使用做法是:

配置文件寫在一個或多個python文件中,比如此處的conf.py。
項目中哪個模塊用到這個配置文件就直接通過import conf這種形式來在代碼中使用配置。
這種做法我不太贊同:

這讓單元測試變得困難(因為模塊內部依賴了外部配置)
另一方面配置文件作為用戶控制程序的接口,應當可以由用戶自由指定該文件的路徑。
程序組件可復用性太差,因為這種貫穿所有模塊的代碼硬編碼方式,使得大部分模塊都依賴conf.py這個文件。
所以,我認為配置的使用,更好的方式是,

模塊的配置都是可以靈活配置的,不受外部配置文件的影響。
程序的配置也是可以靈活控制的。
能夠佐證這個思想的是,用過nginx和mysql的同學都知道,nginx、mysql這些程序都可以自由的指定用戶配置。

所以,不應當在代碼中直接import conf來使用配置文件。上面目錄結構中的conf.py,是給出的一個配置樣例,不是在寫死在程序中直接引用的配置文件。可以通過給main.py啟動參數指定配置路徑的方式來讓程序讀取配置內容。當然,這
里的conf.py你可以換個類似的名字,比如settings.py。或者你也可以使用其他格式的內容來編寫配置文件,比如settings.yaml之類的。

 


跨目錄調用文件:atm.py 調用settings.py和main.py
atm
|-bin
| |- __init__.py
| |- atm.py
|-conf
| |- __init__.py
| |- settings.py
|-core
|- __init__.py
|- main.py

def login():
print("welcome to my atm")
print(__file__) ----------------> 當前文件的相對路徑
import os
#print(os.path.abspath(__file__)) ---> 當前文件的絕對路徑

#通過dir命令:返回路徑名,不要文件名
#print(os.path.dirname(os.path.abspath(__file__))) -----> 可以找到父一級的目錄:bin目錄

#再向上一級,到atm的總目錄
import sys
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
import conf,core

from conf import settings -----------> 這樣就可以調用 conf 目錄下的文件了


from core import main -----------> 這樣就可以調用core目錄下的文件了
main.login()


程序結構:
day4-atm/
├── README
├── atm #ATM主程目錄
│ ├── __init__.py
│ ├── bin #ATM 執行文件 目錄
│ │ ├── __init__.py
│ │ ├── atm.py #ATM 執行程序
│ │ └── manage.py #ATM 管理端,未實現
│ ├── conf #配置文件
│ │ ├── __init__.py
│ │ └── settings.py
│ ├── core #主要程序邏輯都 在這個目錄 里
│ │ ├── __init__.py
│ │ ├── accounts.py #用於從文件里加載和存儲賬戶數據
│ │ ├── auth.py #用戶認證模塊
│ │ ├── db_handler.py #數據庫連接引擎
│ │ ├── logger.py #日志記錄模塊
│ │ ├── main.py #主邏輯交互程序
│ │ └── transaction.py #記賬\還錢\取錢等所有的與賬戶金額相關的操作都 在這
│ ├── db #用戶數據存儲的地方
│ │ ├── __init__.py
│ │ ├── account_sample.py #生成一個初始的賬戶數據 ,把這個數據 存成一個 以這個賬戶id為文件名的文件,放在accounts目錄 就行了,程序自己去會這里找
│ │ └── accounts #存各個用戶的賬戶數據 ,一個用戶一個文件
│ │ └── 1234.json #一個用戶賬戶示例文件
│ └── log #日志目錄
│ ├── __init__.py
│ ├── access.log #用戶訪問和操作的相關日志
│ └── transactions.log #所有的交易日志
└── shopping_mall #電子商城程序,需單獨實現
└── __init__.py

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM