Python----类的方法(元类、异常处理、断言)

对比静态属性和描述符:

两者都可以进行对参数的类型检测,前面有过详细的代码及分析。当两者结合在一起时会发生什么事:

class Typed:
    def __get__(self, instance, owner):
        print('get')

    def __set__(self, instance, value):
        print('set')

    def __delete__(self, instance):
        print('delete')

class Test:
    name = Typed()
    age = Typed()
    def __init__(self,name,age):
        self.name = name
        self.age = age

    @property   # 取值时触发
    def name(self):
        print('get')

    @name.setter  # 设置name变量时触发
    def name(self,val):
        print('name set')
        # if type(val) is not str:
        #     print('name error')

    @property
    def age(self):
        print('get')

    @age.setter   # 设置age变量时触发
    def age(self, val):
        print('age set')
        # if type(val) is not int:
        #     print('age error')

print(t.name)


>>> name set
age set
get

由此可见当两者共存时,先执行静态属性,静态属性的优先级高于描述符。(因为当从上到下执行文件时先执行装饰器)

描述符需要定义在另一个类,减少代码量,而静态属性方法不需要定义另外的类,代码量可能会比描述符多

元类:

类是 类的类 的实例,元类就是 类的类。正如类的实例是对象,元类的实例是类。type是python的内建元类。

类名=type(类名(字符串形式),(继承的类,以元组的形式,因为可以多继承所以是元组形式),属性和属性值{x;1})  这样就创建了一个类。和class定义的一样.

Foo = type('Foo', (object,), {'x': 1,'y':2})  # 必须有三个参数 类名,继承的类,类的属性
f1 = Foo()  # 正常的实例化对象
print(f1.x)
print(f1.y)


>>> 1
>>> 2

也可以调用初始化函数:

def __init__(self,name,age):
    self.name = name
    self.age = age

# 在类初始化时会自动调用__init__方法
Foo = type('Foo', (object,), {'x': 1,'__init__':__init__})

f1 = Foo('Jax',19)
print(f1.__dict__)


>>> {'name': 'Jax', 'age': 19}

还有另一种姿势:

def __init__(self,name,age):
    self.name = name
    self.age = age

# 如果__init__函数定义的名字不是__init__则需如下执行
Foo = type('Foo', (object,), {'x': 1,'eat':__init__})

f1 = Foo()
f1.eat('Jax',19)# 需要手动的调用初始化

print(f1.__dict__)


>>> {'name': 'Jax', 'age': 19}

让我们来自定义一个元类(metaclass)

class Foo(metaclass = type(默认就是继承type))。

# 元类
class Meta(type):
    def __init__(self,a,b,c):
        print(self)  # 类
        print(a)  # 类名
        print(b)  # 类的继承
        print(c)  # 类的属性字典

    def __call__(self, *args, **kwargs):
        p = object.__new__(self)  # 实例People类的对象
        p.__init__(*args,**kwargs)  # 到了People类的__init__方法
        return p  # 返回对象


class People(metaclass=Meta):   # metaclass 传4个参数到元类的初始化方法  类自动传 类名,类的继承,类的属性字典
    def __init__(self,name,age):
        self.name = name  # p.name = name
        self.age = age

p = People('Jax',19)   # 加括号运行类触发__call__方法
# print(p.__dict__)     # __call__方法里没有定义__dict__方法名
print(p.__dict__)


>>> {'name': 'Jax', 'age': 19}

执行顺序:首先正常加载元类,加载继承元类的类时,触发元类的__init__的方法,运行完后,跳到实例化对象,因为类加括号运行,所以触发__call__方法,在此方法内通过__new__方法实例一个对象,运行这个对象的__init__初始化方法,最后返回该对象。

异常处理:

错误分为语法错误和逻辑错误。

异常:程序运行时发生错误的信号由异常类和异常值和追踪信息组成。python统一了类与类型。

一些常见的异常类:

AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

异常处理的格式:Exception  万能异常类型,什么错误都会捕捉到

try:
    print('可能会出现错误的代码')
except ValueError as e:
    print(e)
    print('有错误会输出我')
except Exception as e:
    print('我是万能错误类')
else:
    print('try内部代码没有异则执行我')
finally:
    print('无论异常与否都会执行该模块,通常是执行清理工作')

自定义异常类:自己定制异常必须继承BaseException

class Zhuo(BaseException):
    def __init__(self,msg):
        self.msg = msg

try:
    raise Zhuo('报错了')
except Zhuo as e:
    print(e)

try...exception  应该少用,因为和程序主逻辑没有关系,用多了反而会使程序的可读性变差。

断言(assert):

用于判断,如果不成立则报错

assert 1==2 

类似于:

if 1 != 2:
    raise Exception('error')

raise用于主动抛出异常。

猜你喜欢

转载自blog.csdn.net/weixin_41678001/article/details/82820228