为什么以下
class A(object): pass
class B: pass
def p(x): print x
a = A()
b = B()
a.__enter__ = lambda *a, **k: p('a.enter')
b.__enter__ = lambda *a, **k: p('b.enter')
a.__exit__ = lambda *a, **k: p('a.exit')
b.__exit__ = lambda *a, **k: p('b.exit')
print 'direct call'
a.__enter__()
b.__enter__()
a.__exit__()
b.__exit__()
print 'with statement'
with a: pass
with b: pass
打印以下内容,但也会抛出异常?
direct call
a.enter
b.enter
a.exit
b.exit
with statement
Traceback (most recent call last):
File "lol.py", line 21, in <module>
with a: pass
AttributeError: __exit__
GamesBrainiac是正确的,我希望猴子修补一个实例。这样做的原因是使用logging
标准库的标准方法是getLoggerClass
/ setLoggerClass
和getLogger
的组合。使用前一对将覆盖所有未来的行为(不可取的),并使用后者似乎阻止我猴子修补对象具有特殊行为。
答案 0 :(得分:0)
更新回复
你们lambda是未绑定的方法。传递a时,它使用类的__exit__
方法,而不是实例。由于该类没有方法,因此属性错误。
演示:
class C(object):
def __enter__(self):
print 'entered'
def __exit__(self, exc_type, exc_value, traceback):
print self
print exc_type
print 'exiting'
c = C()
with c:
print 'inside context'
打印:
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
但是当我们分配一个lambda时,我们仍然得到绑定方法:
c.__exit__ = lambda *args: p('lambda exit')
with c:
print 'inside context'
打印:
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
你可以使用模拟库来模拟类方法(我必须从带有`sudo apt-get install python-mock的python 2的Ubuntu repos中获取它),在Python 3的标准库中使用unittest.mock :
import mock # unittest.mock in Python 3
with mock.patch.object(C, '__exit__', autospec=True) as mock_method:
new_c = C()
with new_c:
print 'inside context'
# __exit__ is called with three None's if no exception
mock_method.assert_called_with(new_c, None, None, None)
打印:
entered
inside context
请注意,不再调用原始__exit__
方法。
答案 1 :(得分:0)
显然,上下文管理器功能仅适用于类。您在实例上定义它们。来自python docs:
Python的with语句支持由上下文管理器定义的运行时上下文的概念。这是使用一对方法实现的,这些方法允许用户定义的类定义在语句体执行之前输入的运行时上下文,并在语句结束时退出:
然而,我确实提出了这个我觉得适合你的黑客行为:
class A:
pass
def p(x):
print(x)
a = A()
class MonkeyPatched(a.__class__):
pass
MonkeyPatched.__enter__ = lambda *a, **k: p('a.enter')
MonkeyPatched.__exit__ = lambda *a, **k: p('a.exit')
a.__class__ = MonkeyPatched
with a:
pass
a = A()
with a:
pass
第一个with
有效,但不是第二个。
这相当丑陋,我不确定是否存在设置__class__
的副作用。
答案 2 :(得分:0)
你必须&#34; monkey-patch&#34;相应实例的类:
new_class = type(a.__class__.__name__, (a.__class__,), {})
new_class.__enter__ = ...
new_class.__exit__ = ...
a.__class__ = new_class
答案 3 :(得分:0)
试试这个,因为__enter__
绑定到__class__
:
from types import MethodType
class A(object):
pass
class B(object):
pass
a = A()
b = B()
def p(x):
print(x)
def en(self, *args):
p('starting')
return self
def ex(self, exc_type, exc_value, traceback):
p('finishing')
return False
a.__class__.__enter__ = MethodType(en, a)
a.__class__.__exit__ = MethodType(ex, a)
with a:
print("Inside stuff")