python--------------------------模术方法

本文详细介绍了Python的魔术方法,包括特殊属性、魔术方法的分类及其应用,如`__hash__`、`__bool__`、运算符重载、上下文管理等,并通过实例展示了如何在类中实现这些方法,以提升代码的可读性和功能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

特殊属性

属性含义
name类,函数,方法等的名字
modeule类定义所在的模块名
class对象或类所属的类
bases类的基类的元组,顺序为它们在基类列表中出现的顺序
doc类函数的文档字符串,如果没有定义则为None
mro类的mro,class.mro()返回结果保存在__mro__中
dict类或实例的属性,可写的字典

查看属性

dir|返回类或者对象的所有成员名称列表dir()函数炒作实例就是调用__dir__().是用实力调用时,如果提供__dir__(),则返回其返回字,要求是可迭代对象.如果没有__dir__().这回从实例和列及祖先类中收集信息
如果dir([obj])参数obj包含方法__dir__(),该方法将被调用.如果参数obj不包含__dir__(),该方法将最大限度地手机属性信息.
dir(obj)对于不同类型的对象obj具有不同的行为:

  • 如果对象是模块对象,返回的列表包含模块的属性名和变量名.
  • 如果对象是类型或者类对象,返回的列表包含类的属性名,及它的基类的属性名.
  • 如果obj不写,返回列表包含内容不同
    • 在模块中,返回模块的列表包含模块的属性名和变量名.
    • 在函数中,返回本地作用域的变量名
    • 在方法中,返回本地作用域的变量名
# animal.py
class Animal:
    x = 123
    def __init__(self, name):
        self.name = name
        self.__age = 10
        self.weight = 20

print(' Animal Module\'s names = {}'.format(dir()))



#Cat.py
import animal

from animal import Animal

class Cat(Animal):
    x = 'cat'
    y = 'abcd'

class Dog(Animal):
    def __dir__(self):
        return ['dog']  #必须返回可迭代对象

print('---------------')
print('Current MOdule\'s name = {}'.format(dir()))
print('animal Module\'s names = {}'.format(dir(animal)))
print("object's __dict__      = {}".format(dir(sorted(object.__dict__.keys()))))
print("Animal's dir()         = {}".format(dir(Animal)))
print("Cat's dir()            = {}".format(dir(Cat)))
print("--------------------")
tom = Cat('tom')
print(sorted(dir(tom)))
print(sorted(tom.__dir__()))

print("Dog's dir = {}".format(dir(Dog)))
dog = Dog('snoppy')
print(dir(dog))
print(dog.__dict__)


运行结果:
 Animal Module's names = ['Animal', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
---------------
Current MOdule's name = ['Animal', 'Cat', 'Dog', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'animal']
animal Module's names = ['Animal', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
object's __dict__      = ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Animal's dir()         = ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x']
Cat's dir()            = ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']
--------------------
['_Animal__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'weight', 'x', 'y']
['_Animal__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'weight', 'x', 'y']
Dog's dir = ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x']
['dog']
{'name': 'snoppy', '_Animal__age': 10, 'weight': 20}

dir()测试如下

class Person:
    def show(self):
        a = 100
        t = int(a)
        print(dir())

def test(a=50,b=100):
    print(dir())

Person().show()
test()

#显示结果如下
['a', 'self', 't']
['a', 'b']

魔术方法

  • 分类:
    创建.初始化与销毁
    • init__与__del
      hash
      bool
      可视化
      运算符重载
      容器和大小
      可调用对象
      上下文管理
      反射
      描述器
      其他杂项

实例化

方法意义
new实例化一个对象,该方法需要返回一个值,如果该值不是cls的实例,则不会调用__init__该方法用元都是静态方法
class A:
    def __new__(cls, *args, **kwargs):
        print(cls)
        print(*args)
        print(**kwargs)
        #return None
        #return 1
        return super().__new__(cls)
    def __init__(self, name):
        self.name = name

a = A('abc')
print(a)

__new__方法很少使用,即使创建了该方法,也会使用returnsuper().new(cls)基类object的__new__方法来创建实例并返回

hash

方法意义
hash内建函数hash()调用的返回字,返回一个整数.如果定义这个方法该类的实例就可hash
print(hash(1))
print(hash('tom'))
print(hash(('tom',)))

返回结果:
1
7949454942506878941
2006260494481220906
class A:
    def __init__(self, name, age=18):
        self.name = name
    def __hash__(helf):
        return 1
    def __repr__(self):
        return self.name

print(hash(A('tom')))
print((A('tom'),A('tom')))
print([A('tom'),A('tom')])
print('-------------------')
s = {A('tom'),A('tom')}  #set
print(s)
print({tuple('t'), tuple('t')})
print({('tom',), ('tom',)})
print({'tom', 'tom'})

上例中set为什么不能提出相同的key?
hash值相同就会去重吗?


class A:
    def __init__(self, name, age=18):
        self.name = name

    def __hash__(self):
        return 1

    def __eq__(self, other):
        return self.name == other.name

    def __repr__(self):
        return self.name

print(hash(A('tom')))
print((A('tom'),A('tom')))
print([A('tom'),A('tom')])
print('`````````````````````')
s = {A('tom'), A('tom')}  #set
print(s)
print({tuple('t'),tuple('t')})
print({('tom',),('tom',)})
print({'tom', 'tom'})
方法意义
__eq__对应==操作符,判断2个对象是否相等,返回bool值

hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__来判断2个对象是否相等.
hash值相等,只是hash冲突,不能说明两个对象是相等的.
因此,一般来说提供__hash__方法是为了作为set或者dict的key,所以去重要同时提供__eq__方法.
不可hash对象isninstance(p1,collections.hashable)一定为Fasle.
去重需要提供__eq
-方法
思考:
list实例为什么不可hash
练习:
设计坐标类Point,使其称为可hash类型,并比较坐标的实例是否相等?
list类实例为什么不可hash
源码中有一句__hash
_ = None,也就是如果调用__hash__()相当于None(),一定报错.
所有类都能继承object,而这个类是就有__hash__()方法的,如果一个类不能被hash,就把 __hash__设置为None.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __hash__(self):
        return hash((self.x, self.y))

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
a = Point(4,5)
b = Point(4,6)
print(hash(a),hash(b))
print({a,b})
print(a == b)

bool

方法意义
__bool__内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值.没有定义__bool__(),就找__len__()返回长度,非0为真,如果__len__()也没有定义,那么所有实例都返回真
class A:pass

print(bool(A()))
if A():
    print('Real A')

class B:
    def __bool__(self):
        return False

print(bool(B))
print(bool(B()))
if B():
    print('Real B')

class C:
    def __len__(self):
        return 0

print(bool(C()))

if C():
    print('Real C')

可视化

方法意义
__repr__内建函数repr()对一个对象获取字符串表达.调用__repr__方法返回字符串表达,如果__repr__也没有定义,就直接返回object的定义就是显示内存地址信息
__str__str()函数,format()函数,print()函数调用,需要返回对象的字符串表达.如果没有定义,就直接去调用__repr__方法返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息
__bytes__bytes()函数调用,返回一个对象的bytes()表达,及返回bytes对象
class A:
    def __init__(self, name, age = 18):
        self.name = name
        self.age = age

    def __repr__(self):
        return "repr: {},{}".format(self.name,self.age)

    def __str__(self):
        return "str: {},{}".format(self.name, self.age)

    def __bytes__(self):
        import json
        return json.dumps(self.__dict__).encode()

print(A('tom'))
print([A('tom')])
print([str(A('tom'))])

print('str:a,1')
s = '1'
print(s)
s1 = 'a'
print(s1)
print([s1],(s,))
print({s, 'a'})

print(bytes(A('tom')))

注意不能通过判断是否带引号来判断输出值的类型,类型判断要使用type和isinstance

运算符重载

operator模块提供以下特殊方法,可以将类的实例使用下面的操作符来操作

运算符特殊方法含义
<,<=,==,>,>=,!=__lt__.__le__,__eq__,__gt__,__ge__,__ne__比较运算符
+,-,*,/,%,//,**,divmod__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__算数运算符,以为,位运算也有相应的方法
+=,-=,*=,/=,%=,//=,**=__iadd__,__isub__,__imul__,__itruediv__,__imod__,__ifloordiv__,__ipow__

实现A类的2个实例相减

class A:
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

    def __sub__(self, other):
        return self.age - other.age

    def __isub__(self, other): #如果没有isub就会调用sub
        return A(self.name, self.age - other.age)
    
tom = A('tom')
jerry = A('jerry', 16)

print(tom - jerry)
print(jerry - tom,jerry.__sub__(tom))

print(id(tom))
tom -= jerry
print(tom.age, id(tom))

__isub__方法定义,一般会in-place来修改自身
如果没有定义__isub__方法,则会调用__sub__
练习:
完成Point类设计,实现判断点相等的方法,并完成向量的加法
在执教坐标系里面,定义远点为向量的起点,两个向量和与差的坐标分别等于这两个向量相应坐标的和与差若向量的表示为(x, y)形式
A(x1, x2) B(y1, y2)
则A+B = (x1 + y1, x2 + y2)
A - B = (x1 - y1, x2 - y2)

class Point:
    def __init__(self, x ,y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __sub__(self, other):
        return (self.x - other.x, self.y - other.y)

    def __add__(self, other):
        return (self.x + other.x, self.y + other.y)

    def __iadd__(self, other):
        return Point(self.x + other.x, self.y + other.y)

    def __isub__(self, other):
        return Point(self.x - other.x, self.y - other.y)

p1 = Point(1, 1)  
p2 = Point(1, 1)
points = (p1, p2)
print(points[0] + (points[1]))
print(points[0].__add__(points[1]))
print(p1 == p2)

运算符重载应用场景

往往是用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式.例如,上例中的对+进行了运算符重载,实现了point类的二元操作,重新定义为Point + Point.
提供运算符重载,比直接提供加法方法更加适合该领域内使用者的习惯.
int类,几乎实现了所有操作符,可以作为参考.
@functools.total_ordering装饰器
__it__,le,eq,gt,ge__是比较大小必须实现的方法,但是全部写完太麻烦,使用@functools.total_ordering装饰器就可以大大简化代码.
但是要求__eq__必须实现,其他方法__it
,le,gt,__ge__实现其一,

from functools import total_ordering

@total_ordering
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.age == other.age

    def __gt__(self, other):
        return self.age > other.age

tom = Person('tom', 20)
jerry = Person('jerry', 16)

print(tom > jerry)
print(tom < jerry)
print(tom >= jerry)
print(tom <= jerry)

上例中大大简化代码,但是一般来说比较实现等于或者小于方法也就够了,其他可以不实现,所以这个装饰器只是看着很美好,且可能会带来性能问题,建议需要什么方法就自己创建,少用这个装饰器.

# from functools import total_ordering
#
# @total_ordering
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.age == other.age

    def __gt__(self, other):
        return self.age > other.age

    def __ge__(self, other):
        return self.age >= other.age

tom = Person('tom', 20)
jerry = Person('jerry', 16)

print(tom > jerry)
print(tom < jerry)
print(tom >= jerry)
print(tom <= jerry)

print(tom == jerry)
print(tom != jerry)

__eq__等于可以推断不等于
__gt__大于可以推断小于
__ge__大于等于可以推断小于等于
也就是用3个方法,就可以把所有比较解决了,所以total.ordering可以不使用

容器相关方法

方法意义
__len__内建函数len(),返回对象的长度(>=0的整数),如果把对象当做容器类型看,就如同list或者dict.bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真
__iter__迭代容器时,调用,返回一个新的迭代器对象
__contains__in成员运算符,没有实现,就调用__iter__方法遍历
__getitem__实现self[key]访问,序类对象,key接受整数为索引,或者切片.对于set和dict,key为hashable.key不存在引发KeyError异常
__setitem__和__getitem__访问类似,是设置值的方法
__missing__字典或其子类使用__getitem__()调用时,key不存在执行该方法
class Cart:
    def __init__(self):
        self.items = []

    def __len__(self):
        return len(self.items)

    def additme(self, num):
        self.items.append(num)
        return self

    def __iter__(self):
        yield from self.items

    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self, key, value):
        self.items[key] = value
        return self

    def __str__(self):
        return str(self.items)

    def __add__(self, other):
        self.items.append(other)
        return self

cart = Cart()
cart.additme(1)
cart.additme('abc')
cart.additme(3) 

#长度,bool
print(len(cart))
print(bool(cart))

#迭代
for i in cart:
    print(i)
#in
print(3 in cart)
print(2 in cart)

#索引操作
print(cart[1])
cart[1] = 'xyz'
print(cart)

#链式不变成实现加法
print(cart + 4 + 5 + 6)
print(cart.__add__(17).__add__((18)))

可调用对象

python中一切皆对象,函数也不例外

def foo():
    print(foo.__module__, foo.__name__)

foo()
# 等价于
foo.__call__()

函数即对象,对象foo加上(),就是调用此函数对象的__call__()方法
可调用对象

方法意义
__call__类中定义一个该方法,实例就可以像函数一样调用

可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用

class Point:
    def __init__(self, x ,y):
        self.x = x
        self.y = y

    def __call__(self, *args, **kwargs):
        return "<Piont>{}{}".format(args, kwargs)

p = Point(4, 5)
print(p)
print(p(1))

class Adder:
    def __call__(self, *args):
        ret = 0
        for x in args:
            ret += x
        self.ret = ret
        return ret

adder = Adder()
print(adder(4, 5, 6))
print()

练习,定义一个斐波那契数列的类,方便调用,计算第n项

class Fib:
    def __init__(self):
        self.items = [0, 1, 1]

    def __call__(self, index):
        if index < 0:
            raise IndexError('Wrong Index')
        if index < len(self.items):
            return self.items[index]
        for i in range(3, index + 1):
            self.items.append(self.items[i - 1] + self.items[i - 2])
        return self.items[index]
    
print(Fib()(100))

上例中,增加迭代的方法,返回容器长度,支持索引的方法

class Fib:
    def __init__(self):
        self.items = [0, 1, 1]

    def __call__(self, index):
        return self[index]

    def __len__(self):
        return len(self.items)

    def __getitem__(self, index):
        if index < 0:
            raise IndexError('Wrong Index')
        if index < len(self.items):
            return self.items[index]
        for i in range(len(self.items), index + 1):
            self.items.append(self.items[i - 1] + self.items[i - 2])
        return self.items[index]

    def __iter__(self):
        yield  from self.items

    def __str__(self):
        return str(self.items)

    __repr__ = __str__

fib = Fib()
print(fib(5),len(fib))
print(fib(10), len(fib))
for x in fib:
    print(x)
print(fib[5],fib[6],fib(101))

上下文管理

文件 I/O操作可以对文件对象使用上下文管理,使用with…as语法

with open('test') as f:
    pass

仿照上例写一个自己的类,实现上下文管理

class Point:
    pass

with Point() as p:
    pass

提示属性错误,没有__exit__,看来需要这个属性
某些版本会显示没有__enter__
上下文管理对象
当一个对象同时实现了__enter__()和__exit__()方法,它就属于上下文管理的对象

方法意义
__enter__进入与此对象相关的上下文,如果存在该方法,with语法会把该方法的返回字作为绑定到as子句中指定的变量上
__seit__退出与此县官的上下文
class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

import time
with Point() as f:
    print('-' * 30)
    print(f)
    time.sleep(5)
    print('*' *30)

print('===============end===============')

实例化对象的时候,并不会调用enter,进入with语句快调用__enter__方法,然后执行语句块,最后离开with语句块的时候,调用__exit__方法.
with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作.
注意:
with并不开启一个新的作用域
上下文管理的安全性
看异常对上下文的影响

class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:
    raise Exception('Error')
    print('do sth')

可以看出在enter和exit照样执行,上下文管理是安全的
极端的例子
调用sys.exit(),它会退出当前解释器.
打开Python捷思奇,在里面敲入sys.exit(),窗口直接关闭了,也就是说碰到这一句,Python运行环境直接退出了.

import sys
class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:
    sys.exit()
    print('do sth')

从执行结果来看,依然执行了__exit__函数,哪怕是退出Python运行环境.
说明上下文管理很安全

with语句

import sys
class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

p = Point()
with p as f:
    print(p == f)
    print('do sth')
    print(1,f)

返回结果f为None,问题再__enter__方法上,它将自己的返回字赋值给f.

import sys
class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

p = Point()
with p as f:
    print(p == f)
    print('do sth')
    print(1,f)

__enter__方法返回值就是上下文使用的对象,with语法会把它的返回字赋给as子句的变量

方法的参数

__enter__方法,没有其他的参数.
exit__方法有三个参数:
def _\exit
(self, exc_type, exc_val, exc_tb):
这三个都与异常有关.
如果该上下文退出时没有异常,这三个参数都为None
如果有异常,参数意义如下:
exc_type:异常的类型
exc_value,异常的值
traceback:异常的追踪信息
__exit__方法赶回一个等效true的值,这压制一场;否则,继续抛出异常

import sys
class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        print('exit')
        return True

p = Point()
with p as f:
    raise Exception('Error')
    print('do sth')

练习
为加法函数计时
方法1,使用装饰器显示该函数的执行时长
方法2,使用上下文管理方法来显示该函数的执行时长

import time

def add(x, y):
    time.sleep(2)
    return x + y

装饰器实现

import time
import datetime
from functools import wraps

# def timeit(fn):
#     @wraps(fn)
#     def wapper(*args,**kwargs):
#         start = datetime.datetime.now()
#         ret = fn(*args,**kwargs)
#         end = (datetime.datetime.now() - start).total_seconds()
#         print(end)
#         return ret
#     return wapper



class timeit:
    def __init__(self, fn):
        self.fn = fn

    def __enter__(self):
        self.start = datetime.datetime.now()
        return self.fn

    def __exit__(self, exc_type, exc_val, exc_tb):
        end = (datetime.datetime.now() - self.start).total_seconds()
        print(end)
        return True



# @timeit
def add(x, y):
    time.sleep(2)
    return x + y


with timeit(add) as f:
    f(4,5)
    print(f)

# print(add(4,5),add.__dir__())

试下直接把类当作装饰器用

class timeit:
    def __init__(self, fn):
        self.fn = fn

    def __enter__(self):
        self.start = datetime.datetime.now()
        return self.fn

    def __exit__(self, exc_type, exc_val, exc_tb):
        end = (datetime.datetime.now() - self.start).total_seconds()
        print(end)
        return True

    def __call__(self, *args, **kwargs):
        ret = self.fn(*args,**kwargs)
        return ret

@timeit
def add(x, y):
    time.sleep(2)
    return x + y

print(add(4,5),add.__dir__())

上面的类即可以用在上下文管理,又可以用作装饰器
上下文应用场景
1,增强功能
在代码执行的前后增加代码,以增强其功能.类似装饰器的功能.
2,资源管理
打开了资源需要关闭,例如文件对象,网络连接,数据库连接
3,权限验证
在执行代码之前,做权限的验证,在__enter__中处理
contextlib.contextmanager
contextlib.contextmanager
它是一个装饰器实现上下文管理,装饰一个函数,而不是响雷一样实现__enter__和__exit__方法.对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值.
也就是这个装饰器接受一个生成器对象作为参数.

import contextlib
@contextlib.contextmanager
def foo():
    print('enter')
    yield 5
    print('exit')

with foo() as f:
    # raise Exception(5)
    print(f)

上面的例子没有办法压制异常

import contextlib
@contextlib.contextmanager
def foo():
    try:
        print('enter')
        yield 5
    finally:
        print('exit')

with foo() as f:
    raise Exception(5)
    print(f)

加上try语句就可以了,那么如何修改为装饰器呢

import contextlib
import datetime
import time

@contextlib.contextmanager
def add(x, y):
    start = datetime.datetime.now()
    print('enter')
    time.sleep(2)
    try:
        yield x + y
    finally:
        end = (datetime.datetime.now() - start).total_seconds()
        print(end)
        print('exit')

with add(4, 5) as f:
    raise Exception(5)
    print(f)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值