1. 异常
异常未被捕捉时程序停止且出现错误信息traceback
- raise语句
- raise Exception,将一个类(必须是exception的子类或实例)作为参数,可多添加错误消息raise Exception('hyperdrive overload')
- 常用内置异常类:
- Exception:几乎所有的异常类都是从它派生而来的
- AttributeError:引用属性或给它赋值失败时引发
- OSError:操作系统不能执行指定的任务(如打开文件)时引发,有多个子类
- IndexError:使用序列中不存在的索引时引发,为LookupError的子类
- KeyError:使用映射中不存在的键时引发,为LookupError的子类
- NameError:找不到名称(变量)时引发
- SyntaxError:代码不正确时引发(这也太搞笑了吧,啥都可以SyntaxError?)
- TypeError:将内置操作或函数用于类型不正确的对象时引发
- ValueError:将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适
- ZeroDivisionError:在除法或求模运算的第二个参数为零时引发
- 自定义异常类:class SomeCustomException(Exception): pass
- 捕获异常
#1.捕获异常
try:
x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y)
except ZeroDivisionError:#捕获意思就是出现了某个要类别的错误,就被捕获到了
print("The second number can't be zero!")
#2.多个except
try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except ZeroDivisionError:
print("The second number can't be zero!")
except TypeError:
print("That wasn't a number, was it?")
#3.或者一次性捕获,但没什么用
try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except (ZeroDivisionError, TypeError, NameError):
print('Your numbers were bogus ...')
#4.捕获对象
try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except (ZeroDivisionError, TypeError) as e:
print(e)
- 引发别的异常
#想引发别的异常:此时ValueError会出现在打印出的错误中
try:
1/0
except ZeroDivisionError:
raise ValueError
#也可以raise...from...来提供自己的异常上下文,也可使用None来禁用上下文:raise ValueError from None
- 其他异常语句
#添加else语句
try:
print('A simple task')
except:
print('What? Something went wrong?')
else:
print('Ah ... It went as planned.')
#finally子句,可用于在发生异常时执行清理工作:不管try子句中发生什么异常,都将执行finally子句
#异常没有被处理的化会一直向上传播到各种调用的函数
- 不那么异常用warn:from warnings import warn,warn("I've got a bad feeling about this.")
2. 魔法方法
2.1 构造函数_init_
重写构造函数:会消除超类的构造函数,若用到超类的属性则没有了
解决方法一:调用未关联的超类构造函数,因为没有实例与之关联
解决方法二:使用函数 super,实际上,它返回的是一个super对象,这个对象将负责为你执行方法解析。(就是用到子类没有的属性就在所有继承的父类里面挨个找,找不到就AttributeError
#父类
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):
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
##解决方法一:调用未关联的超类构造函数,因为没有实例与之关联
class SongBird(Bird):
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
#解决方法二:使用函数 super,实际上,它返回的是一个super对象,这个对象将负责为你执行方法解析。
#当你访问它的属性时,它将在所有的超类(以及超类的超类,等等)中查找,直到找到指定的属性或 引发AttributeError异常。
class SongBird(Bird):
def __init__(self):
super().__init__()
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
2.2 基本的序列和映射协议
不可变对象要实现两个方法,可变要实现4个(不可变不能set和del,方法名字顾名思义,很简单的~)
- _len_(self):这个方法应返回集合包含的项数,对序列来说为元素个数,对映射来说 为键值对数。如果__len__返回零(且没有实现覆盖这种行为的__nonzero__),对象在布尔上下文中将被视为假(就像空的列表、元组、字符串和字典一样)。
- _getitem_(self, key):这个方法应返回与指定键相关联的值。对序列来说,键应该是0~n-1的整数(也可以是负数,这将在后面说明),其中n为序列的长度。对映射来说,键可以是任何类型。
- _setitem_(self, key, value):这个方法应以与键相关联的方式存储值,以便以后能够使用__getitem__来获取。当然,仅当对象可变时才需要实现这个方法
- _delitem_(self, key):这个方法在对对象的组成部分使用__del__语句时被调用,应删除与key相关联的值。同样,仅当对象可变(且允许其项被删除)时,才需要实现这个方法。
#例如继承list后
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)
2.3 property函数
作用为在新式类中返回属性值,语法为class 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
#上面这个类不能单独r.size,只能r.get_size
#加上一行size = property(get_size, set_size)后就可以了,
2.4 静态方法和类方法
- 静态方法:类或实例调用;定义中没有参数self
- 类方法:类或实例调用;定义中有cls
- 实例方法:只能实例调用;定义中有self
class MyClass:
@staticmethod
def smeth():#定义中没self,调用时用类不用对象就可以
print('This is a static method')
@classmethod
def cmeth(cls):#用类调用
print('This is a class method of', cls)
MyClass.smeth()
MyClass.cmeth()
2.5 其余魔法方法(与属性相关)
与序列的映射要求的区别就是序列是get/set/del+item
- _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()
3. 迭代器
- 方法 _iter_ 返回一个迭代器,它是包含方法 _next_ 的对象
相当于可迭代对象的__iter__方法每次都返回self,然后__next__里的代码会被执行一次,例如在for循环里 - 通过对可迭代对象调用内置函数iter,可获得一个迭代器,调用next进行迭代
- 除了对迭代器和可迭代对象进行迭代(通常这样做)之外,还可将它们转换为序列
#例1
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
#例2:用内置函数iter从列表得到迭代器,再调用next(iter)即可
it = iter([1, 2, 3])
next(it)
next(it)
#例3
class testiter:
value = 0
def __next__(self):
self.value += 1
if self.value > 10:raise StopIteration
return self.value
def __iter__(self):
return self
ti = testiter()
list(ti)
4. 生成器
注意迭代器是内置函数iter和next,但是生成器方法r.send()不是内置函数哈~
生成器不是使用return返回一个值,而是可以生成多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行。
4.1 创建生成器
nested = [[1, 2], [3, 4], [5]]
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element#不是print
for num in flatten(nested):#像列表一样放在这个位置
print(num)
list(flatten(nested)) #转换成序列
#简单生成器例子:列表推导
sum(i ** 2 for i in range(10))
4.2 递归式生成器
如果不止两层for,则用递归代替
def flatten(nested):
try:
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
#递归:递归条件和基线条件
#此处:基线条件:只剩一个数时,这个函数展开单个元素(如一个数)。for循环将引发TypeError异常,因为for不可能对一个数迭代,此时yield返回即可
list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8]))
4.3通用生成器
def simple_generator(): #生成器的函数:包含yield
yield 1#返回生成器的迭代器
simple_generator#方法
simple_generator()#生成器
4.4 生成器的方法:send
def repeater(value):
while True:
new = (yield value)
if new is not None:
value = new
r = repeater(42)
next(r)
r.send("Hello, world!")#send方法,可有一个参数,类似next,但仅当生成器被挂起(即遇到第一个yield)后,使用send(而不是next)才有意义
4.5 生成器的回溯
略(没仔细搞明白还)