使用枚举类
当我们在定义常量时,一个方法就是用大写变量通过整数来定义,这种方法简单,但是缺点是类型是int,并且仍然是变量,更好的方法时为这样的枚举类型定义一个class类型,然后,每个常量都是class的唯一的一个实例。
枚举的实现:
1.首先要导入enum模块
2.枚举定义要用class关键字,继承Enum类
3.用于定义枚举的class和定义类的class是有区别的。
示例:
fromenumimport Enum
classColor(Enum):
red
=1
orange =2
yellow =3
green =4
blue =5
indigo =6
purple =7
代码分析:
1.我们定义了颜色的枚举Color
2.有7个成员,每个成员都有自己的名字。例如:Colour.red成员的名称就是:red。值是1.
3.每个成员的数据类型就是它属的枚举。
注意:
1.定义枚举时,成员名称不能重复,不然会出现TypeError错误。
2.默认情况下,不同的成员值允许相同,但是两个相同值得成员,第二个成员名称视作是第一个成员名称的别名。在通过值获取枚举成员时,只能获取到第一个成员。
3.如果要限制枚举的成员不能有相同的值,可以使用装饰器@unique
fromenumimportEnum,unique
@unique
classColour(Enum):
red =1
yellow =1
执行就会出现:ValueError: duplicate values found in : yellow -> red
访问枚举:
1.通过成员名称获取成员
Color['red']
2.通过成员值获取
Color(5)
2.通过成员,来获取它的名称和值
yanse = Color.blue
yanse.name
'blue'
yanse.value
5
迭代枚举成员
1.遍历
forcolorinColor:
print(color)
输出为:Color.redColor.orangeColor.yellowColor.greenColor.blueColor.indigoColor.purple
2.如果成员值相同,就会遍历第一个成员
fromenumimport Enum
classColor(Enum):
red
=1
orange =2
yellow =3
green =4
blue =5
indigo =6
purple =7
red_wai =1
forcolorinColor:
print(color)
Color.redColor.orangeColor.yellowColor.greenColor.blueColor.indigoColor.purple
3.如果想把重复值也遍历出来,就要在枚举时加一个特殊属性__member__
fromenumimport Enum
classColor(Enum):
red
=1
orange =2
yellow =3
green =4
blue =5
indigo =6
purple =7
red_wai =1
forcolor in Color.__members__.items():
print(color)
输出为:('red', )('orange', )('yellow', )('green', )('blue', )('indigo', )('purple', )('red_wai', )
比较:
1.同一性比较
Color.redisColor.red
True
Color.redisnotColor.blue
True
2.等值比较
Color.blue == Color.red
False
3.不能进行大小比较
Color.red>Color.blue
TypeError: '>' not supported between instances of 'Color' and 'Color'
参考资料:
航行博客:https://www.cnblogs.com/ucos/p/5896861.html
使用元类
type()
在定义类型时,我们通常用class 类名称(object).......来定义,但在Python解释器中,遇到class定义,仅仅只是扫描一下,然后就调用type()函数创建出class。也就是说我们通过定义type()也可以创建出类来,
type()函数创建类要依次传入三个参数:
1.class的名称
2.继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法
3.class的方法名称与函数绑定
示例:
deffn(self,name ='world'):#先定义函数
print('Hello,%s.'%name)
Hello = type('Hello',(object,), dict(hello = fn))#创建Hello class
调用:
h = Hello()
h.hello()
Hello,world.
print(type(Hello))
print(type(h))
type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type。而h是一个实例,它的类型就是class Hello。
type()函数既可以返回函数类型,又可以创建新的函数。
但正常情况下,我们都用class Xxx.....来定义类,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言非常不同。
metaclass
metaclass直译就是元类,我们可以通过其创建出类:
先定义metaclass,就可以创建类,最后创建实例。
metaclass是Python面向对象里最难理解的,也是最难使用的魔术代码。正常情况下,不会碰到需要使用metaclass的情况。
所以暂且不看了。下面是SQL语句,没看懂。
错误、调试和测试
错误处理
在程序运行时,我们通常会遇到错误,如果返回错误码的话,我们在查看错误时非常不方便,因为函数本身应该返回的正常结果和错误码混在一起,造成调用者必须用大量的代码来判断是否出错。所以在python中内置了一套语句:try.........except..........finally........
try
如果我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳至错误处理代码,即except后,如果有finally语句块,则执行此语句块,至此,执行完毕。
举例:
try:
print('try......')
r =10/ int('a')
print('result:',r)
exceptValueErrorase:
print('ValueError:',e)
exceptZeroDivisionErrorase:
print('ZeroDivisonError:', e)
finally:
print('finally....')
print('END')
int()函数可能会抛出ValueError,还有ZeroDivisionError错误,所以用except来排除。也可以在except语句块后加一个else语句,当没有错误发生时,就会自动执行else语句块。
try:
print('try......')
r =10/ int('a')
print('result:',r)
exceptValueErrorase:
print('ValueError:',e)
exceptZeroDivisionErrorase:
print('ZeroDivisonError:', e)
else:
print('NoError')
finally:
print('finally....')
print('END')
注意:在Python中,错误也是一个class,也有父类和子类。所有的BaseException,在使用except时需要注意的是,它不光捕获该类型的错误,还将其子类‘一网打尽’。
使用try......except还有一个好处,可以跨越多层调用比如说main()函数调用foo()函数,foo()调用bar(),结果bar()出错了,这时只要我们捕获到main()函数就可以处理错误了,也就是说我们不需要再每一个可能出错的地方去捕获错误,只要在合适的层次去捕获错误就可以了。大大减小了写try......except.......finally的麻烦了。
调用栈
如果错误没有被捕获,那么错误就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。来解读下错误信息。举例:
#err.py:
deffoo(s):
return10/int(s)
defbar(s):
returnfoo(s)*2
defmain():
bar('0')
main()
因为int(s)返回的是0,在计算10/0时出错了。这是错误的源头。
在出错的时候,一定要分析错误的调用栈的信息,才能定位错误的位置。
记录错误
我们不捕获错误可以让解释器来打印出错误堆栈,但同时程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,同时让程序继续执行下去。然后分析错误信息。
Python内置的logging模块可以非常容易地记录错误信息:
importlogging
deffoo(s):
return10/int(s)
defbar(s):
returnfoo(s)*2
defmain():
try:
bar('0')
exceptExceptionase:
logging.exception(e)
main()
print('END')
通过配置,logging还可以把错误记录到日志文件里,方便日后排查。
抛出错误
错误不是凭空出现的,而是有意创建的,Python内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。
如果要抛出一个错误。根据需要,可以定义一个错误的class,选择好继承关系,然后。用raise语句抛出一个错误的实例:
#err_raise.py
classFooError(ValueError):
pass
deffoo(s):
n = int(s)
ifn ==0:
raiseFooError('invalid value: %s'%s)
return10/n
foo('0')
错误中就出现了我们自己定义的错误。
只有在必要的时候我们才自己定义错误类型,尽量使用python内置的错误类型。
有时候我们不知道错误如何处理,在捕获到错误时我们会继续往上抛,让顶层调用者去处理,
raise语句如果不带参数,就会把当前的错误原样抛出。
deffoo(s):
n = int(s)
ifn==0:
raiseValueError('invalid value: %s'%s)
return10/n
defbar():
try:
foo('0')
exceptValueErrorase:
print('ValueError')
raise
bar()
此外,在except中raise一个Error,还可以把一种错误转化成另一个错误:
try:
10/0
exceptZeroDivisonError:
raiseValueError('input error!')
但前提是转换逻辑要合理,不能转化成毫不相关的错误类型。
调试
程序写完能正常运行的概率很小,基本不会超过1%,总会出现或简单或复杂的bug,所以我们需要一整套的调试方法去修复这些bug。
print()语句:简单的方法就是把它打印出来,用print()语句把可能出现问题的变量打印出来看看。但这种办法是在修正后还要在删除掉print(),所以有了下面的方法:
断言(assert)
凡是print()语句来辅助查看的地方,都可以用断言(assert)来替代。
deffoo(s):
n = int(s)
assertn !=0,'n is zero!'
return10/n
defmain():
foo('0')
例子中的assert意思是,表达式 n != 0应该是True,否则根据程序运行的逻辑,后面的代码肯定会出错的。如果断言失败,assert语句本身会抛出AssertionError
AssertionError: n is zero!
但如果程序中充满着assert,和print()语句相比也好不哪去,不过,我们可以启动python解释器时可以用-0参数来关闭assert。关闭后,可以把所有的assert语句当成pass来看。