十九、 描述符

    描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

    所谓特殊类型, 要求至少实现以下三个方法中的一个(全部实现也可以)

__get__(self, instance, owner)    定义当描述符的值被取得时的行为
     用于访问属性, 他返回属性的值
 __set__(self, instance, value)    定义当描述符的值被改变时的行为
     将在属性分配操作中调用, 不返回任何值
 __delete__(self, instance)    定义当描述符的值被删除时的行为
     控制删除操作,不返回任何内容class MyProperty:
     def __init__(self, f_get=None, f_set=None, f_del=None):
         self.f_get = f_get
         self.f_set = f_set
         self.f_del = f_del    def __get__(self, instance, owner):
         return self.f_get(instance)    def __set__(self, instance, value):
         return self.f_set(instance,value)    def __delete__(self, instance):
         self.f_del(instance) class C:
     def __init__(self):
         self._x = None    def get_x(self):
         return self._x    def set_x(self, value):
         self._x = value    def del_x(self):
         del self._x    x = MyProperty(get_x, set_x, del_x)
 c = C()
 c.x = "xiaoming"
 print(c.x)                    xiaomingdel c.x
 print(c.x)                    AttributeError: 'C' object has no attribute '_x'二十、 定制序列
    协议是什么?
     协议与其他编程语言中的接口很相似, 它规定你哪些方法必须要定义。 然而, 在python中的协议就显得不那么正式, 
     事实上, 在python中, 协议更像是一种指南。    鸭子类型 : 当看到一只鸟走起来像鸭子, 游起来像鸭子, 叫起来也像鸭子, 那么这只鸟就可以被称为鸭子。
     如果你希望定制的容器是不可变的话, 你只需要定义 __len__() 和 __getitem__() 方法。
    如果你希望定制的容器是可变的话, 除了 __len__() 和 __getitem__() 方法, 
     你还需要定义 __setitem__() 和 __delitem__() 两个方法。         __len__() 方法 : 
                 定义当被 len() 调用时的行为(返回容器中元素的个数)        __getitem__() 方法:
                 定义获取容器中指定元素的行为, 相当于 self[key]        __setitem__() 方法:
                 定义设置容器中指定元素的行为,  相当于 self[key] = value        __delitem__() 方法:
                 定义删除容器中指定元素的行为。 相当于 del self[key] 不可变容器 : 
class CountList:
    def __init__(self, *args):
         self.values = [x for x in args]
         self.count = {}.fromkeys(range(len(self.values)), 0)    def __len__(self):
         print('你正在调用 __len__ 方法啊')
         return len(self.values)    def __getitem__(self, item):
         print('你正在调用 __getitem__ 方法啊')
         self.count[item] += 1
         return self.values[item] count_list1 = CountList(1, 3, 5, 7, 9)
count_list2 = CountList(2, 4, 6, 8, 10)
 print(count_list1[1])
 你正在调用 __getitem__ 方法啊
 3print(count_list2[1])
 你正在调用 __getitem__ 方法啊
 4print(count_list1[1] + count_list2[1])
 你正在调用 __getitem__ 方法啊
 你正在调用 __getitem__ 方法啊
 7print(count_list1.count)
 {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}print(count_list2.count)
 {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}count_list1[1] = 5
 print(count_list1[1])                TypeError: 'CountList' object does not support item assignment 可变容器 : 
class CountList:
    def __init__(self, *args):
         self.values = [x for x in args]
         self.count = {}.fromkeys(range(len(self.values)), 0)    def __len__(self):
         print('你正在调用 __len__ 方法啊')
         return len(self.values)    def __getitem__(self, item):
         print('你正在调用 __getitem__ 方法啊')
         self.count[item] += 1
         return self.values[item]    def __setitem__(self, key, value):
         print('正在调用 __setitem__ 方法啊')
         self.values[key] = value    def __delitem__(self, key):
         print('正在调用 __delitem__ 方法啊')
         del self.values[key] count_list1 = CountList(1, 3, 5, 7, 9)
count_list1[1] = 5
 正在调用 __setitem__ 方法啊print(count_list1[1])
 你正在调用 __getitem__ 方法啊
 5del count_list1[1]
 正在调用 __delitem__ 方法啊二十一、迭代器
     
     迭代器的特点 : 
     迭代,顾名思义就是重复做一些事很多次(就现在循环中做的那样)。迭代器是实现了__next__()方法的对象(这个方法在调用时不需要任何参数),
     它是访问可迭代序列的一种方式,通常其从序列的第一个元素开始访问,直到所有的元素都被访问才结束。 
     [注意]:迭代器只能前进不能后退.
     如果迭代到最后超出范围, 则抛出 :     StopIteration          迭代器需要可以被 iter() 和 next() 这两个方法调用    。 【 实现两个 __iter__()  和 __next__() 这两个魔法方法 】
    创建迭代器 : 
         a、 使用内建的工厂函数iter(iterable)可以将可迭代序列转换为迭代器a = [1, 2, 3, 4]
print(type(a))                        <class 'list'>
print(next(a))                        TypeError: 'list' object is not an iterator
print(type(iter(a)))                <class 'list_iterator'>
print(next(iter(a)))                1
    
        b、 自定义迭代器。
             由于Python中没有“迭代器”这个类,因此具有以下两个特性的类都可以称为“迭代器”类:
               1、有__next__()方法,返回容器的下一个元素或抛出StopIteration异常
                2、有__iter__()方法,返回迭代器本身class Fibs:
    def __init__(self, n):
         self.a = 0
         self.b = 1
         self.n = n    def __iter__(self):
         return self    def __next__(self):
         self.a, self.b = self.b, self.a + self.b
         if self.a > self.n:
             raise StopIteration
         return self.afor each in Fibs(30):
     print(each)输出 :  1    1    2    3    5    8    13    21
 二十二、 生成器
     所谓的协调程序就是可以运行的独立的函数调用, 函数可以暂停或者挂起, 并在需要的时候从程序离开的地方继续或者重新开始。    对于普通的函数, 一般都是从函数的第一行开始执行, 知道所有的行都执行完毕或者遇到 return 函数才结束。
     一旦函数将控制权交还给调用者, 函数里面的一切都结束了。     而 【生成器】 是一个特殊的【函数】, 调用可以在中断、停止。 暂停之后, 他把控制权临时交出来, 
     然后下次调用再接着上次离开的地方继续执行。    生成器关键字 : 【  yield  】
def my_gen():
     print('生成器执行了。。。')
     yield 1                            【 第一次调用在这里结束】
     yield 2                            【 第二次调用从这里开始】 mg = my_gen()
 print('-'*50)
 print(next(mg))
 print('-'*50)
 print(next(mg))输出 : 
--------------------------------------------------
 生成器执行了。。。
 1
 --------------------------------------------------
 2 生成器实现 斐波那契 : 
def fibs():
     a = 0
     b = 1
     while True:
         a, b = b, a + b
         yield a for each in fibs():
     if each > 30:
         break
     print(each) 输出 :  1    1    2    3    5    8    13    21