美文网首页
枚举、元类和错误调试

枚举、元类和错误调试

作者: TAsama | 来源:发表于2018-11-17 14:51 被阅读5次

枚举

在各种编程语言中常用的枚举类型在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个参数:
  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
  4. 通过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()方法接收到的参数依次是:
  1. 当前准备创建的类的对象;
  2. 类的名字;
  3. 类继承的父类集合;
  4. 类的方法集合。
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)

相关文章

  • 枚举、元类和错误调试

    枚举 在各种编程语言中常用的枚举类型在python中当然不例外的也是有的,创建一个枚举非常简单,如下: 创建了一个...

  • 使用枚举类和元类

    获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量 创建类的三种方式: 正常情况下,我们都...

  • python定制类、枚举类和元类

    定制类 : 类里面有很多自带的方法,我们可以重写他们 _str_ print一个实例对象时会自动调用_str_方法...

  • swift元类型

    一、元类型(术语表示metaType) 元类型是指所有类型的类型,包括类、结构体、枚举和协议。 类、结构体或枚举类...

  • Swift 元类型、self、Self、AnyObject、An

    1.元类型 元类型是指所有类型的类型,包括类、结构体、枚举和协议。类、结构体或枚举类型的元类型是相应的类型名紧跟....

  • Python 面向对象高级编程

    使用__slots__ 使用@property 多重继承 定制类 使用枚举类 使用元类

  • Kotlin面向对象 (6)枚举类

    枚举类构造函数枚举常用属性和函数 kotlin 中使用 enum 和 class 两个关键词声明枚举类。 枚举类使...

  • java enum实现原理

    一、分析自定义枚举类 普通的枚举类和抽象枚举类相似,故直接分析抽象枚举类。 1. 编写一个抽象枚举类 2. 编译 ...

  • 【C#与.net】2.0 变量、常量、枚举、Console类、运

    1.0 变量 2.0 常量 3.0 枚举 4.0 Console类的使用 5.0 运算符 6.0 程序调试 END

  • 枚举和类

    枚举和类的异同 相同点: 枚举类是一种特殊的类,它和普通的类一样,有自己的成员变量、成员方法、构造器。 枚举类也可...

网友评论

      本文标题:枚举、元类和错误调试

      本文链接:https://www.haomeiwen.com/subject/xjobfqtx.html