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的增大而增大
- dict的key或者set的值 都必须是可以hash的
- 不可变对象 都是可hash的, 如str, fronzenset, tuple,自己实现的类 __hash__
- dict的内存花销大,但是查询速度快, 自定义的对象 或者python内部的对象都是用dict包装的
- dict的存储顺序和元素添加顺序有关
- 添加数据有可能改变已有数据的顺序
四、对象引用、可变性和垃圾回收
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)