魔法方法、特性和迭代器笔记

1、
若函数继承没有用函数super()

>>> class Bird:
        def __init__(self):
            self.hungry = True
        def eat(self):
            if self.hungry:
                print("Aaaah...")
                self.hungry = False
            else:
                print('No,thanks!')

>>> b = Bird()
>>> b.eat()
Aaaah...
>>> b.eat()
No,thanks!

    class SongBird(Bird):
        def __init__(self):
            self.sound = 'Squawk!'
        def sing(self):
            print(self.sound)

>>> sb = SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in eat
AttributeError: 'SongBird' object has no attribute 'hungry'
#SongBird没有属性hungry,因为SongBird重写了构造函数__init__,但新的构造函数没有包含任何初始化属性hungry的代码
#要消除这种错误,要保证SongBird的构造函数必须调用其超类Bird的构造函数。
#方法有:调用未关联的超类构造函数和函数super()

2、方法一:调用未关联的超类构造函数

#在类SongBird中添加一行代码
>>> class SongBird(Bird):
        def __init__(self):
            Bird.__init__(self) 
#对实例调用方法时,方法的参数self将自动关联到实例(称为关联的方法)    
#本例中使用的时**类调用**方法(如Bird.__init__),就没有实例与其相关联,
#在这种情况下,可以随便设置参数self,这样的方法称为未关联的        
            self.sound = 'Squawk!'
        def sing(self):
            print(self.sound)

>>> sb = SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat()
Aaaah...
>>> sb.eat()
No,thanks!

通过将这个未关联方法的self参数设置为当前实例,将使用超类的构造函数来初始化SongBird对象。这意味着将设置其属性hungry。

3、方法二:使用函数super()

>>> class Bird:
        def __init__(self):
            self.hungry = True
        def eat(self):
            if self.hungry:
                print('Aaaah...')
                self.hungry = False
            else:
                print('No,thanks')

>>> class SongBird(Bird):
        def __init__(self):
            super().__init__()   #******
            self.sound = 'Squawk'
        def sing(self):
            print(self.sound)

>>> sb = SongBird()
>>> sb.sing()
Squawk
>>> sb.eat()
Aaaah...
>>> sb.eat()
No,thanks

即使有多个超类,函数super也只需调用一次(条件是所有超类的构造函数也使用函数super)
函数super返回的是一个super对象,它负责为你执行方法解析。当你访问它的属性时,它将在所有的超类(超类的超类,等等)中,知道找到指定的属性或者引发AttributeError。

4、

在Python中,协议通常指规范行为的规则,有点类似于前面提及的接口,协议指定应实现哪些方法以及这些方法应做什么。在Python中,多态仅仅基于对象的行为(而不是基于祖先,如属于哪个类或其超类等),因此这个概念很重要:其他的语言可能要求对象基于特定的类或实现了特定的接口,而Python通常只要求对象遵循特定的协议。因此,要成为序列,只需遵循序列协议即可。

5、基本的序列和映射协议

序列和映射基本上是元素(item)的集合,要实现它们的基本行为(协议),不可变对象需要实现2个方法(1、2、),而可变对象需要实现4个。
1、_len_(self):这个方法应返回集合包含的项数,对序列来说是元素个数,对映射来说是键值对数。如果为零,这布尔值视为假。
2、_getitem_(self,key):这个方法应返回与指定键相关联的值。对序列来说,键应该是0~n-1的整数(索引?)(也可以是负数,见后),其中n为序列的长度。对映射来说,键可以是任何类型。
3、_setitem_(self,key,value):这个方法应以与键相关联的方法存储值,以便以后能够使用_getiem_来获取。当然,仅当对象可变时才需要实现这个方法。
4、_delitem_(self,key):这个方法在对对象的组成部分使用_del_语句时被调用,应删除与key相关联的值。当然,仅当对象可变时才需要实现这个方法。

6、

>>> class Rectangle:
        def __init__(self):
            self.width = 0
            self.height = 0
        def set_size(self,size):
            self.width,self.height = size
        def get_size(self):
            return self.width,self.height
>>> r = Rectangle()
>>> r.width = 10
>>> r. height = 5
>>> r.get_size()
(10, 5)
>>> r.set_size((100,150))
>>> r.width
100

get_size 和 set_size 是假想属性size的存取方法,这个属性是一个由width和height组成的元组。使用这个类时,程序员应无需关心它是怎么实现的(封装)。如果有一天想修改实现,让size称为真正的属性,而width和height是动态计算出来的,就需要提供用于访问width和height的存取方法,使用这个类的程序也必须重写。应让客户端代码(使用你所编写代码的代码)能够以同样的方式对待所有的属性。
那么如何解决这个问题呢?给所有的属性都提供存取方法吗?这当然并非不可能,但如果有大量简单的属性,这样做就不现实(有点傻),因为将需要编写大量这样的存取方法,这些方法除了获取或设置属性外什么都不做。这将引入复制并张贴(重复代码)的坏味,显然很糟糕(虽然在有些语言中,这很常见)。
所幸Python能够替你隐藏存取方法,让所有的属性看起来都一样。通过存取方法定义的属性通常称为特性(property)

>>> class Rectangle:
        def __init__(self):
            self.width = 0
            self.height = 0
        def set_size(self,size):
            self.width,self.height = size
        def get_size(self):
            return self.width,self.height 
        size = property(get_size,set_size)
#通过调用函数property并将存取方法作为参数(获取方法在前,设置方法在后)创建一个特性,并把该特性关联到名称size。
#这样就能以同样的方式对待width,height和size,而不用关心它们是如何实现的    
>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 15
>>> r.size
(10, 15)
>>> r.size = 100,150
>>> r.width
100
#虽然属性size依然受制于get_size和set_size执行的计算,但看起来就像普通属性一样

关于函数property
调用函数property时,还可以不指定参数,或者指定一个,两个,三个和四个参数。
如果不指定参数,创建的特性将不可读也不可写。一个参数的特性是只读的,两个是可读可写。第三参数可选,第三参数指定用于删除属性的方法(这个方法不接受任何参数)。第四参数可选,指定一个文档字符串。
这四个参数分别名为fget,fset,fdel和doc。如果要创建一个只含文档字符串的特性,可以使用关键字参数来实现。

7、迭代器

迭代器协议
_iter_是迭代器协议的基础。迭代(iterate)意味着重复多次,就像循环那样。虽然前面只使用了for循环迭代过序列和字典,但实际上也可以迭代其他对象:实现了方法_iter_的对象。
方法_iter_返回一个迭代器,它包含方法_next_的对象,而调用这个方法时可不提供任何参数。当你调用方法_next_时,迭代器应返回其下一个值。如果迭代器没有可供返回的值,就会引发StopIteration异常。也可以使用内置的便利函数next,在这种情况下,next(it)与it._next_()等效。
这有什么意义呢?为何不使用列表呢?因为在很多情况下,使用列表都有点像用大炮打蚊子。例如,如果你有一个可逐个计算值的函数,你可能只想逐个地获取值,而不是使用列表一次性获取。这是因为如果有很多值,列表可能占用太多的内存。但还有其他原因:使用迭代器更通用,更简单,更优雅。

下面是一个不能使用列表的例子(使用列表的话,列表长度为无穷大),这个“ 列表 ”为斐波那契数列,表示该数列的迭代器如下:

>>> class Fibs:
...     def __init__(self):
...         self.a = 0
...         self.b = 1
...     def __next__(self):
...         self.a, self.b = self.b, self.a + self.b
...         return self.a
...     def __iter__(self):
...         return self
...    
>>> fibs = Fibs()
>>> for f in fibs:
...     if f > 1000:
...         print(f)
...         break
...        
1597

这个迭代器实现了方法_iter_,而这个方法返回迭代器本身。在很多情况下,都在另一个对象中实现返回迭代器的方法_iter_,并在for循环中使用这个对象。但推荐在迭代器中也实现方法_iter_(并像刚才那样让它返回self),这样迭代器就可直接用于for循环中。

pass

8、生成器

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000

生成器是一种使用普通函数语法定义的迭代器。

下面函数功用:将嵌套列表展开的函数。

#普通函数形式
>>> nested = [[1,2],[3,4],[5]]
>>> def flatten(nested):
        for sublist in nested:
            for element in sublist:
                print(element)

>>> flatten(nested)
1
2
3
4
5

#使用生成器形式
>>> nested = [[1,2],[3,4],[5]]
>>> def flatten(nested):
        for sublist in nested:
            for element in sublist:
                yield element   #包含yield语句的函数都被称为生成器

>>> list(flatten(nested))
[1, 2, 3, 4, 5]
#或者
>>> for num in flatten(nested):
        print(num)

1
2
3
4
5

生成器行为与普通函数截然不同。差别在于,生成器不是使用return返回一个值,而是可以生成多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重写唤醒。被唤醒后,函数将从停止的地方开始继续执行

pass

9、递归式生成器

前面例子是两层嵌套列表,可以使用两个for循环来实现。如果要处理任意层嵌套的列表,该如何办?
例如,可能会用这样的列表来表示树结构(也可以使用特定的树类,但策略是相同的)。对于每层嵌套,都需要一个for循环,但由于不知道有多少层嵌套,必须修改方案,使其更灵活。这时候就需要递归了。

>>> nested = [[1,2],[3,4],[5]]
>>> def flatten(nested):
        try:
            for sublist in nested:
                for element in flatten(sublist):
                    yield element
        except TypeError:
            yield nested

>>> list(flatten(nested))
[1, 2, 3, 4, 5]
>>> list(flatten([[1,2],[3,4,5],[6,[7,8]],[9]]))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

调用faltten时,有两种可能性(处理递归时都如此):基线条件和递归条件。
在基线条件下,要求这个函数展开单个元素(如一个数)。在这种情况下,for循环将引发TypeError异常(因为试图迭代一个数),而这个生成器只生成一个元素。
然而,如果要展开的是一个列表(或其他任何可迭代对象),就需要做这些工作:遍历所有的子列表(其中有些可能不是列表)并对它们调用flatten,然后使用另一个for循环生成展开后的子列表中的元素。
然而,这个解决方案存在一个问题。如果nested是字符串或者类似于字符串的对象,它就属于序列,因此不会引发TypeError异常,可你并不想对齐进行迭代。

pass

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值