🌍个人简介
🍁作者简介:大家好,我是姐姐划船吗?运维领域创作者,🏅阿里云ACE认证高级工程师🏅
✒️个人主页:姐姐划船吗?🔥
🕺支持我:点赞👍+收藏⭐️+留言📝
🫀格言:你未必出类拔萃,但一定与众不同!🔥
🧊系列专栏:
🎒 阶段一:windows基础
🎒 阶段二:Linux基础知识
🎒 阶段三:shell基础+shell高级
🎒 阶段四:学会python,逆天改命
🎒 阶段五:Linux网络服务
🎒 阶段六:集群原理及架构
🎒 阶段七:云计算虚拟化技术

目录
1. 错误和异常
1.1 assert
2. 装饰器(扩展)
2.1 Class
2.2 参数
2.3 嵌套
2.4 functools.wraps
2.5 装饰器都能干嘛?
👑👑👑结束语👑👑👑
1. 错误和异常
按照一般的学习思路,掌握了前两节内容,已经足够编程所需了。但是,我还想再多一步,还是因为本教程的读者是要 from beginner to master。 |
1.1 assert
>>> assert 1==1>>> assert 1==0Traceback (most recent call last): File "", line 1, in AssertionError
从上面的举例中可以基本了解了 assert 的特点。 assert,翻译过来是“断言”之意。assert 是一句等价于布尔真的判定,发生异常就意味着表达式为假。 assert 的应用情景就有点像汉语的意思一样,当程序运行到某个节点的时候,就断定某个变量的值必然是什 么,或者对象必然拥有某个属性等,简单说就是断定什么东西必然是什么,如果不是,就抛出错误。 |
#!/usr/bin/env Python# coding=utf-8class Account(object): def __init__(self, number): self.number = number self.balance = 0 def deposit(self, amount): assert amount > 0 self.balance += balance def withdraw(self, amount): assert amount > 0 if amount <= self.balance: self.balance -= amount else: print "balance is not enough."
上面的程序中,deposit() 和 withdraw() 方法的参数 amount 值必须是大于零的,这里就用断言,如果不满足条件就会报错。比如这样来运行: |
if __name__ == "__main__": a = Account(1000) a.deposit(-10)
$ python 21801.pyTraceback (most recent call last): File "21801.py", line 22, in a.deposit(-10) File "21801.py", line 10, in deposit assert amount > 0AssertionError
这就是断言 assert 的引用。什么是使用断言的最佳时机?有文章做了总结: 如果没有特别的目的,断言应该用于如下情况: |
• 防御性的编程 • 运行时对程序逻辑的检测 • 合约性检查(比如前置条件,后置条件) • 程序中的常量 • 检查文档 |
不论是否理解,可以先看看,请牢记,在具体开发过程中,有时间就回来看看本教程,不断加深对这些概念的理解,这也是 master 的成就之法。 最后,引用危机百科中对“异常处理”词条的说明,作为对“错误和异常”部分的总结(有所删改): |
异常处理,是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况(即超出程序正常执行流程的某些特殊条件)。 各种编程语言在处理异常方面具有非常显著的不同点(错误检测与异常处理区别在于:错误检测是在正常的程序流中,处理不可预见问题的代码,例如一个调用操作未能成功结束)。某些编程语言有这样的函数:当输入存在非法数据时不能被安全地调用,或者返回值不能与异常进行有效的区别。例如,C 语言中的 atoi 函数(ASCII串到整数的转换)在输入非法时可以返回 0。在这种情况下编程者需要另外进行错误检测(可能通过某些辅助全局变量如 C 的 errno),或进行输入检验(如通过正则表达式),或者共同使用这两种方法。 通过异常处理,我们可以对用户在程序中的非法输入进行控制和提示,以防程序崩溃。 从进程的视角,硬件中断相当于可恢复异常,虽然中断一般与程序流本身无关。 从子程序编程者的视角,异常是很有用的一种机制,用于通知外界该子程序不能正常执行。如输入的数据无效(例如除数是 0),或所需资源不可用(例如文件丢失)。如果系统没有异常机制,则编程者需要用返回值来标示发生了哪些错误。 一段代码是异常安全的,如果这段代码运行时的失败不会产生有害后果,如内存泄露、存储数据混淆、或无效的输出。 Python 语言对异常处理机制是非常普遍深入的,所以想写出不含 try, except 的程序非常困难。
2. 装饰器(扩展)
装饰器 (Decorator) 在 Python 编程中极为常⻅,可轻松实现 Metadata 、 Proxy 、 AOP 等模式。 简单点说,装饰器通过返回包装对象实现间接调⽤,以此来插⼊额外逻辑。 语法看上去和 Java Annotation 、 C# Attribute 类似,但不仅仅是添加元数据。 |
>>> @check_args... def test(*args):... print args
>>> test = check_args(test)
类似的做法,我们在使⽤ staticmethod 、 classmethod 时就已⻅过。 |
>>> def check_args(func):... def wrap(*args):... args = filter(bool, args)... func(*args)... ... return wrap!! ! ! # 返回 wrap 函数对象>>> @check_args! ! ! ! # 解释器执⾏ test = check_args(test)... def test(*args):... print args>>> test! ! ! ! ! # 现在 test 名字与 wrap 关联。>>> test(1, 0, 2, "", [], 3)! ! # 通过 wrap(test(args)) 完成调⽤。(1, 2, 3)
整个过程⾮常简单: • 将⺫标函数对象 test 作为参数传递给装饰器 check_args 。 • 装饰器返回包装函数 wrap 实现对 test 的间接调⽤。 • 原函数名字 test 被重新关联到 wrap ,所有对该名字的调⽤实际都是调⽤ wrap 。 |
你完全可以把 "@" 当做语法糖,也可以直接使⽤函数式写法。只不过那样不便于代码维护,毕竟 AOP 极⼒避免代码侵⼊。 装饰器不⼀定⾮得是个函数返回包装对象,也可以是个类,通过 __call__ 完成⺫标调⽤。 |
>>> class CheckArgs(object):... def __init__(self, func):... self._func = func... ... def __call__(self, *args):... args = filter(bool, args)... self._func(*args)>>> @CheckArgs! ! ! ! ! ! # ⽣成 CheckArgs 实例。... def test(*args):... print args>>> test! ! ! ! ! ! ! # 名字指向该实例。>>> test(1, 0, 2, "", [], 3)! ! ! ! # 每次都是通过该实例的 __call__ 调⽤。(1, 2, 3)
⽤类装饰器对象实例替代原函数,以后的每次调⽤的都是该实例的 __call__ ⽅法。这种写法有点啰嗦,还得注意避免在装饰器对象上保留状态 |
2.1 Class
为Class 提供装饰器同样简单,⽆⾮是将类型对象做为参数⽽已。 |
>>> def singleton(cls):... def wrap(*args, kwargs):... o = getattr(cls, "__instance__", None)... if not o:... o = cls(*args, kwargs)... cls.__instance__ = o... ... return o... ... return wrap!! ! ! # 返回 wrap 函数,可以看做原 class 的⼯⼚⽅法。>>> @singleton... class A(object):... def __init__(self, x):... self.x = x>>> A>>> a, b = A(1), A(2)>>> a is bTrue
将 class A 替换成 func wrap 可能有些不好看,修改⼀下,返回 class wrap 。 |
>>> def singleton(cls):... class wrap(cls):... def __new__(cls, *args, kwargs):... o = getattr(cls, "__instance__", None)... if not o:... o = object.__new__(cls)... cls.__instance__ = o... ... return o... ... return wrap>>> @singleton... class A(object):... def test(self): print hex(id(self))>>> a, b = A(), A()>>> a is bTrue>>> a.test()0x1091e9990
创建继承⾃原类型的 class wrap ,然后在 __new__ ⾥⾯做⼿脚就⾏了。 ⼤多数时候,我们仅⽤装饰器为原类型增加⼀些额外成员,那么可直接返回原类型。 |
>>> def action(cls):... cls.mvc = staticmethod(lambda: "Action")... return cls>>> @action... class Login(object): pass>>> Login.mvc()'Action
这就是典型的 metaprogramming 做法了 |
2.2 参数
参数让装饰器拥有变化,也更加灵活。只是需要两步才能完成:先传参数,后送类型。 |
>>> def table(name):... def _table(cls):... cls.__table__ = name... return cls... ... return _table>>> @table("t_user")... class User(object): pass>>> @table("t_blog")... class Blog(object): pass>>> User.__table__'t_user'>>> Blog.__table__'t_blog
User = (table("t_user"))(User)
2.3 嵌套
>>> def A(func):... print "A"... return func>>> def B(func):... print "B"... return func>>> @A... @B... def test(): ... print "test"BA
test = A(B(test))
如果装饰器返回的是包装对象,那么有些东⻄必然是不同的。 |
>>> def check_args(func):... def wrap(*args):... return func(*filter(bool, args))... ... return wrap>>> @check_argsdef test(*args):... """test function"""... print args>>> test.__name__! ! ! # 冒牌货!'wrap'>>> test.__doc__! ! ! # ⼭寨货连个说明书都没有!
⼀旦 test 的调⽤者要检查某些特殊属性,那么这个 wrap 就会暴露了。幸好有 functools . wraps 。 |
>>> def check_args(func):... @functools.wraps(func)... def wrap(*args):... return func(*filter(bool, args))... ... return wrap>>> @check_argsdef test(*args): """test function""" print args>>> test>>> test.__name__'test'>>> test.__doc__'test function'>>> test(1, 0, 2, "", 3)(1, 2, 3)
functools . wraps 是装饰器的装饰器,它的作⽤是将原函数对象的指定属性复制给包装函数对象, 默认有 __module__ 、 __name__ 、 __doc__ ,或者通过参数选择。 |
2.5 装饰器都能干嘛?
• AOP: ⾝份验证、参数检查、异常⽇志等等。 • Proxy: 对⺫标函数注⼊权限管理等。 • Context: 提供函数级别的上下⽂环境,⽐如 Synchronized(func) 同步。 • Caching: 先检查缓存是否过期,然后再决定是否调⽤⺫标函数。 • Metaprogramming: 这个⾃不必多说了。 • 等等 …… |
👑👑👑结束语👑👑👑
