Python高級編程和異步IO並發編程(一)


Python高級編程和異步IO並發編程


 一、類與對象

 1、抽象基類(abc模塊)

# 可判斷某類是否有某方法
hasattr(obj, '__len__')


# 我們在某些情況之下希望判定某個對象的類型
isinstance(obj, list)


# 我們需要強制某個子類必須實現某些方法
# 如:實現了一個web框架,集合cache(如希望可替換成 redis, cache, memorychache 之一)
# 則需要設計一個抽象基類,指定子類必須實現某些方法
# 如何去模擬一個抽象基類:
class CacheBase():
    def get(self, key):
        raise NotImplementedError  #子類未重載該方法時,調用該方法會拋出異常
    def set(set, key, value):
        raise NotImplementedError

class RedisCache(CacheBase):
    pass

redis_cache = RedisCache()
# 調用時會拋出異常
redis_cache.set('key', 'value')

# 如果我們希望初始化時就拋出異常:
import abc
class CacheBase(metaclss=abc.ABCMeta):
    @abc.abstractmethod
    def get(self, key)
        pass

    @abc.abstractmethod
    def set(set, key, value):
        raise NotImplementedError

class RedisCache(CacheBase):
    pass

# 初始化時會拋出異常
redis_cache = RedisCache()

 


 2、isinstance 與 type 區別

class A:
    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
# type 無法找出繼承關系
print(type(b) is A)  # 返回:False

 

因此,類型判斷一般使用isinstance ,少用type


 3、類變量 和 對象變量

類變量:類 及 對象均可以調用,不同點在於:類調用時如果修改該變量數據,則在該類中數據被永久修改;對象在調用該變量時,如果是修改該變量,此時修改的是自己對象中該變量的值,不會影響到類中該變量的值

對象變量:類不能調用,只能對象可以調用

 

私有屬性/方法:在類當中定義私有屬性/方法 ,只能在類內使用,不能在類之外被點語法等調用。私有屬性(__name)在python內部中實際是是被結構化處理成:_classname__name,在類外層調用,不能直接調用__name屬性,但可以調用

_classname__name 來獲取私有屬性的值,其中classname是類的名稱


 

 4、super函數

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A):
    def __init__(self):
        print('C')
        super().__init__()

class D(B, C):
    def __init__(self):
        print('D')
        super(D, self).__init__()

if __name__ == '__main__':
    print(D.__mro__)
# 返回:(<class '__main__.D'>,<class '__main__.B'>,<class '__main__.C'>,<class '__main__.A'>,<class 'object'>)
    d = D()
# 返回:
#   D
#   B
#   C
#   A

super函數的調用,不是調用父類的放法,而是按MRO算法順序調用的,具體參考上述代碼


 

5、上下文管理器 with語句

with 上下文管理器有兩個魔法函數:

  • __enter__ :程序啟動時調用
  • __exit__   :程序結束時調用
# 上下文管理器協議
class Sample():
    def __enter__(self):
        print('enter')
        #作用獲取資源,自動調用
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        #作用釋放資源,自動調用
        print('exit')
    def do_something(self):
        print('doing something')

with Sample() as sample:
    sample.do_something()
# 返回:
#   enter
#   doing something
#   exit

 

 python 內置模塊:contextlib 簡化上下文管理器:

import contextlib

@contextlib.contextmanager # 加上contextlib裝飾器,將file_open函數變成上下文管理器
def file_open(file_name):
# yield 前相當於 __enter__,yield 后相當於 __exit__
    print('file open')
    yield {}
    print('file end')

with file_open('filename.txt') as f_opened:
    print('file processing')
# 返回:
#   file open
#   file processing
#   file end

 

二、自定義序列類 

 1、序列類型的分類:

 

  • 容器序列:list、tuple、deque    --可放置任何類型
  • 扁平序列:str、bytes、bytearray、array.array    --可遍歷的、放置的數據類型需相同
  • 可變序列:list、deque、bytearray、array
  • 不可變序列:str、tuple、bytes

 append、extend區別:

 append:將數據原原整整追加到對象中,如a=[1,2],a.append((3,4)),結果:[1,2,(3,4)]

 extend:將數據追加到對象中,以自身類型為准。如a=[1,2],a.extend([3,4]),結果:[1,2,3,4]


 

 2、實現可切片的對象

 

import numbers

class Group:
    def __init__(self, group_name, company_name, staffs)
        self.group_name = group_name
        self.company_name = company_name
        self.staffs = staffs

    def __reversed__(self):  # 實現數據反轉功能
        self.staffs.reverse()

    def __getitem__(self, item): # 實現可切片功能
        cls = type(self)
        if isinstance(item, slice):
            return cls(group_name=self.group_name, company_name=self.company_name, staffs=self.staffs[item])
        else isinstance(item, numbers.Integral):
            return cls(group_name=self.group_name, company_name=self.company_name, staffs=[self.staffs[item]])

    def __len__(self):  # 實現len()查看長度功能
        return len(self.staffs)

    def __iter__(self):  # 實現可迭代功能
        return iter(self.staffs)

    def __contains__(self, item):  # 實現if判斷功能
        if item in self.staffs:
            return True
        else:
            return False

staffs = ['MJ1','MJ2','MJ3','MJ4']
group = Group(company_name='i', group_name='user', staffs=staffs)
sub_group = group[:2]   # 調用getitem方法
len(group)   # 調用__len__方法
for i in group :    # 調用__iter__方法
if sb in group:     # 調用 __contains__方法
### 通過實現相關的魔法函數,可以使自定義的類具有某些功能 ###

 

 3、bisect 維護已排序的序列

import bisect

# 用來處理已排序的序列,用來維持已排序的序列,升序
# 內部使用的是二分查找法
inter_list = deque()
bisect.insort(inter_list, 3) # 插入數據,默認在右邊插入
bisect.insort(inter_list, 2)
bisect.insort(inter_list, 5)
bisect.insort(inter_list, 1)
bisect.insort(inter_list, 6)

print(bisect.bisect(inter_list, 3))  # 查找數據位置,   返回:3
print(bisect.bisect_left(inter_list, 3))  # 返回:2
print(inter_list)  # 返回:[1,2,3,5,6]

 

 4、列表推導式、生成器表達式、字典推導式

  • 列表推導式性能高於列表操作

 列表生成式(列表推導式):

# 1. 提取出1~20之間的奇數
odd_list = []
for i in range(21):
    if i%2 == 1:
        odd_list.append(i)
print(odd_list)

# 列表推導式:
odd_list = [i for i in range(21) if i % 2 == 1]
print(odd_list)


# 2. 邏輯復雜的情況
def hadle_item(item):
    return item * item
odd_list = [hadle_item(i) for i in range(21) if i % 2 == 1]

print(type(odd_list))
print(odd_list)

 

生成器表達式:

# 生成器表達式
odd_gen = (i for i in range(21) if i % 2 == 1)
print(type(odd_gen))  # <class 'generator'>

 

生成器表達式,即將列表推導式的[]換成()即是 

字典推導式:

# 字典推導式
my_dict = {'MJ1':22, 'MJ2':23, 'MJ3':24}
reversed_dict = {value:key for key, value in my_dict.items()}
print(reversed_dict)

 

集合推導式:

# 集合推導式
my_set = {key for key, value in my_dict.itmes()} # 只是把key放集合里
print(type(my_set))
print(my_set)

 三、深入set、dict

dict常用方法:

a = {'name1':{'age':22}, 'name2':{'age':23}}
a.clear()  # 清空

new_dict = a.copy()  # 淺拷貝
new_dict['name1']['age'] = '25'  # a 會隨之被修改

import copy
new_dict = copy.deepcopy(a)  # 深拷貝
new_dict['name1']['age'] = '26'  # a 不變


#formkeys
new_list = ['name1','name2']
new_dict = dict.fromkeys(new_list, {'age':23})  # 將可迭代對象轉換成字典dict


new_dict.get('name', {})  # 如果key沒有為name,則不會報錯,返回{}

for key, value in new_dict.items():
    print(key, value)

default_value = new_dict.setdefault('name','27')  # 不但將值取出,還將 'name':'27' 映射到字典里

new_dict.update(name='29', name3='30')
#new_dict.update([('name','31')])
#new_dict.update((('name','31'),))

 

set 和 frozenset:

#set 集合 fronzenset (不可變集合) 無序, 不重復
# s = set('abcdee')
# s = set(['a','b','c','d','e'])
s = {'a','b', 'c'}
# s = frozenset("abcde") #frozenset 可以作為dict的key
# print(s)

#向set添加數據
another_set = set("cef")
re_set = s.difference(another_set)
re_set = s - another_set
re_set = s & another_set
re_set = s | another_set

#set性能很高
# | & -  #集合運算
print(re_set)

print (s.issubset(re_set))
# if "c" in re_set:
#     print ("i am in set")
  • dict查找的性能遠遠大於list
  • 在list中隨着list數據的增大 查找時間會增大
  • 在dict中查找元素不會隨着dict的增大而增大
  1.  dict的key或者set的值 都必須是可以hash的
  2. 不可變對象 都是可hash的, 如str, fronzenset, tuple,自己實現的類 __hash__
  3. dict的內存花銷大,但是查詢速度快, 自定義的對象 或者python內部的對象都是用dict包裝的
  4.  dict的存儲順序和元素添加順序有關
  5.  添加數據有可能改變已有數據的順序

 

四、對象引用、可變性和垃圾回收

1、== 與 is 的區別

==:判斷對象內容是否相等

is :主要判斷id是否相等

注意:當兩個變量值為較小的int或者str等值,而不是list、set等,它們的id是同一個,這是python內部的優化機制,如:

a = 1
b = 1
print(a == b)   # True
print(a is b)   # True ,ip地址相同


a = [1,2,3,4]
b = [1,2,3,4]
print(a == b)   # True
print(a is b)   # False ,ip地址不同

2、del語句和垃圾回收機制  

  • python中垃圾回收的算法是采用 引用計數,引用計數減到0時刪除對象
  • 刪除變量並不表示刪除對象(釋放內存空間),只有對象或類引用計數器減為0才是刪除對象,釋放內存空間
a = object()  # object的引用計數為1
b = a  # object的引用計數變為2
del a  # 刪除a,object的引用計數減成1,當為0時則被釋放掉
print(b)
print(a)  # 報錯,找不到

class A:
    def __del__(self):
        pass

 五、元類編程

1、property

from datetime import date, datetime

class User:
    def __init__(self, name, birthday):
        self.name = name
        self.birthday = birthday
        self._age = 0

    @property
    def age(self):   # get方法
        return datetime.now().year - self.birthday.year

    @age.setter    # set方法
    def age(self, value):
        self._age = value

 

2、__getattr__、__getattribute__魔法函數

  • 當調用某屬性時,如果找不到,則會調用 __getattr__方法
  • 只要是__init__定義的屬性,當被調用時,會優先調用 __getattribute__方法,優先級大於__getattr__方法
from datetime import date
class User:
    def __init__(self,info={}):
        self.info = info

    def __getattr__(self, item): #找不到相應屬性時調用
        return self.info[item]

    def __getattribute__(self, item):
        return "db"

if __name__ == "__main__":
    user = User(info={"company_name":"imooc", "name":"sb"})
    print(user.test)     # 返回:sb
    print(company_name)  # 返回:sb
# init中定義的屬性被調用時會優先調用__getattribute__方法,故打印的都是該方法中的返回值

 3、屬性描述符和屬性查找過程

  • 數據屬性描述符:實現 get、set、delete 方法
  • 非數據屬性描述符:只實現 get方法

實例:

from datetime import date, datetime
import numbers

class IntField:
    # 數據屬性描述符
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < 0:
            raise ValueError("positive value need")
        self.value = value
    def __delete__(self, instance):
        pass

class NonDataIntField:
    # 非數據屬性描述符,只實現 get
    def __get__(self, instance, owner):
        return self.value

class User:
    # 屬性描述符對象
    age = IntField()

if __name__ == "__main__":
    user = User()
    user.age = 30
    print(user.__dict__)   # 返回:{}
    print(getattr(user, 'age'))

    user.__dict__['age'] = 'abc'
    print(user.__dict__)          # 返回:{'age':'abc'}
    print(user.__dict__['age'])   # 返回:abc

 

屬性查找過程:

如果user是某個類的實例,那么user.age(或getattr(user,’age’)方法):
首先會調用__getattribute__,在__getattribute__內部會調用描述符__get__方法,如果沒有找到方法拋出異常(AttributeError ),此時會調用__getattr__方法,如都沒有則拋AttributeError異常。
#方法調用順序:__getattribute方法__ → __getattribute__內部調用__get__方法 → __getattr__方法

屬性查找過程:
user = User(), 那么user.age 順序如下(參考上述實例):

1、如果“age”是在User類中定義或在其基類的__dict__中, 且age是data descriptor(數據屬性描述符), 那么調用該類的__get__方法, 

2、如果“age”出現在user實例對象的__dict__中, 那么直接返回 user.__dict__[‘age’], 
    #user.__dict__['age']=10 ,此時數據是存於user對象的__dict__中的,

3、如果“age”出現在User或其基類的__dict__中:

  3.1、如果age是non-data descriptor,那么調用其__get__方法

  3.2、返回 __dict__[‘age’]

4、如果User有__getattr__方法,調用__getattr__方法,否則

5、拋出AttributeError

# 順序由上往下:先查類屬性 → 數據屬性描述符 → 實例屬性 → 非數據描述符 → AttributeError異常

 

4、__new__和__init__的區別

__new__ 是用來控制類對象的生成過程,在對象生成之前調用。 
__init__ 是在 __new__ 生成類對象之后調用。 
注意:如果new方法不返回對象,則不會調用init函數

class User:
    def __new__(cls, *args, **kwargs):
        print('in new')
        return super().__new__(cls)

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

if __name__ == '__main__':
    user = User(name='sb')

 

5、自定義元類

類也是對象,typ是創建類的類,type叫元類。 
type → 創建類class對象 → 創建對象

創建類的三種方法:

5.1、常用方法:

def create_class(name):
    if name == 'user':
        class User:
            def __str__(self):
            return 'user'
        return User
    elif name == 'company':
        class Company:
            def __str__(self):
            return 'company'
        return Company

 

5.2、type 動態創建:

# type(obj_name,bases,dict)

def say(self):
    return 'i am user'

class BaseClass:
    def answer(self):
        return 'i am baseclass'

if __name__ == '__main__':
    #type動態創建類
    MyClass = type('User', (BaseClass,), {'name':'user', 'say':say})
    my_obj = User()
    print(my_obj)             # 返回:<__main__.User object at ...>
    print(my_obj.name)        # 返回:user
    print(my_obj.say)         # 返回:i am user
    print(my_obj.answer())    # 返回:i am baseclass

 

5.3、metaclass基類控制類創建:

類實例化過程(實例化user):首先會向繼承的類中尋找metaclass,如果有,則會通過metaclass指向的類創建User類,再實例化對象(控制User類的操作可以再metaclass指定的類中完成)→ 如沒有metaclass,則會由type創建類

class MetaClass(type):  # 基類繼承type
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)

class User(metaclass=MetaClass): 
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'user'

if __name__ == '__main__':
    my_obj = User(name='MJ')
    print(my_obj)  # 返回:user

 

六、迭代器和生成器

1、迭代器和可迭代對象

迭代器是訪問集合內元素的一種方式,一般用來遍歷數據。

迭代器和以下標的訪問方式不一樣,迭代器是不能返回的,迭代器提供了一種惰性訪問數據的方式。

實現魔法函數:

 __iter__ :可迭代對象

實現魔法函數:

 __iter__、__next__  :迭代器

from collections.abc import Iterator

class Company(object):
    # 可迭代對象
    def __init__(self, employee_list):
        self.employee = employee_list

    def __iter__(self):
        return MyIterator(self.employee) #使用下面自定義的MyIterator迭代器

class MyIterator(Iterator):
    # 迭代器
    def __init__(self, emplyee_list):
        self.iter_list = employee_list
        self.index = 0

    def __next__(self):
        # 真正返回迭代值的邏輯
        try:
            word = self.iter_list[self.index]
        except IndexError:
            raise StopIteration
        self.index += 1
        return word

if __name__ == '__main__':
    company = Company(['tom', 'bob', 'jane'])
    my_itor = iter(company)

    #while True:
    #   try:
    #       print(next(my_itor))
    #   except StopIteration:
    #       pass
    # 相當於:
    #for item in company:
    #   print(item)

 


 


免責聲明!

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



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