魔法方法、特性和迭代器
构造函数
重写构造函数时,必须调用超类(继承的类)的构造函数,否则可能无法正确地初始化对象
class Bird: def __init__(self): self.hungry = True def eat(self): if self.hungry: print('Aaaah ...') self.hungry = False else: print('No, thanks!')# 子类SongBird, SongBird没有属性hungry
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 "<stdin>", line 1, in ? File "birds.py", line 6, in eat if self.hungry:
AttributeError: SongBird instance has no attribute 'hungry'
class SongBird(Bird): def __init__(self): super().__init__()self.sound = 'Squawk!' def sing(self): print(self.sound)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wNGwaOIZ-1677508665299)(./resource/super%E4%BC%98%E7%82%B9.png)]
元素访问
序列和映射基本上是元素(item)的集合,要实现它们的基本行为(协议),不可变对象需要实现2个方法,而可变对象需要实现4个
__len__(self)
__getitem__(self, key)
__setitem__(self, key, value)
__delitem__(self, key)
对于这些方法,还有一些额外的要求。
- 对于序列,如果键为负整数,应从末尾往前数。换而言之,x[-n]应与x[len(x)-n]等效。
- 如果键的类型不合适(如对序列使用字符串键),可能引发TypeError异常。
- 对于序列,如果索引的类型是正确的,但不在允许的范围内,应引发IndexError异常。
特性
- 函数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)>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 5
>>> r.size
(10, 5)
>>> r.size = 150, 100
>>> r.width
150
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wOqhk63y-1677508665301)(./resource/property%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.png)]
- 静态方法和类方法
class MyClass: def smeth(): print('This is a static method') smeth = staticmethod(smeth) def cmeth(cls): print('This is a class method of', cls) cmeth = classmethod(cmeth)# 使用装饰器
class MyClass: @staticmethoddef smeth(): print('This is a static method') @classmethoddef cmeth(cls): print('This is a class method of', cls)
可指定一个或多个装饰器,为此可在方法(或函数)前面使用运算符@列出这些装饰器(指定了多个装饰器时,应用的顺序与列出的顺序相反
__getattr__
、__setattr__
等方法
__getattribute__(self, name)
:在属性被访问时自动调用(只适用于新式类)。__getattr__(self, name)
:在属性被访问而对象没有这样的属性时自动调用。__setattr__(self, name, value)
:试图给属性赋值时自动调用。__delattr__(self, name)
:试图删除属性时自动调用。
class Rectangle: def __init__ (self): self.width = 0 self.height = 0 def __setattr__(self, name, value): if name == 'size': self.width, self.height = value else: self. __dict__[name] = value def __getattr__(self, name): if name == 'size': return self.width, self.height else: raise AttributeError()
-
__dict__
属性是一个字典,其中包含所有的实例属性。之所以使用它而不是执行常规属性赋值,是因为旨在避免再次调用__setattr__
,进而导致无限循环 -
编写
__getattribute__
时亦如此。由于它拦截对所有属性的访问(在新式类中),因此将拦截对__dict__
的访问!在__getattribute__
中访问当前实例的属性时,唯一安全的方式是使用超类的方法__getattribute__
(使用super)。
迭代器
方法__iter__返回一个迭代器,它是包含方法__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,可获得一个迭代器
>>> it = iter([1, 2, 3])
>>> next(it)
1
>>> n
从迭代器创建序列
class TestIterator: value = 0 def __next__(self): self.value += 1 if self.value > 10: raise StopIteration return self.value def __iter__(self): return self >>> ti = TestIterator()
>>> list(ti)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生成器
- 创建生成器
def flatten(nested): for sublist in nested: for element in sublist: yield element>>> nested = [[1, 2], [3, 4], [5]]
>>> for num in flatten(nested):
... print(num)
1
2
3
4
5
生成器不是使用return返回一个值,而是可以生成多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行
- 递归式生成器
def flatten(nested): try: for sublist in nested: for element in flatten(sublist): yield element except TypeError: yield nested
-
基线条件 在基线条件下,要求这个函数展开单个元素(如一个数)。在这种情况下,for循环将引发TypeError异常(因为你试图迭代一个数),而这个生成器只生成一个元素。
-
递归条件 递归调用flatten(sublist)
如果nested是字符串或类似于字符串的对象,它就属于序列,因此不会引发TypeError异常,可你并不想对其进行迭代
# 要检查对象是否类似于字符串,最简单、最快捷的方式是,尝试将对象与一个字符串拼接起来,并检查这是否会引发TypeError异常①。def flatten(nested): try: # 不迭代类似于字符串的对象:try: nested + '' except TypeError: pass else: raise TypeError for sublist in nested:for element in flatten(sublist): yield element except TypeError: yield nested>>> list(flatten(['foo', ['bar', ['baz']]]))
['foo', 'bar', 'baz']
- 通用的生成器
生成器由两个单独的部分组成:生成器的函数和生成器的迭代器。生成器的函数是由def语句定义的,其中包含yield。
>>> def simple_generator(): yield 1
...
>>> simple_generator
<function simple_generator at 153b44>
>>> simple_generator()
<generator object at 1510b0>
对于生成器的函数返回的迭代器,可以像使用其他迭代器一样使用它。
- 生成器的方法
在生成器开始运行后,可使用生成器和外部之间的通信渠道向它提供值。这个通信渠道包含如下两个端点
-
外部世界 外部世界可访问生成器的方法send,这个方法类似于next,但接受一个参数(要发送的“消息”,可以是任何对象)。
-
生成器 在挂起的生成器内部,yield可能用作表达式而不是语句。换而言之,当生成器重新运行时,yield返回一个值——通过send从外部世界发送的值。如果使用的是next,yield将返回None。
def repeater(value): while True: new = (yield value) if new is not None: value = new>>> r = repeater(42)
>>> next(r)
42
>>> r.send("Hello, world!")
"Hello, world!"
- 方法throw
- 方法close
- 模拟生成器
def flatten(nested): result = [] # 1try: # 不迭代类似于字符串的对象:try: nested + '' except TypeError: pass else: raise TypeError for sublist in nested: for element in flatten(sublist): result.append(element) # 2except TypeError: result.append(nested) # 3-1return result # 3-2
小结
魔法方法:Python中有很多特殊方法,其名称以两个下划线开头和结尾。这些方法的功能
各不相同,但大都由Python在特定情况下自动调用。例如__init__是在对象创建后调用的。
构造函数:很多面向对象语言中都有构造函数,对于你自己编写的每个类,都可能需要
为它实现一个构造函数。构造函数名为__init__,在对象创建后被自动调用。
重写:类可重写其超类中定义的方法(以及其他任何属性),为此只需实现这些方法即可。
要调用被重写的版本,可直接通过超类调用未关联版本(旧式类),也可使用函数super
来调用(新式类)。
序列和映射:要创建自定义的序列或映射,必须实现序列和映射协议指定的所有方法,
其中包括__getitem__和__setitem__等魔法方法。通过从list(或UserList)和dict(或
UserDict)派生,可减少很多工作量。
迭代器:简单地说,迭代器是包含方法__next__的对象,可用于迭代一组值。没有更多的
值可供迭代时,方法__next__应引发StopIteration异常。可迭代对象包含方法__iter__,
它返回一个像序列一样可用于for循环中的迭代器。通常,迭代器也是可迭代的,即包含
返回迭代器本身的方法__iter__。
生成器:生成器的函数是包含关键字yield的函数,它在被调用时返回一个生成器,即一
种特殊的迭代器。要与活动的生成器交互,可使用方法send、throw和close。