特性、迭代器和生成器
Python 中的魔法(特殊)方法。
首先,所有的类都将隐式的继承 object
类,如果没有指定超类,将直接继承它,否则将间接的继承它。
构造函数
介绍的第一个魔法方法是构造函数,其实就是初始化方法,命名为
__init__
。
构造函数将在对象创建后自动调用它们
1 | class Foobar: |
或者,给构造函数添加参数
1 | class Foobar: |
参数是可选的,可以使用默认的参数值,也可以传入指定的参数值,对原有的值进行覆盖
重写
每一个类都有一个或多个超类,并从它们那里继承行为。
对于大多数的子类,重写构造函数时,必须调用超类的构造函数,否则可能无法正确的初始化对象
1 | class Bird: |
这时,执行程序,你会发现无法正确的初始化对象,提示对象没有 hungry
属性
这是因为子类重写了构造函数,但是并没有初始化父类的构造函数
调用未关联的超类构造函数
由于历史遗留问题,我们先介绍旧版 Python
的初始化方式:
1 | class SongBird(Bird): |
修改之后再次执行,会发现已经可正确的初始化了
但是在新式类中,我们应该避免这种初始化方式,而使用 super
使用函数 super
使用 super
时通常不用提供任何参数
1 | class SongBird(Bird): |
当然,如果子类没有重写构造函数,那么默认继承了父类的构造函数,可以直接使用
元素访问
基本的序列和映射协议
序列和映射基本上是元素的集合,要实现它们的基本行为,不可变对象需要实现2个方法,可变对象需要实现4个
__len__(self)
返回集合包含的项数
__getitem__(self, key)
返回与指定键相关联的值
__setitem__(self, key, value)
以键相关联的方式存储值
__delitem__(self, key)
当对象可变(且允许被删除时),才需要实现
下面创建一个无穷序列
1 | def check_index(key): |
这里我们没有实现 __del__
,意味着不允许删除元素
从 list
、dict
、str
派生
快速实现一个序列和映射,可以从已有的基本序列和映射继承,通过重写魔法方法,实现一个新的序列或映射,例如:
实现一个计算器列表:
1 | class CounterList(list): |
特性
之前有介绍 set
和 get
的存取方法,是一种很重要的封装状态变量(属性)的方法。但我们没有办法将所有属性都提供存取方法,这是不现实的,所以我们采用另外一种方法 —— property
。它可以隐藏存取方法,让所有属性看起来都一样。
通过存取方法定义的属性通常称为特性。
函数 property
1 | class Rectangle: |
这样,size
属性依然受制于 set
和 get
执行计算,但是看起来就像普通属性一样
或者使用装饰器实现
1 | class Rectangle: |
静态方法和类方法
静态方法和类方法在创建的过程中是分别包装在 staticmethod
和 classmethod
类的对象中的
静态方法的定义中,没有参数 self
,可以直接通过类来调用
类方法的定义中包含类似于 self
的参数,通常被命名为 cls
,对与类方法,也可以通过对象直接调用,但参数 cls
将自动关联到类
1 | class MyClass: |
在引入装饰器(装饰器可以用于包装任何可调用的对象,并且可用于方法和函数)以后,还可以用于这样包装方法
1 | class MyClass: |
定义方法后,无需实例化,可以直接使用它们
__getattr__
、__setattr__
__getattr__(self, name)
在属性被访问而对象没有这样的属性时自动调用
__setattr__(self, name, value)
试图给属性赋值时自动调用
使用上面的方法可以编写处理多个特性的代码,但是,在可能的情况下,还是使用函数 property
代替比较好(出于使用方式和效率的考虑)
迭代器
迭代,意味着重复多次,就像循环那样,实现了
__iter__
的对象是可迭代的。
方法 __iter__
返回一个迭代器,它是包含方法 __next__
的对象,而调用方法时可以不提供任何参数。
在某些情况下,你可能只想逐个地获取值,而不是使用列表一次性获取,因为如果值很多,列表可能占用太多的内存,然而使用迭代器更通用、更简单、更优雅
1 | class Fibs: |
实现了方法 __iter__
的对象是可迭代的,而实现方法 __next__
的对象是迭代器
1 | class TestIterator: |
生成器
生成器是一中使用普通函数语法定义的迭代器。
生成器使用 yied
语句进行创建,之前有简单介绍,也可以说包含 yield
语句的函数称作生成器
现在我们使用一个生成器展开一个嵌套列表:
1 | def flatten(nested): |
输出:
1 | [1, 2, 3, 4, 5] |