对比静态属性和描述符:
两者都可以进行对参数的类型检测,前面有过详细的代码及分析。当两者结合在一起时会发生什么事:
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__初始化方法,最后返回该对象。
另外的姿势创建元类:
class MyType(type):
def __init__(self,*args,**kwargs):
print("init")
super().__init__(*args,**kwargs)
def __call__(self, *args, **kwargs):
print("call")
return super().__call__(*args, **kwargs)
# class xxx(metaclass=MyType):
# pass
#
# class Foo(xxx):
# pass
class Foo(MyType("xxx", (object,), {})): # 这样写法等同于上面注释内容
pass
# def with_meta(base): 通过函数进行再次封装
# return MyType("xxx", (base,), {})
#class Foo(with_meta(object)):
# pass
f = Foo()
>>> init
>>> init
>>> call
异常处理:
错误分为语法错误和逻辑错误。
异常:程序运行时发生错误的信号由异常类和异常值和追踪信息组成。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用于主动抛出异常。