Python基础之更高级功能(三)

196 阅读7分钟

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 生成器的回溯

略(没仔细搞明白还)