Python 部分知識點總結(八)

此篇博客只是記錄第十周未掌握或不熟悉的知識點,用來加深印象。

一、魔術方法

  1. __name__:類、函數、方法等的名字
    ?? ??__module__:類定義所在的模塊名
    ? ? ?__class__:對象或類所屬的類
    ? ? ?__bases__:類的基類的元祖,順序為它們在基類列表中出現的順序
    ? ? ?__doc__:類、函數的文檔字符串,如果沒有定義則為?None
    ? ? ?__mro__:類的?mro,class.mro( )?返回的結果保存在 __mro__?中
    ?? ??__dict__:類或實例的屬性,可寫的字典
  2. 查看屬性
    __dir__:返回類或者對象的所有成員名稱列表,dir()?函數就是調用 __dir__(),如果提供?__dir__(),則返回屬性的列表(返回值要求必須是可迭代,且只對實例有影響),否則會盡量從?__dir__?屬性中收集信息
    dir()?對于不同類型的對象具有不同的行為:
    ?? ??? ?? ? 模塊對象,返回的列表包含模塊的屬性名
    ?? ??? ?? ? 類型或類對象,返回的列表包含類的屬性名,以及它的基類的屬性名
    ?? ??? ?? ? 否則,返回列表包含對象的屬性名、它的類的屬性名和類的基類的屬性名
  3. __hash__:內建函數?hash()?調用的返回值,返回一個整數,如果定義這個方法,該類的實例就可?hash
    __eq__:對應 ==?操作符,判斷兩個對象是否相等,返回?bool?值
    __hash__?方法只是返回一個?hash?值作為?set?的?key,但是去重,還需要? __eq__?來判斷兩個對象是否相當,如果?hash?值相等,只是?hash?沖突,不能說明兩個對象是相等的。
    因此,一般來說,提供 __hash__?方法是為了作為?set?或者?dict?的?key,所以去重要同時提供 __eq__?方法
    列表類不可哈希的原因就是因為源碼中有一句 __hash__ = None,也就是說如果調用 __hash__()?相當于?None(),一定報錯
  4. __bool__:內建函數?bool(),或者對象放在邏輯表達式的位置,調用這個函數返回布爾值。沒有定義 __bool__(),就找 __len__() 返回長度,非 0?位就是真。如果 __len__()?也沒有定義,那么所有實例都返回真
  5. 可視化
    __repr__:內建函數?repr()?對一個對象獲取字符串表達,調用 __repr__?方法返回字符串表達,如果 __repr__?沒有定義,就直接返回?object ,即顯示內存地址信息
    __str__:str()?函數、內建函數?format()、print()?函數調用,需要返回對象的字符串表達,如果沒有定義,就去調用 __repr__?方法返回字符串表達,如果 __repr__?沒有定義,就直接返回對象的內存地址信息
    __bytes__:byte()?函數調用,返回一個對象的?bytes?表達,即返回?bytes?對象
    class A:
    ????def __init__(self, name, age=18):
    ????????self.name = name
    ????????self.age = age
    ????def __repr__(self):
    ????????return ‘repr: {},{}’.format(self.name, self.age)
    ????def __str__(self):
    ????????return ‘str: {},{}’.format(self.name, self.age)
    ????def __bytes__(self):
    ????????# return ‘repr: {},{}’.format(self.name, self.age)
    ????????import json
    ????????return json.dumps(self.__dict__).encode()
    a = A(‘tom’)
    print(a)? –> 調用 __str__
    print(bytes(a))
    print([a])??–> 列表使用 __str__,但其內部使用 __repr__
    print(str(a))
    print(‘str:a, 1’)??–> 字符串直接輸出沒有引號
    s = ‘1’
    print(s)
    print([‘a’], (s,))??–>? 字符串在基本數據類型內部輸出有引號
    print({s, ‘a’})

    str: tom,18
    b'{“name”: “tom”, “age”: 18}’
    [repr: tom,18]
    str: tom,18
    str:a, 1
    1
    [‘a’] (‘1’,)
    {‘1’, ‘a’}
  6. 運算符重載
    <、<=、==、>、>=、!=? 對應? __lt__、__le__、 __eq__?、__gt__、 __ge__、__ne__
    +、-、*、/、%、//、**、divmod? 對應? __add__、__sub__、__mul__、__truediv__、__mod__、__floordiv__、__pow__、__divmod__
    +=、-=、*=、/=、%=、//=、**=? 對應? __iadd__、__isub__、__imul__、__itruediv__、__imod__、__ifloordiv__、__ipow__
  7. __lt__、__le__、 __eq__?、__gt__、 __ge__、__ne__ 是比較大小必須實現的方法,但是全部寫完太麻煩,使用 @functools.total_ordering 裝飾器就可以大大簡化代碼,但是要求 __eq__ 必須實現,其它方法實現其一即可
    from functools import total_ordering
    @total_ordering
    class Person:
    ????def __init__(self, name, age):
    ????????self.name = name
    ????????self.age = age
    ????def __eq__(self, other):
    ????????return self.age == other.age
    ????def __gt__(self, other):
    ????????return self.age > other.age
  8. 容器相關方法
    __len__:內建函數 len(),返回對象的長度(>=0 的整數),如果把對象當作容器類型看,就如同 list 或者 dict。bool() 函數調用的時候,如果沒有 __bool__ 方法,則會看 __len__ 方法是否存在,存在返回非 0 為真
    __iter__:迭代容器時調用,返回一個新的迭代器對象
    __contains__:in?成員運算符,沒有實現,就調用 __iter__?方法遍歷
    __getitem__:實現?self[key]?訪問,序列對象,key?接受整數為索引,或者切片。對于?set?和?dict,key?為?hashable。key?不存在引發?KeyError?異常
    __setitem__:和 __getitem__?的訪問類似,是設置值的方法
    __missing__:字典或其子類使用 __getitem__()?調用時,key?不存在執行該方法
  9. 購物車代碼實現:
    class Cart:
    ????def __init__(self):
    ????????self.items = []
    ????def __len__(self):
    ????????return len(self.items)
    ????def additem(self, item):
    ????????self.items.append(item)
    ????def __iter__(self):
    ????????return iter(self.items)
    ????def __getitem__(self, index):
    ????????return self.items[index]
    ????def __setitem__(self, key, value):
    ????????self.items[key] = value
    ????def __str__(self):
    ????????return str(self.items)
    ????def __add__(self, other):
    ????????self.items.append(other)
    ????????return self
    cart = Cart()
    cart.additem(1)
    cart.additem(‘abc’)
    cart.additem(3)
    print(len(cart))
    print(bool(cart))
    for x in cart:
    ????print(x)
    print(3 in cart)
    print(2 in cart)
    print(cart[1])
    cart[1] = ‘xyz’
    print(cart)
    print(cart + 4 + 5 + 6)
    print(cart.__add__(17).__add__(18))

    3
    True
    1
    abc
    3
    True
    False
    abc
    [1, ‘xyz’, 3]
    [1, ‘xyz’, 3, 4, 5, 6]
    [1, ‘xyz’, 3, 4, 5, 6, 17, 18]
  10. 函數即對象,對象 foo 加上 (),就是調用對象的 __call__() 方法
    ?? ??? ?__call__():類中定義一個此方法,實例就可以像函數一樣調用
  11. 緩存數據,便于檢索的斐波那契數列
    class Fib:
    ????def __init__(self):
    ????????self.items = [0, 1, 1]
    ????def __call__(self, index):
    ????????return self[index]
    ????def __iter__(self):
    ????????return iter(self.items)
    ????def __len__(self):
    ????????return len(self.items)
    ????def __getitem__(self, index):
    ????????if index <= 0:
    ????????????raise IndexError(‘Wrong Index’)
    ????????if index < len(self.items):
    ????????????return self.items[index]
    ????????for i in range(len(self), index+1):
    ????????????self.items.append(self.items[i-1] + self.items[i-2])
    ????????return self.items[index]
    ????def __str__(self):
    ????????return str(self.items)
    ????__repr__ = __str__
    f = Fib()
    print(f(10), len(f))
    print(f(5), len(f))
    for x in f:
    ????print(x)
    print(f[5], f[6])

二、上下文管理

  1. 當一個對象同時實現了 __enter__() 和 __exit__() 方法,它就屬于上下文管理的對象
    __enter__:進入與此對象相關的上下文,如果存在該方法, with 語法會把該方法的返回值作為綁定到 as 子句中指定的變量上
    __exit__:退出與此對象相關的上下文
    實例調用順序是:先調用 __init__,再調用 __enter__,再執行 with 內部的語句塊,最后調用 __exit__
    ?? ??? ??? ??? ??? ??? ?? ?如果 with 內部語句塊有錯誤,也會調用 __exit__,所以上下文管理是安全的
    ?? ??? ??? ??? ??? ??? ?? ?如果調用 sys.exit(),會退出當前解釋器,但是還是會調用 __exit__
  2. __enter__?方法沒有其他參數
    __exit__(self, exc_type, exc_value, traceback)如果該上下文退出時沒有異常,這三個參數都為?None;如果有異常,exc_type?代表異常類型,exc_value?代表異常的值,traceback?代表異常的追蹤信息
    但是如果 __exit__?方法返回一個等效?True?的值,則壓制異常,否則,繼續拋出異常
    class Point:
    ????def __init__(self):
    ????????print(‘init’)
    ????def __enter__(self):
    ????????print(‘enter’)
    ????????return self
    ????def __exit__(self, exc_type, exc_val, exc_tb):
    ????????print(‘type:’, exc_type)
    ????????print(‘val:’, exc_val)
    ????????print(‘tb:’, exc_tb)
    ????????print(‘exit’)
    ????????return “abc”
    p = Point()
    with p as f:
    ????raise Exception(‘New Error’)
    ????print(‘do sth’)
    print(‘outer’)

    init
    enter
    type: <class ‘Exception’>
    val: New Error
    tb: <traceback object at 0x000001A4F146BE08>
    exit
    outer
  3. 類既可以用在上下文管理,又可以用作裝飾器
    import datetime
    import time
    from functools import wraps
    class TimeIt:
    ????def __init__(self, fn):
    ????????self.fn = fn
    ????????wraps(fn)(self)
    ????def __enter__(self):
    ????????self.start = datetime.datetime.now()
    ????????return self
    ????def __exit__(self, exc_type, exc_val, exc_tb):
    ????????delta = (datetime.datetime.now() – self.start).total_seconds()
    ????????print(self.fn.__name__, delta)
    ????def __call__(self, *args, **kwargs):
    ????????self.start = datetime.datetime.now()
    ????????ret = self.fn(*args, **kwargs)
    ????????self.delta = (datetime.datetime.now() – self.start).total_seconds()
    ????????print(self.fn.__name__, self.delta)
    ????????return ret
    @TimeIt
    def add(x, y):
    ????“””This is function.”””
    ????time.sleep(2)
    ????return x + y
    print(add(10, 5))
    print(add.__doc__)
    print(‘====================’)
    with TimeIt(add) as timeitobj:
    ????print(timeitobj(11, 122))
    print(TimeIt(add).__doc__)

    add 2.000007
    15
    This is function.
    ====================
    add 2.000533
    133
    add 2.000533
    This is function.
  4. 上下文應用場景
    ?? ?? ? 增強功能:在代碼執行的前后增加代碼,以增強其功能,類似裝飾器的功能
    ?? ?? ? 資源管理:打開了資源需要關閉,例如 文件對象、網絡連接、數據庫連接等
    ?? ?? ? 權限驗證:在執行代碼之前,做權限的驗證,在 __enter__ 中處理
  5. contextlib.contextmanager:它是一個裝飾器實現上下文管理,裝飾一個函數,而不用像類一樣實現,__enter__ 和 __exit__ 方法
    要求:必須有?yield,就算增加一個異常,也能保證?exit?的執行
    ?? ??? ?? yield?之前的當做 __enter__?方法執行
    ?? ?? ? ? yield?之后的當做 __exit__?方法執行
    ?? ?? ? ? yield?的值作為 __enter__?的返回值
    import contextlib
    import datetime
    import time
    @contextlib.contextmanager
    def add(x, y):
    ????start = datetime.datetime.now()
    ????try:
    ????????yield x + y
    ????finally:
    ????????delta = (datetime.datetime.now() – start).total_seconds()
    ????????print(delta)
    with add(4, 5) as f:
    ????# raise Exception(‘Error’)
    ????time.sleep(2)
    ????print(f)

三、反射

  1. 運行時,區別于編譯時,指的是程序被加載到內存中執行的時候
    ?? ??反射,reflection,指的是運行時獲取類型定義信息
    ? ? ?在 Python?中,能夠通過一個對象,找出其?type、class、attribute?或者?method?的能力,稱為反射或自省
    ? ? ?具有反射能力的函數有:type()、isinstance()、callable()、dir()、getattr()
  2. getattr(object, name[, default]):通過?name?返回?object?的屬性值,當屬性不存在,將使用?default?返回,如果沒有?default,則拋出?AttribteError。?name?必須為字符串
    setattr(object,?name, value):object?的屬性存在,則覆蓋,不存在,則新增
    hasattr(object, name):判斷對象是否有這個名字的屬性,name?必須為字符串
    這種動態增刪屬性的方式是運行時改變類或者實例的方式,但是裝飾器或?Mixin?都是定義時就決定了,因此反射能力具有更大的靈活性
  3. 通過名稱找對象的方法,命令分發器的代碼實現
    class Dispatcher:
    ????def __init__(self):
    ????????self._run()
    ????def cmd1(self):
    ????????print(“I’m cmd1”)
    ????def cmd2(self):
    ????????print(“I’m cmd2”)
    ????def _run(self):
    ????????while 1:
    ????????????cmd = input(‘Please input a command:’).strip()
    ????????????if cmd == ‘quit’:
    ????????????????break
    ????????????getattr(self, cmd, lambda : print(‘Unknow Command {}’.format(cmd)))()
    Dispatcher()
  4. __getattr__:一個類的屬性會按照繼承關系找,如果找不到,就會執行 __getattr__? 方法,如果沒有此方法,則會拋出 AttributeError 異常找不到屬性
    屬性查找順序為:instance.__dict__? –> instance.__class__.__dict__? –>? 繼承的祖先類(直到 object)的 __dict__? –> 找不到? –>? 調用 __getattr__()
    __setattr__:實例通過 點 設置屬性,如同 self.x = x,就會調用 __setattr__(),屬性要加到 __dict__ 中,就需要自己完成
    此方法可以攔截對實例屬性的增加、修改操作,如果要設置生效,需要自己操作實例的 __dict__
    ????def __setattr__(self, key, value):
    ????????print(key, value)
    ????????self.__dict__[key] = value
    __delattr__:可以阻止通過實例刪除屬性的操作,但是通過類依然可以刪除屬性
    __getattribute__:實例的所有屬性訪問,第一個都會調用此方法,它阻止了屬性的查找,該方法應該返回(計算后的)值或者拋出?AttributeError?異常
    它的?return?值將作為屬性查找的結果,如果拋出?AttributeError?異常,則會直接調用 __getattr__?方法,因為表示屬性沒有找到
    為了避免在該方法中無限的遞歸,它的實現應該永遠調用基類的同名方法,以訪問需要的任何屬性,例如?object.__getattribute__(slef, name)
  5. 總結
    __getattr__():當通過搜索實例、實例的類及祖先類查不到屬性,就會調用此方法
    __setattr__():通過?點?訪問實例屬性,進行增加、修改都要調用它
    __delattr__():當通過實例來刪除屬性時調用此方法
    __getattribute__:實例所有的屬性調用都從這個方法開始

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/98377

(0)
龐豪龐豪
上一篇 2018-05-13
下一篇 2018-05-13

相關推薦

欧美性久久久久