深刻理解 Python 特性(讀書筆記)

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

 

 

1.斷言

Python 的斷言語句是一種調試輔助功能,不是用來處理運行時錯誤的機制。python

assert 在條件爲 False 的時候觸發,後面的內容是報錯信息。緩存

import sys

assert sys.version_info >= (3, 7), "請在Python3.7及以上環境執行"

若是這個項目要求最低是 Python3.7 的環境,那麼若是使用 Python3.6 來運行這個項目,就會出現這個錯誤信息。數據結構

Traceback (most recent call last):
  File "/Users/chennan/pythonproject/demo/nyandemo.py", line 3, in <module>
    assert sys.version_info > (3, 7), "請在Python3.7以上環境執行"
AssertionError: 請在Python3.7以上環境執行

提早停止項目app

2.巧妙的放置逗號

合理的格式化列表裏面的元素,更容易維護ssh

通常咱們在寫列表的時候會這樣ide

l = ["apple", "banana", "orange"]

使用下面的方式能夠更加清晰的區分每個項目,習慣性的在末尾加個逗號,防止下次添加元素遺漏了逗號,看着也更 Pythonic函數

l = [
    "apple", 
    "banana", 
    "orange",
]

3.下劃線、雙下劃線以及其餘

前置單下劃線 : _var

1.是一種約定,前置單下劃線的方法和變量只在內部使用命令行

2.在使用通配符導包的使用 from xx import * 這種,不用導入前置單下劃線的變量,除非定義了 __all__ 覆蓋了這個行爲。PEP8 通常不建議經過這種方式導包。翻譯

後置單下劃線:  var_

若是使用的變量名被 Python 中的關鍵字佔用,好比要聲明 class 這個變量,咱們這時候能夠在其後面加個單下劃線 class_調試

這個也是 PEP8 里約定的

前置雙下劃線: __var

     前置雙下劃線會讓 Python 解釋器重寫屬性名稱,防止被子類中的命名覆蓋。
class Test:
    def __init__(self):
        self.foo = 11
        self.__bar = 2


t = Test()
print(dir(t))

查看類的屬性能夠發現 self.__bar 變爲了 _Test__bar,這也稱之爲名稱改寫 (name mangling),解釋器會更改變量的名稱,防止拓展這個類型時命名衝突。

['_Test__bar', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'foo']

這個時候若是想訪問 __bar, 怎麼辦呢,咱們能夠經過 t._Test__bar進行訪問。

若是咱們繼承一下Test而後重寫 __bar會咋樣呢

class ExtendTest(Test):
    def __init__(self):
        super().__init__()
        self.foo = "overridden"
        self.__bar = "overridden"


et = ExtendTest()
print(et.foo)
print(et.__bar)

發現出現了錯誤

AttributeError: 'ExtendTest' object has no attribute '__bar'

緣由就是前面的同樣,由於解釋器把 __bar的名字給改了防止父類的這個變量被改寫了。

咱們能夠分別訪問這兩個類的 __bar發現他們是同時存在的,確實沒有被覆蓋。

print(et._Test__bar)
print(et._ExtendTest__bar)

獲得結果

2
overridden

順便說下 __bar 在英語中通常都是叫作 dunderbar。

除了雙下劃線的變量,雙下劃線的方法名也能夠被解釋器名稱改寫。

class ManglingMethod:
    def __mangled(self):
        return 42

    def call_it(self):
        return self.__mangled()

md = ManglingMethod()
md.call_it()
md.__mangled()

運行以後獲得出錯信息

AttributeError: 'ManglingMethod' object has no attribute '__mangled'

先後雙下劃線: __var__

所謂的魔法方法,它的名稱不會被解釋器所改變,可是就命名約定而言最好避免使用這種形式變量和方法名

單下劃線: _

1._ 能夠表示變量是臨時的或者是可有可無的

for _ in rang(5):
    print("hello")

2.在數字以前使用還能夠看成是千位分隔符

for i in range(1000_000):
    print(i)

3.在解包元組的時候能夠看成是佔位符。

car = ("red", "auto", 12, 332.4 )
color,_,_,mileage = car
print(color)
print(_mileage)

4.若是使用命令行模式的話,_ 能夠獲取先前計算的結果

>>>  20+5
25
>>> _
25
>>> print(_)
25

4.自定義異常類

咱們有如下代碼

def validate(name):
    if len(name) < 10:
        raise ValueError

若是在其餘文件中調用這個方法,

validate("lisa")

在不理解這個方法的做用的時候,若是名字驗證失敗時,調用棧會打印出如下信息

Traceback (most recent call last):
  File "/Users/chennan/pythonproject/demo/nyandemo.py", line 57, in <module>
    validate("lisa")
  File "/Users/chennan/pythonproject/demo/nyandemo.py", line 55, in validate
    raise ValueError
ValueError

這個棧調試回溯中的信息指出了,出現了錯誤的值,可是並不知道爲何出錯了,因此這個時候就須要跟進這個 validate 一探究竟,

這個時候咱們就能夠本身定義一個異常類

class NameTooShortException(ValueError):
   def __str__(self):
       return "輸入的名字長度必須大於等於10"



def validate(name):
    if len(name) < 10:
        raise NameTooShortException(name)

validate("lisa")

這樣若是再出現錯誤,就能夠知道爲何錯了,同時調用法也方便捕獲指定的異常,不用再使用 ValueError。

try:
    validate("lisa")
except NameTooShortException as e:
    print(e)

5.Python字節碼

Cpython 解釋器執行時,首先將其翻譯成一系列的字節碼指令。字節碼是 Python 虛擬機的中間語言,能夠提升程序的執行效率

Cpython 不直接執行人類可讀的源碼,而是執行由編譯器解析和語法語義分析產生的緊湊的數、變量和引用。

這樣,再次執行相同程序時能節省時間和內存。由於編譯步驟產生的字節碼會以 .pyc 和 .pyo 文件的形式緩存在硬盤上,因此執行字節碼比再次執行相同的Python文件速度更快。

def greet(name):
    return 'hello, ' + name + '!'


#__code__能夠獲取greet函數用到的虛擬機指令,常量和變量
gc = greet.__code__
print(gc.co_code)  # 指令流

print(gc.co_consts)  # 常量

print(gc.co_varnames)  # 傳過來的參數
dis.dis(greet)

結果

b'd\x01|\x00\x17\x00d\x02\x17\x00S\x00'
(None, 'hello, ', '!')
('name',)
 70           0 LOAD_CONST               1 ('hello, ')
              2 LOAD_FAST                0 (name)
              4 BINARY_ADD
              6 LOAD_CONST               2 ('!')
              8 BINARY_ADD
             10 RETURN_VALUE

解釋器在索引1處('hello, ')查找常量,並放入棧中,而後將 name 的變量內容放入棧
Cpython 虛擬機是基於棧式虛擬機,棧就是虛擬機的內部數據結構。
棧只支持兩種動做:入棧和出棧
入棧:將一個值添加到棧頂
出棧:刪除並返回棧頂的值。

假設棧初始爲空,在執行前兩個操做碼(opcode)以後,虛擬的內容(0是最上面的元素)
好比咱們傳入的name爲lisa.

0: 'lisa'
1: 'hello, '

BINARY_ADD 指令從棧中彈出兩個字符串值,並將他們鏈接起來
而後再次將結果壓入棧中。

0:'hello, lisa'

而後由下一個 LOAD_CONST 將'!'壓入棧。
此時的結果

0:'!'
1:'hello, lisa'

下一個 BINARY_ADD 操做碼再次將這兩個字符串從棧中彈出並鏈接以後壓入棧,生成最終結果

0:'hello, lisa!'

最後字節碼 RETURN_VALUE ,告訴虛擬機當前位於棧頂的是該函數的返回值。

相關文章
相關標籤/搜索