Python 如何在不首先定义特殊方法的情况下使用特殊方法?
为什么会出现以下情况Python 如何在不首先定义特殊方法的情况下使用特殊方法?,python,Python,为什么会出现以下情况 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.exi
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__
直接呼叫
a、 进入
b、 进入
a、 出口
b、 出口
带声明
回溯(最近一次呼叫最后一次):
文件“lol.py”,第21行,在
带着一张:通行证
AttributeError:\uuu退出__
GamesBrainiac是正确的,我正在寻找猴子补丁实例。原因是使用
日志记录
标准库的标准方法是getLoggerClass
/setLoggerClass
和getLogger
的组合。使用前一对将覆盖所有未来行为(不希望的),使用后一对似乎会阻止我将对象修补为具有特殊行为。更新的响应
lambda是未绑定的方法。传递时,它使用类的\uuuuuuuuuuuuu
方法,而不是实例。由于该类没有方法,这是一个属性错误
证明:
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
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
entered
inside context
印刷品:
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
entered
inside context
印刷品:
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
entered
inside context
<__main__.C object at 0x7fd885e4eed0>
None
exiting
entered
inside context
请注意,不再调用原始的
\uuuuuuuuuuuuuuuuuuuuuu
方法。显然,上下文管理器函数只对类起作用。您正在实例上定义它们。从python中:
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
使用的第一个有效,但第二个无效
这相当难看,我不确定设置\uuuuu class\uuuu
是否有副作用。您必须“猴子补丁”相应实例的类:
new_class = type(a.__class__.__name__, (a.__class__,), {})
new_class.__enter__ = ...
new_class.__exit__ = ...
a.__class__ = new_class
请尝试此操作,因为\uuuuu enter\uuuu
绑定到\uuuu class\uuuu
:
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")
那是个打字错误。将其更改为lambda:p(“…”)
仍会引发异常。问题更新。是的,你可以这样做,但他想要的是猴子补丁绑定的上下文管理器。如果你能稍等一下,我会给你写一个充实的答案。我想我已经为你提供了一个解决方案,我错了吗?@AaronHall我试过了。但是,我想你做不到。因为它不是用\uuuu dict\uuuu
@AaronHall waaaaiittt定义的!我想我猴子补的特殊方法@阿伦霍尔完成了!伙计,很棒的感觉真好。所有成员函数都是通过类查找的。您可以通过实际修改类来绕过该限制。@filmor,但我认为op希望对该实例进行修补。我认为Zach不想修改该类。既然您有文档说明了该类为什么不起作用,并提供了一个用于特定用途的示例,请给出要点@菲莫,这似乎并不准确。正常的查找不是这样工作的:@ZachRiggle成员函数总是这样查找的。我所说的成员函数是指将其第一个属性隐式分配给调用它的对象(“self
”)的函数。当然,您可以将函数放置在普通实例属性中,但这并不相同(如您所见)。此外,特殊的成员函数(即\uuuu call\uuu
,\uuu add\uuuu
等)总是被解析为给定意义上的成员函数。但本质上你仍然在创建一个新类。他正在创建一个新类。这不是答案。这似乎是在不修改原始类的情况下执行此操作的正确方法。这个解决方案和西蒙扎克的一样,但我只能给你们中的一个打分。我采用了一种与你的两个答案相似的方法。我正在创建一个行为与以前的类完全相同的类,它甚至有相同的名称(请注意,这与simonzack的解决方案有(微小的)区别)。特别是,isinstance
和issubclass
等功能将继续正常工作,只要您不为此使用a.。\uuuu class\uuuu
。我想这是唯一一种可以做你想做的事情的pythonic方法。除了与simonzack和我的答案几乎相同之外,这直接修改了A
。在此之后,A
的所有实例将具有覆盖的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
功能,顺便说一句,这些功能将始终具有!我编辑过一次(类型调用出错)。但是,这与解决方案中的问题无关:)