枚举
在各种编程语言中常用的枚举类型在python中当然不例外的也是有的,创建一个枚举非常简单,如下:
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
创建了一个枚举类'Month',包含('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')十二个枚举
调用也很简单
Month.Jan
枚举数值是默认从0开始的,如果想要自定义枚举的数值可以用下面这种方式创建枚举类:
@unique # 装饰器用以检测没有重复值
class Gender(Enum):
Male = 1000
Female = 1001
创建了一个性别枚举
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# 测试:
bart = Student('Bart', Gender.Male)
if bart.gender == Gender.Male:
print('测试通过!')
else:
print('测试失败!')
type函数的新用法
前面讲到过,type函数可以用来判断对象的类型,这次介绍type的新用法
type()函数既可以返回一个对象的类型,又可以创建出新的类型
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
- 要创建一个class对象,type()函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
- 通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
创建好一个新的类以后,我们就可以正常的调用他的方法和属性等:
h = Hello()
h.hello()
这是一种动态创建类的方法,可以灵活使用。
元类
类似OC中的基类去理解
创建一个元类:
class ListMetaclass(type):
def __new__(cls, name, base, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, base, attrs)
- new()方法接收到的参数依次是:
- 当前准备创建的类的对象;
- 类的名字;
- 类继承的父类集合;
- 类的方法集合。
class MyList(list, metaclass=ListMetaclass):
pass
L = MyList()
L.add(1)
print(L)
这样我们给Mylist添加了一个add方法,实际很难用到这种做法。
当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.new()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
错误调试、测试
-
try...except...finally
高级语言通常都内置了一套try...except...finally...的错误处理机制,Python也不例外。
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
错误应该有很多种类,如果发生了不同类型的错误,应该由不同的except语句块处理。
try:
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally...')
print('END')
所有的错误类型都继承自BaseException,所以在使用except时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”
出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。
记录错误
-
logging
Python内置的logging模块可以非常容易地记录错误信息:
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
-
raise
只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n == 0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
raise语句如果不带参数,就会把当前错误原样抛出。此外,在except中raise一个Error,还可以把一种类型的错误转化成另一种类型:
def foo(s):
n = int(s)
if n==0:
raise ValueError('invalid value: %s' % s)
return 10 / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise
练习
运行下面的代码,根据异常信息进行分析,定位出错误源头,并修复:
# from functools import reduce
def str2num(s):
try:
return int(s)
except ValueError as e:
return float(s)
def calc(exp):
ss = exp.split('+')
ns = map(str2num, ss)
return reduce(lambda x, y: x + y, ns)
def main():
r = calc('100 + 200 + 345')
print('100 + 200 + 345 =', r)
r = calc('99 + 88 + 7.6')
print('99 + 88 + 7.6 =', r)
main()
-
调试,断言,凡是用print()来辅助查看的地方,都可以用断言(assert)来替代:
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
-
pdb.set_trace()
这个方法也是用pdb,但是不需要单步执行,我们只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点:
import pdb
s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)
虽然用IDE调试起来比较方便,但是最后你会发现,logging才是终极武器。
把print()替换为logging
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
网友评论