文章目录
一,构造函数:__init__
…
析构函数:__del__
这个方法在对象被销毁(作为垃圾被收集)前被调用,但鉴于你无法知道准确的调用时间,建议尽可能不要使
用 __del__ 。
二,重写普通方法和特殊的构造函数
子类可以直接重写普通方法
重写构造函数时要注意调用 超类 的构造函数
①多用于旧版
调用未关联的超类构造函数
超类.__init__(self)
通过类调用方法(如 Bird.init ),就没有实例与其相关联。在这种情况下,你可随便设置参数 self 。这样的方法称为未关联的。
②使用函数 super 只适用于新式类。
调用这个函数时,将当前类和当前实例作为参数。对其返回的对象调用方法时,调用的将是超类(而不是当前类)的方法。在Python3中调用super可以不添加任何参数。
super()..__init__()
即便有多个超类,也只需调用一遍super
拓展
函数 super 返回的到底是什么呢?通常,你无需关心这个问题,只管假定它返回你所需的超类即可。实际上,它返回的是一个 super 对象,这个对象将负责为你执行方法解析。当你访问它的属性时,它将在所有的超类(以及超类的超类,等等)中查找,直到找到指定的属性或引发 AttributeError 异常。
三,元素访问
3.1.基本的序列和映射协议。
不可变对象要实现2个方法,可变对象需要实现4个。
①__len__(self)
②__getitem__(self,key)
对序列来说键是整数,对字典来说键可以是任何类型。
③__setitem__(self,key,value)
(对象可变时)
④__delitem__(self,key).
(对象可变时)
⑤额外要求
对于序列,如果键为负整数,应从末尾往前数。换而言之, x[-n] 应与 x[len(x)-n] 等效。
如果键的类型不合适(如对序列使用字符串键),可能引发TypeError 异常。
对于序列,如果索引的类型是正确的,但不在允许的范围内,应引发 IndexError 异常。
试着创建无穷序列:
def check_index(key):
"""
指定的键是否是可接受的索引?
键必须是非负整数,才是可接受的。如果不是整数,
将引发TypeError异常;如果是负数,将引发Index
Error异常(因为这个序列的长度是无穷的)
"""
if not isinstance(key, int): raise TypeError
if key < 0: raise IndexError
class ArithmeticSequence:
def __init__(self, start=0, step=1):
"""
初始化这个算术序列
start -序列中的第一个值
step -两个相邻值的差
changed -一个字典,包含用户修改后的值
"""
self.start = start # 存储起始值
self.step = step # 存储步长值
self.changed = {} # 没有任何元素被修改
def __getitem__(self, key):
"""
从算术序列中获取一个元素
"""
check_index(key)
try: return self.changed[key] # 修改过?
except KeyError: # 如果没有修改过,
return self.start + key * self.step # 就计算元素的值
def __setitem__(self, key, value):
"""
修改算术序列中的元素
"""
check_index(key)
self.changed[key] = value # 存储修改后的值
使用:
>>> s = ArithmeticSequence(1, 2)
>>> s[4]
9
>>> s[4] = 2
>>> s[4]
2
>>> s[5]
11
禁止删除 因为没有实现__del__
长度无限,所以没有方法 __len__
如果所使用索引的类型非法,将引发 TypeError 异常;如果索引的类型正确,但不在允许的范围内(即为负数),将引发 IndexError 异常。
索引检查是由为此编写的辅助函数 check_index 负责的。
3.2.从list,dict 和 str 派生
重写部分行为时注意调用或返回超类的原方法
#带计数器的列表
class CounterList(list):
def __init__(self, *args):
super().__init__(*args)
self.counter = 0
def __getitem__(self, index):
self.counter += 1
return super(CounterList, self).__getitem__(index
注意
重写 __getitem__ 并不能保证一定会捕捉用户的访问操作,因为还有其他访问列表内容的方式,如通过方法 pop 。
五,函数property
5.1 property特性
新类提倡使用property特性,而不是存取器
属性 = property(fget,fset,fdel,doc) #这四个参数都是可选的
#第三个参数指定用于删除属性的方法,这个方法不接受任何参数。
#第四个参数指定一个文档字符串
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 = 5
>>> r.size
(10, 5)
>>> r.size = 150, 100
>>> r.width
150
5.2静态方法和类方法
静态方法 staticmethod
没有参数self,直接通过类来调用
类方法 classmethod
类似于self的参数,通常命名为cls,但是关联到类,可通过类或对象来调用。
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:
@staticmethod
def smeth():
print('This is a static method')
@classmethod
def cmeth(cls):
print('This is a class method of', cls)
实际上,装饰器可用于包装任何可调用的对象,并且可用于方法和函
数。)可指定一个或多个装饰器,为此可在方法(或函数)前面使用运算符 @ 列出这些装饰器(指定了多个装饰器时,应用的顺序与列出的顺序相反。
5.3 __getattr__ 、 __setattr__ 等方法
拦截对对象属性的所有访问企图
用途之一是在旧式类中实现特性
__getattribute__(self, name) :在属性被访问时自动调用(只适用于新式类)。
__getattr__(self, name) :在属性被访问而对象没有这样的属性时自动调用。
__setattr__(self, name, value) :试图给属性赋值时自动调用。
__delattr__(self, name) :试图删除属性时自动调用
前面property的例子
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()
注意
即便涉及的属性不是 size ,也将调用方法 setattr 。因此这个方法必须考虑如下两种情形:如果涉及的属性为 size ,就执行与以前一样的操作;否则就使用魔法属性 dict 。dict 属性是一个字典,其中包含所有的实例属性。之所以使用它而不是执行常规属性赋值,是因为旨在避免再次调用 setattr ,进而导致无限循环。
仅当没有找到指定的属性时,才会调用方法 getattr 。这意味着如果指定的名称不是size ,这个方法将引发 AttributeError 异常。这在要让类能够正确地支持 hasattr 和 getattr等内置函数时很重要。如果指定的名称为 size ,就使用前一个实现中的表达式。
编写方法 __setattr__ 时需要避开无限循环陷阱,编写__getattribute__ 时亦如此。由于它拦截对所有属性的访问(在新式类中),因此将拦截对 __dict__ 的访问!在 __getattribute__ 中访问当前实例的属性时,唯一安全的方式是使用超类的方
法 __getattribute__ (使用 super )
六,迭代器iter
6.1 迭代器协议
方法__iter__返回一个迭代器,它是包含方法__next__的对象,可不提供任何参数.
调用方法__next__ 时,迭代器应返回其下一个值。
如果迭代器没有可供返回的值,应引发 StopIteration 异常。
你还可使用内置的便利函数 next ,在这种情况下, next(it) 与it.__next__() 等效。
正规的定义是,实现了方法 __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
这个迭代器实现了方法 __iter__ ,而这个方法返回迭代器本身。在很多情况下,都在另一个对象中实现返回迭代器的方法 __iter__ ,并在 for 循环中使用这个对象。但推荐在迭代器中也实现方法 __iter__ (并像刚才那样让它返回 self ),这样迭代器就可直接用于 for 循环中。
>>> fibs = Fibs()
>>> for f in fibs:
... if f > 1000:
... print(f)
... break
...
1597
通过对可迭代对象调用内置函数 iter ,可获得一个迭代器。
>>> it = iter([1, 2, 3])
>>> next(it)
1
>>> next(it)
2
还可使用它从函数或其他可调用对象创建可迭代对象
可从迭代器创建序列。
七,生成器
7.1简单生成器
包含 yield 语句的函数都被称为生成器。生成器的行为与普通函数截然不同。差别在于,生成器不是使用 return 返回一个值,而是可以生成多个值,每次一个。每次使用 yield 生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行。
为使用所有的值,可对生成器进行迭代
>>> nested = [[1, 2], [3, 4], [5]]
>>> for num in flatten(nested):
... print(num)
...
1
2
3
4
5
或
>>> list(flatten(nested))
[1, 2, 3, 4, 5]
生成器推导:
>>> g = ((i + 2) ** 2 for i in range(2, 27))
>>> next(g)
16
不同于列表推导,这里使用的是圆括号
直接在一对既有的圆括号内(如在函数调用中)使用生成器推导时,无需再添加一对圆括号
sum(i ** 2 for i in range(10))
7.2递归式生成器
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
调用 flatten 时,有两种可能性(处理递归时都如此):基线条件和递归条件。
在基线条件下,要求这个函数展开单个元素(如一个数)。在这种情况下, for 循环将引发 TypeError 异常(因为你试图迭代一个数),而这个生成器只生成一个元素。
然而,如果要展开的是一个列表(或其他任何可迭代对象),你就需要做些工作:遍历所有的子列表(其中有些可能并不是列表)并对它们调用 flatten ,然后使用另一个 for 循环生成展开后的子列表中的所有元素。这可能看起来有点不可思议,但确实可行。
>>> list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8]))
[1, 2, 3, 4, 5, 6, 7, 8]
注意
在函数 flatten 中,不应该对类似于字符串的对象进行迭代,主要原因有两个。首先,你想将类似于字符串的对象视为原子值,而不是应该展开的序列。其次,对这样的对象进行迭代会导致无穷递归,因为字符串的第一个元素是一个长度为1的字符串,而长度为1的字符串的第一个元素是字符串本身!
7.3通用生成器
生成器由两个单独的部分组成:生成器的函数和生成器的迭代器。生成器的函数是由 def 语句定义的,其中包含 yield 。生成器的迭代器是这个函数返回的结果。用不太准确的话说,这两个实体通常被视为一个,通称为生成器。
对于生成器的函数返回的迭代器,可以像使用其他迭代器一样使用它。
7.4生成器的方法
①send
在生成器开始运行后,可使用生成器和外部之间的通信渠道向它提供值。这个通信渠道包含如下两个端点。
外部世界:外部世界可访问生成器的方法 send ,这个方法类似于 next ,但接受一个参数(要发送的“消息”,可以是任何对象)。
生成器:在挂起的生成器内部, yield 可能用作表达式而不是语句。换而言之,当生成器重新运行时, yield 返回一个值——通过 send 从外部世界发送的值。如果使用的是 next (自然递归),yield 将返回 None 。
请注意,仅当生成器被挂起(即遇到第一个 yield )后,使用 send (而不是 next )才有意义。要在此之前向生成器提供信息,可使用生成器的函数的参数。
注意 如果一定要在生成器刚启动时对其调用方法 send ,可向它传递参数 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
用于在生成器中( yield 表达式处)引发异常,调用时可提供一个异常类型、一个可选值和一个 traceback 对象。
③close
用于停止生成器,调用时无需提供任何参数。
拓展:
方法 close (由Python垃圾收集器在需要时调用)也是基于异常的:在 yield 处引发GeneratorExit 异常。因此如果要在生成器中提供一些清理代码,可将 yield 放在一条 try / finally语句中。如果愿意,也可捕获 GeneratorExit 异常,但随后必须重新引发它(可能在清理后)、引发其他异常或直接返回。对生成器调用 close 后,再试图从它那里获取值将导致 RuntimeError 异常。
7.5模拟生成器
针对Python旧版本