Python基礎簡單總結

python語言的特點

  1. 解釋性語言,無需編譯
  2. 語法簡單
  3. 開源,模塊豐富

深淺拷貝的區別

Python 中的copy.copy()和 copy.deepcopy()實現了深淺拷貝

import copy

a = [[1,2], 'gly', 666]

b = copy.copy(a)
c = copy.deepcopy(a)

print(b)
print(c)

print(id(a[0]))
print(id(b[0]))
print(id(c[0]))
print('-------')
print(id(a[1]))
print(id(b[1]))
print(id(c[1]))


a[0][0] = 0
a[1] = 'gly qaq'
print('-------')
print(id(a[1]))
print(id(b[1]))
print(id(c[1]))


print(id(a[0]))
print(id(b[0]))
print(id(c[0]))

print(b)
print(c)
[[1, 2], 'gly', 666]
[[1, 2], 'gly', 666]
1508853212808
1508853212808
1508853213320
-------
1508853208624
1508853208624
1508853208624
-------
1508853207216
1508853208624
1508853208624
1508853212808
1508853212808
1508853213320
[[0, 2], 'gly', 666]
[[1, 2], 'gly', 666]

可以發現,淺拷貝是對拷貝對象地址指針的引用,當copy對象的可變對象的值發生改變時,當前對象的值也發生了改變。
對於深拷貝來說,對於copy對象的每一個可變對象都建立了一個新的副本,源數據的改變並不會應到到深拷貝對象的值。
當然,對於那些不可變對象,如int,string,float,tuple等,深淺拷貝並無區別。

Python中的三元表達式

python中的三元表達式的結構爲:
res = ‘變量1’ if 條件 else ‘變量2’

如果條件爲True 則 res 賦值爲變量1 ,否則爲變量2

Python中多線程的原理和實現

  1. 什麼是線程
    線程是處理器進行調度的基本單元

  2. 與進程的區別
    一個進程可以擁有多個線程,進程是線程的容器,進程是資源分配的最小單位,而線程是處理器調度的基本單元

  3. Python中的多線程模塊 threading.Thread

  4. 線程之間的同步

    1. 可以使用Lock 或者RLock 來實現需要注意的是,Lock 只能acquire一次, Rlock可以acquire多次,但是兩者releae的次數都要和acquire的次數相等。
    2. queue來實現,queue.Queue 中的 get和 task.done()類似於 acquire 和 release
  5. 由於全局解釋器鎖的存在 GIL Python 中同時只能由一個線程在運行(我們看起來像是並行)。CIL並不是Python語言的特性,Python也完全可以不依賴於GIL。

Python中的匿名函數 lambda

b = lambda x:x+1
print(b(1))

2

可以看出來,匿名函數就像是函數的簡單寫法,前一個x爲參數,後一個x爲返回值。
另一個使用匿名函數的簡單例子
將數組裏的值都加1

a = [1,2,3,4,5]

b = list(map(lambda x:x+1, a))
print(b)

[2, 3, 4, 5, 6]

說明: map函數接收兩個參數,前一個爲函數,後一個爲可迭代對象,對象裏的每個值都執行一次lambda函數

Python 中的列表推導式

語法:
a = [表達式 for 變量 in 列表 ]

Python 中的生成器和迭代器(重要)

迭代器

1. 迭代是訪問集合對象的一種方式
2. 迭代器可以記住訪問對象的位置
3. 迭代器從訪問對象的第一個位置開始訪問,只能前進不能後退
4. 迭代器有兩個基本的方法,iter()和next()

創建一個迭代器

list = [1,2,3,4,5]
it = iter(list)
print(next(it))
print(next(it))

1
2

iter()方法創建一個迭代器對象,next()用於訪問迭代器的下一個元素。

使用for循環遍歷

list = [1,2,3,4,5]
it = iter(list)
for i in it:
    print(i)
1
2
3
4
5
# 遍歷字典
dic = {
    1 :'gly',
    2 : 'gly 666'
}

for i in iter(dic):
    print(i,dic[i])
1 gly
2 gly 666

創建一個迭代器

  1. 把一個類作爲迭代對象需要在類中實現iter()方法和 next()方法
  2. iter 方法返回一個迭代器對象,這個迭代器對象實現了next方法,並且通過StopIteration標識迭代完成
  3. next 方法,返回對象的值,並讓當前的迭代器指向下一個對象(其實就是根據規則更新對象的內容)。
    具體實現
class test():
    
    
    def __iter__(self):
        self.a = 1
        return self
    
    def __next__(self):
        x = self.a
        self.a += 1
        return x
    
    

myclass = test()
myiter = iter(myclass)

print(next(myiter))
print(next(myiter))

1
2

上述代碼如果用for循環去遍歷的話就會無限的迭代下去,如果是迭代停止呢?

StopIteration 異常用來標識一個迭代的終點

class test():

    def __iter__(self):
        self.a = 1
        return self

    def __next__(self):
        x = self.a
        if x <= 5:
            self.a += 1
            return x
        else:
            raise StopIteration


myclass = test()
myiter = iter(myclass)

while True:
    try:
        print(next(myiter))
    except:
        print('stopiteration')
        break
1
2
3
4
5
stopiteration

這種寫法寫起來比較麻煩,for循環能夠幫助我們判斷sotpiteration異常

class test():

    def __iter__(self):
        self.a = 1
        return self

    def __next__(self):
        x = self.a
        if x <= 5:
            self.a += 1
            return x
        else:
            raise StopIteration


myclass = test()
myiter = iter(myclass)

for i in myiter:
    print(i)
1
2
3
4
5

生成器

在Python中使用了yield關鍵字的函數稱之爲生成器(generator)

當調用生成器時返回的是一個迭代器對象。簡單理解生成器就是:返回迭代器的函數

在調用生成器的過程中,每次遇到yield的時,函數會保存當前的所有信息並返回yield 的值。直到
下一次調用next函數時,函數接着上次保存的狀態繼續執行,直到遇到下一個yield。

使用yield 實現斐波那契數列

def fib(max):
    n, a, b = 0, 1, 1
    
    while(n<max):
        a, b = b, a+b
        yield b
        n += 1
    return 'done'


f = fib(5)

print(next(f))
print(next(f))

2
3

使用for循環遍歷

def fib(max):
    n, a, b = 0, 1, 1

    while (n < max):
        a, b = b, a + b
        yield b
        n += 1
    return 'done'


f = fib(5)


for it in f:
    print(it)

2
3
5
8
13

但是我們發現上述函數並沒有拿到最終的返回值done

原因是:for循環幫助我們處理了StopIteration 的異常,但是最終的返回值就在StopIteration的value中

def fib(max):
    n, a, b = 0, 1, 1

    while (n < max):
        a, b = b, a + b
        yield b
        n += 1
    return 'done'


f = fib(5)

while True:
    try:
        print(next(f))
    except StopIteration as e:
        print(e.value)
        break
2
3
5
8
13
done

這樣我們就拿到了,fib函數的返回值。

補充send 的作用

def test():

    i = 1
    print('aaa')
    tmp = yield i*3
    print(tmp)

    if tmp == 'hello':
        print('send hello')


f = test()
try:

    print(next(f))
    f.send('hello')
except StopIteration as e:
    print(e)
aaa
3
hello
send hello

說明:第一次調用next會使生成器執行到tmp==yield 這裏然後掛起,這時候打印出來的值就是3
當我們send的時候,相當於給tmp賦值爲send的內容,並且讓生成器函數繼續執行。

生成器表達式

a = (x * 3 for x in range(5))
print(a)

for i in iter(a):
    print(i)
    
<generator object <genexpr> at 0x0000015F4EB2D3C8>
0
3
6
9
12

生成器表達式用(),他與生成器函數並無二至,只是生成器的兩種寫法。

總結:

  1. 迭代器 ,任何可迭代對象都有迭代器,如果要實現一個可迭代的類,則需要在類中實現iter方法,和next方法。其中iter方法返回一個迭代器,也就是對象本身。next方法,返回一個值,並且使當前的迭代器指向下一個值。迭代的終點用StopIteration表示,for循環能夠幫助我們處理這個異常,而next方法則需要手動處理。
  2. 生成器, 當一個函數使用了yield 使。這個函數就是一個生成器函數,簡單理解生成器就是返回迭代器的函數,當我們調用函數名+()時即得到了一個迭代器。當我們遇到yield時會 返回yield的值並記錄當前所有的信息,直到下一次調用next函數時纔回到上一次的位置繼續運行。 我們也可以使用send函數來使生成器函數繼續運行,並且傳入一個值。
  3. 生成器的使用情景; 一個簡單的使用情景就是使用生成器實現斐波那契數列,我們在實現的過程中並不需要用一個列表把所有的項都記錄下來,而是在求出一項個輸出,然後調用next函數讓生成器函數繼續執行。這樣比較節省內存。

Python中的裝飾器 decorators

什麼是裝飾器

裝飾器就是在不改變原函數(類)名的情況下,給函數添加新的功能

大致看了一下博客,然後再回頭總結。

理解裝飾器之前,首先要知道,python中

  1. 函數可以嵌套
  2. 函數的參數可以是一個函數
  3. 函數的返回值也可以是一個函數
  4. 函數名就相當於指向該函數的指針,我們是可以改變他的指向,讓他指向另一個函數的。
    理解以上三點,就可以實現一個簡單的裝飾器了。

def first_decorator(f):

    def wrap():
        print('doing something before %s'% f.__name__)
        f()
        print('doing something after %s'% f.__name__)
    return wrap


def foo():
    print('i am foo')


foo = first_decorator(foo)

foo()

doing something before foo
i am foo
doing something after foo

這正是裝飾器起到的作用,我們在不修改foo函數的情況下,給foo函數添加了一些其他的功能。

但是這會出現另一個問題

from functools import wraps
def first_decorator(f):
    def wrap():
        """
        我是wrap的註釋
        :return:
        """
        print('doing something before %s'% f.__name__)
        f()
        print('doing something after %s'% f.__name__)
    return wrap


def foo():
    """
    我是foo的註釋
    :return:
    """
    print('i am foo')


foo = first_decorator(foo)

foo()

print(foo.__name__)
print(foo.__doc__)


doing something before foo
i am foo
doing something after foo
wrap

        我是wrap的註釋
        :return:

我們發現foo函數的名字和文檔被wrap替換掉了。
其實這個時候foo函數已經等同於wrap函數了。
如何避免這種情況發生呢。

from functools import wraps
def first_decorator(f):
    @wraps(f)
    def wrap():
        """
        我是wrap的註釋
        :return:
        """
        print('doing something before %s'% f.__name__)
        f()
        print('doing something after %s'% f.__name__)
    return wrap


def foo():
    """
    我是foo的註釋
    :return:
    """
    print('i am foo')


foo = first_decorator(foo)

foo()

print(foo.__name__)
print(foo.__doc__)


doing something before foo
i am foo
doing something after foo
foo

    我是foo的註釋
    :return:

只需在wrap前加上@wraps(函數名)即可。
wraps是python functools包中的一個模塊。
@wraps接受一個函數來進行裝飾,並加入了複製函數名稱、註釋文檔、參數列表等等的功能。這可以讓我們在裝飾器裏面訪問在裝飾之前的函數的屬性
同時我們也能用更簡單的一個方法來裝飾一個函數

from functools import wraps
def first_decorator(f):
    @wraps(f)
    def wrap():
        """
        我是wrap的註釋
        :return:
        """
        print('doing something before %s'% f.__name__)
        f()
        print('doing something after %s'% f.__name__)
    return wrap


@first_decorator
def foo():
    """
    我是foo的註釋
    :return:
    """
    print('i am foo')


foo()

print(foo.__name__)
print(foo.__doc__)


doing something before foo
i am foo
doing something after foo
foo

    我是foo的註釋
    :return:

在函數foo上面加一個@first_decorator 就相當於實現了
foo = first_decorator(foo)
然後直接調用foo就可以了。

帶參數的裝飾器

from functools import wraps


def first_decorator(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        print('do sth before')
        return f(*args, **kwargs)
    return wrap


@first_decorator
def foo(*args, **kwargs):
    print(args)
    print(kwargs)
    return args[0]*args[0]

print(foo(4,5,6,name='gly', age=20))

do sth before
(4, 5, 6)
{'name': 'gly', 'age': 20}
16

上面是一個帶參數的裝飾器的例子。

裝飾器的使用藍本

from functools import wraps


def first_decorator(f):
    @wraps(f)
    def wrap():
        if not can_run:
            return ("%s cant run"% f.__name__)
        else:
            return f()
    return wrap


@first_decorator
def foo():

    return ("foo is running")


can_run = False

print(foo())


can_run = True

print(foo())

foo cant run
foo is running

注意這裏裝飾器中的寫法,return 的是f(),當然這裏可以加一些參數,這裏相當於
執行了f函數,然後把結果返回了。
相當於
res = f()
return res

內置的裝飾器

staticmethod

staticmethod裝飾器用來去掉定義的類方法默認傳入的第一個參數(也就是self),使得該方法與普通的函數一樣,並且不實例化也能調用該方法
class myclass(object):

    name = 'gly'

    def show(self): # 普通的類方法
        print('qaq')


    @staticmethod
    def show1():
        print('hahaha!')


print(myclass.name)
myclass.show1()
gly
hahaha!

name是類屬性,show是類方法,當類沒有實例化爲對象時,我們時不能調用類方法。
但是有時候我們希望運用一些類方法像運用普通的函數一樣,這個時候就需要staticmethod裝飾器

classmethod

類方法裝飾器,當我們爲一個類加上類方法裝飾器時,第一個參數默認是cls,類方法是和類綁定的方法,不是和實例綁定的方法。類方法可以修改類的狀態並應用到實例中。

property

property是一個裝飾器,包含三種方法,deleter,setter,getter,分別對應刪除屬性,設置屬性的值,和取屬性的值。
class student(object):

    def get_soc(self):
        return self.soc

    def set_soc(self, val):
        if not isinstance(val, int):
            raise ValueError('soc must be int')
        if val < 0 or val > 100:
            raise ValueError('soc must between 0-100')
        self.soc = val

    def del_soc(self):
        print('del soc')
        del self.soc


s = student()
s.set_soc(100)
print(s.get_soc())
s.del_soc()

100
del soc

上述代碼分別對應了設置值,取值和刪除值。但是這樣寫起來比較麻煩,能不能像訪問屬性一樣修改soc,同時又能檢查設置的soc是否合法呢?

class student(object):

    @property
    def soc(self):
        return self._soc

    @soc.setter

    def soc(self, val):
        if not isinstance(val, int):
            raise ValueError('soc must be int')
        if val < 0 or val > 100 :
            raise ValueError('sco must between 0-100')
        self._soc = val


s = student()
s.soc = 99   # 相當於 s.set_soc
print(s.soc) # 相當於 s.get_soc

99

這樣,我們在設置_soc的值的時候,即沒有暴漏屬性名,也能夠在設置值的是否檢查是否符合規範。

Python 中的異常處理

Python進階的作者Muhammad Yasoob Ullah Khalid. 說:
異常處理是一門藝術,一旦你掌握,回授予你無窮的力量。

1. try/except 從句。

將可能會除發異常的代碼放在try裏,在except裏邊處理捕捉到的異常
1個簡單的例子

try:
    s = 1/0
except ZeroDivisionError as e:
    print(e)

division by zero

2. 處理多個異常

try:
    f = open('text.txt', 'rb')
except EOFError as e:
    print('an error occurred')
except IOError as e:
    print('other error occurred!')

other error occurred!

3. 捕獲所有異常

try:
    f = open('a.txt')
except Exception:
    # 打印異常日誌
    raise
---------------------------------------------------------------------------

FileNotFoundError                         Traceback (most recent call last)

<ipython-input-29-2fe564bb1386> in <module>
      1 try:
----> 2     f = open('a.txt')
      3 except Exception:
      4     raise


FileNotFoundError: [Errno 2] No such file or directory: 'a.txt'

finally 從句

finally從句不管異常是否除發,都會執行,一般用於腳本執行後的收尾工作。

try:
    f = open('file.txt')
except Exception as e:
    print(e)
finally:
    print("nothing happend")
[Errno 2] No such file or directory: 'file.txt'
nothing happend

try/except/else/finally

try:
    ...
except:

else:
    ....
finally:
    .....

else語句裏邊的代碼只會在try裏面的語句沒有拋出異常時執行。

用戶自定義異常

class myerror(Exception):

    def __init__(self, arg):
        self.arg = arg


try:
    raise myerror('an error occurred')
except myerror as e:
    print(e)
an error occurred

用戶自定義的異常需要創建自己的異常類,並且可以直接或者間接的繼承自Exception

Python中的內存管理

內存池的概念:
頻繁的創建釋放消耗小內存的對象時,會使效率低。內存池就是事先申請一定數量,大小相等的內存塊放在內存池中,當有的對象需要分配內存時,直接從內存池中分配,如果不夠的話就再申請內存。

Python中的內存管理機制-pymalloc:
針對小對象,大小小於256bits時,直接從內存池中申請,當大於256bits直接執行new/malloc來申請空間。

內從空間的釋放:當一個對象的引用計數變爲0時,系統會調用它的析構函數,在析構時,如果內存來自內存池,則會把內存返回到內存池中,以避免頻繁的釋放。

發佈了332 篇原創文章 · 獲贊 69 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章