如何临时赋值成员变量?
How to assign member variables temporarily?
经常发现需要临时赋值一些成员变量,例如
old_x = c.x
old_y = c.y
# keep c.z unchanged
c.x = new_x
c.y = new_y
do_something(c)
c.x = old_x
c.y = old_y
但我希望我可以简单地写
with c.x = new_x; c.y = new_y:
do_something(c)
甚至
do_something(c with x = new_x; y = new_y)
Python 的装饰器或其他语言功能可以启用这种模式吗? (我可以根据需要修改 c
的 class)
mock
提供此功能,具体查看 patch.object
. It's in core libraries in python3, and available on pypi 的上下文管理器用法,用于较早的 python.
设置:
>>> class C:
... def __init__(self, x, y, z):
... self.x = x
... self.y = y
... self.z = z
...
>>> c = C(0,1,2)
使用演示:
>>> print(c.x, c.y, c.z)
0 1 2
>>> with patch.object(c, 'x', 'spam'), patch.object(c, 'y', 'eggs'):
... print(c.x, c.y, c.z)
...
spam eggs 2
>>> print(c.x, c.y, c.z)
0 1 2
Context managers 可以很容易地用于它。
引用官方文档:
Typical uses of context managers include saving and restoring various
kinds of global state, locking and unlocking resources, closing opened
files, etc.
看起来保存和恢复状态正是我们想要做的。
示例:
from contextlib import contextmanager
@contextmanager
def temporary_change_attributes(something, **kwargs):
previous_values = {k: getattr(something, k) for k in kwargs}
for k, v in kwargs.items():
setattr(something, k, v)
try:
yield
finally:
for k, v in previous_values.items():
setattr(something, k, v)
class Something(object):
def __init__(self, x, y):
self.x = x
self.y = y
def say_hello(self):
print("hello", self.x, self.y)
s = Something(1, 2)
s.say_hello() # hello 1 2
with temporary_change_attributes(s, x=4, y=5):
s.say_hello() # hello 4 5
s.say_hello() # hello 1 2
我认为 contextmanager
应该做你想做的事:
from contextlib import contextmanager
@contextmanager
def current_instance(c, temp_x, temp_y):
old_x, old_y = c.x, c.y
c.x, c.y = temp_x, temp_y
yield c
c.x, c.y = old_x, old_y
with current_instance(c, x, y) as c_temp:
do_something(c_temp)
您也可以使用 __enter__
和 __exit__
本机执行此操作。简单示例:
class SomeObject(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
class Temporary(object):
def __init__(self, target, **kv):
self.target = target
self.to_set = kv
self.to_restore = {}
def __enter__(self):
self.to_restore = map(partial(getattr, self.target), filter(partial(hasattr, self.target), self.to_set.keys()))
for k,v in self.to_set.items():
if hasattr(self.target, k):
self.to_restore[k] = getattr(self.target, k)
setattr(self.target, k, v)
def __exit__(self, *_):
for k,v in self.to_restore.items():
setattr(self.target, k, v)
for k in self.to_set.keys():
if k not in self.to_restore:
delattr(self.target, k)
o = SomeObject()
print(o.__dict__)
with Temporary(o, a=42, d=1337):
print(o.__dict__)
print(o.__dict__)
愚蠢的解决方案
>>> class Foo(object):
def __init__(self):
self._x = []
self._y = []
@property
def x(self):
return self._x[-1] or None
@x.setter
def x(self, val):
self._x.append(val)
def reset_vals(self):
if len(self._x) > 1:
self._x.pop()
>>> bar = Foo()
>>> bar.x = 1
>>> bar.x
1
>>> bar.x = 2
>>> bar.x
2
>>> bar.reset_vals()
>>> bar.x
1
>>> bar.reset_vals()
>>> bar.x
1
仍然很愚蠢,但不是那么解决
>>> class Foo(object):
def __init__(self):
pass
>>> import copy
>>> bar = Foo()
>>> bar.x = 1
>>> bar.x
1
>>> bar2 = copy.copy(bar)
>>> bar2.x
1
>>> bar2.x = 5
>>> bar2.x
5
>>> bar
<__main__.Foo object at 0x0426A870>
>>> bar.x
1
经常发现需要临时赋值一些成员变量,例如
old_x = c.x
old_y = c.y
# keep c.z unchanged
c.x = new_x
c.y = new_y
do_something(c)
c.x = old_x
c.y = old_y
但我希望我可以简单地写
with c.x = new_x; c.y = new_y:
do_something(c)
甚至
do_something(c with x = new_x; y = new_y)
Python 的装饰器或其他语言功能可以启用这种模式吗? (我可以根据需要修改 c
的 class)
mock
提供此功能,具体查看 patch.object
. It's in core libraries in python3, and available on pypi 的上下文管理器用法,用于较早的 python.
设置:
>>> class C:
... def __init__(self, x, y, z):
... self.x = x
... self.y = y
... self.z = z
...
>>> c = C(0,1,2)
使用演示:
>>> print(c.x, c.y, c.z)
0 1 2
>>> with patch.object(c, 'x', 'spam'), patch.object(c, 'y', 'eggs'):
... print(c.x, c.y, c.z)
...
spam eggs 2
>>> print(c.x, c.y, c.z)
0 1 2
Context managers 可以很容易地用于它。
引用官方文档:
Typical uses of context managers include saving and restoring various kinds of global state, locking and unlocking resources, closing opened files, etc.
看起来保存和恢复状态正是我们想要做的。
示例:
from contextlib import contextmanager
@contextmanager
def temporary_change_attributes(something, **kwargs):
previous_values = {k: getattr(something, k) for k in kwargs}
for k, v in kwargs.items():
setattr(something, k, v)
try:
yield
finally:
for k, v in previous_values.items():
setattr(something, k, v)
class Something(object):
def __init__(self, x, y):
self.x = x
self.y = y
def say_hello(self):
print("hello", self.x, self.y)
s = Something(1, 2)
s.say_hello() # hello 1 2
with temporary_change_attributes(s, x=4, y=5):
s.say_hello() # hello 4 5
s.say_hello() # hello 1 2
我认为 contextmanager
应该做你想做的事:
from contextlib import contextmanager
@contextmanager
def current_instance(c, temp_x, temp_y):
old_x, old_y = c.x, c.y
c.x, c.y = temp_x, temp_y
yield c
c.x, c.y = old_x, old_y
with current_instance(c, x, y) as c_temp:
do_something(c_temp)
您也可以使用 __enter__
和 __exit__
本机执行此操作。简单示例:
class SomeObject(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
class Temporary(object):
def __init__(self, target, **kv):
self.target = target
self.to_set = kv
self.to_restore = {}
def __enter__(self):
self.to_restore = map(partial(getattr, self.target), filter(partial(hasattr, self.target), self.to_set.keys()))
for k,v in self.to_set.items():
if hasattr(self.target, k):
self.to_restore[k] = getattr(self.target, k)
setattr(self.target, k, v)
def __exit__(self, *_):
for k,v in self.to_restore.items():
setattr(self.target, k, v)
for k in self.to_set.keys():
if k not in self.to_restore:
delattr(self.target, k)
o = SomeObject()
print(o.__dict__)
with Temporary(o, a=42, d=1337):
print(o.__dict__)
print(o.__dict__)
愚蠢的解决方案
>>> class Foo(object):
def __init__(self):
self._x = []
self._y = []
@property
def x(self):
return self._x[-1] or None
@x.setter
def x(self, val):
self._x.append(val)
def reset_vals(self):
if len(self._x) > 1:
self._x.pop()
>>> bar = Foo()
>>> bar.x = 1
>>> bar.x
1
>>> bar.x = 2
>>> bar.x
2
>>> bar.reset_vals()
>>> bar.x
1
>>> bar.reset_vals()
>>> bar.x
1
仍然很愚蠢,但不是那么解决
>>> class Foo(object):
def __init__(self):
pass
>>> import copy
>>> bar = Foo()
>>> bar.x = 1
>>> bar.x
1
>>> bar2 = copy.copy(bar)
>>> bar2.x
1
>>> bar2.x = 5
>>> bar2.x
5
>>> bar
<__main__.Foo object at 0x0426A870>
>>> bar.x
1