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)

 


 

posted on 2018-10-07 01:51  Eric_nan  阅读(1191)  评论(0编辑  收藏  举报