我有一个类,我动态添加一些属性,在某些时候我想将类恢复到原始状态而没有添加属性。
情况:
class Foo(object):
pass
Foo.x = 1
# <insert python magic here>
o = Foo() # o should not have any of the previously added attributes
print o.x # Should raise exception
我最初的想法是创建原始类的副本:
class _Foo(object):
pass
Foo = _Foo
Foo.x = 1
Foo = _Foo # Clear added attributes
o = Foo()
print o.x # Should raise exception
但由于Foo只是对_Foo的引用,因此任何属性都会添加到原始的_Foo中。我也试过
Foo = copy.deepcopy(_Foo)
如果有帮助但显然没有。
澄清:
用户不需要关心如何实现类。因此,它应该具有“正常定义”类的相同特征,即内省,内置帮助,子类化等。这几乎排除了基于__getattr__
答案 0 :(得分:5)
我同意格伦的观点,这是一个非常糟糕的想法。无论如何,这里你是如何与装饰师一起做的。感谢Glenn的帖子以及提醒我你可以从类字典中删除项目,而不是直接删除。这是代码。
def resetable(cls):
cls._resetable_cache_ = cls.__dict__.copy()
return cls
def reset(cls):
cache = cls._resetable_cache_ # raises AttributeError on class without decorator
for key in [key for key in cls.__dict__ if key not in cache]:
delattr(cls, key)
for key, value in cache.items(): # reset the items to original values
try:
setattr(cls, key, value)
except AttributeError:
pass
我是否通过捕获尝试使用try
更新不可分配的属性(如我所示)或构建此类属性的列表来重置值。我会留给你的。
这是一个用途:
@resetable # use resetable on a class that you want to do this with
class Foo(object):
pass
Foo.x = 1
print Foo.x
reset(Foo)
o = Foo()
print o.x # raises AttributeError as expected
答案 1 :(得分:3)
您必须记录原始状态并明确还原。如果在更改之前存在该值,则恢复该值;否则删除你设置的值。
class Foo(object):
pass
try:
original_value = getattr(Foo, 'x')
originally_existed = True
except AttributeError:
originally_existed = False
Foo.x = 1
if originally_existed:
Foo.x = original_value
else:
del Foo.x
o = Foo() # o should not have any of the previously added attributes
print o.x # Should raise exception
你可能不想这样做。猴子补丁有一些有效的案例,但你通常不想尝试猴子 unpatch 。例如,如果两个独立的代码位修补相同的类,其中一个试图在不知道另一个的情况下反转动作可能会破坏事物。有关此实际有用的案例,请参阅https://stackoverflow.com/questions/3829742#3829849。
答案 2 :(得分:1)
您可以使用检查和维护原始成员列表,然后删除不在原始列表中的所有成员
import inspect
orig_members = []
for name, ref in inspect.getmembers(o):
orig_members.append(name)
...
现在,当您需要恢复原来的
时for name, ref in inspect.getmembers(o):
if name in orig_members:
pass
else:
#delete ref here
答案 3 :(得分:0)
我不完全明白为什么你需要这个,但我会去。普通继承可能不会这样做,因为你想“重置”到旧状态。 about a proxy pattern怎么样?
class FooProxy(object):
def __init__(self, f):
self.f = foo
self.magic = {}
def set_magic(self, k, v):
self.magic[k] = v
def get_magic(self, k):
return self.magic.get(k)
def __getattr__(self, k):
return getattr(self.f, k)
def __setattr__(self, k, v):
setattr(self.f, k, v)
f = Foo()
p = FooProxy(f)
p.set_magic('m_bla', 123)
将f用于普通的“原始”访问,使用p进行代理访问,它的行为应该与Foo类似。如果需要
,请使用新配置重新代理f答案 4 :(得分:0)
我不知道你是否可以为课程接受额外的模块文件,如果可以的话:
my_class.py
class Foo(object):
pass
您的主要脚本:
import my_class
Foo = my_class.Foo
Foo.x = 1
p = Foo()
print p.x # Printing '1'
# Some code....
reload(my_class) # reload to reset
Foo = my_class.Foo
o = Foo()
print p.x # Printing '1'
print o.__class__ == p.__class__ # Printing 'False'
print o.x # Raising exception
我不确定是否有任何副作用。它似乎做了OP想要的,虽然这真的很不寻常。
答案 5 :(得分:0)
在你的第二个例子中,你是在引用类而不是实例。
Foo = _Foo#Reference
如果您改为制作实例副本,那么您想要做的就是它的工作方式。您可以根据需要修改实例,并通过创建新实例来“恢复”它。
Foo = _Foo()
#!的/ usr / bin中/ Python的
class FooClass(object): 通
FooInstance = FooClass()#创建一个实例
FooInstance.x = 100#修改实例
print dir(FooClass)#Verify FooClass没有'x'属性
FooInstance = FooClass()#创建一个新实例
print FooInstance.x#Exception
答案 6 :(得分:0)
我不明白你要做什么,但请记住,你不必为类添加属性,以使看起来像你添加属性到类。
您可以为类提供一个__getattr__
方法,该方法将针对任何缺失的属性进行调用。它取得的价值取决于你:
class MyTrickyClass(object):
self.magic_prefix = "m_"
self.other_attribute_source = SomeOtherObject()
def __getattr__(self, name):
if name.startswith(self.magic_prefix):
stripped = name[len(self.magic_prefix):]
return getattr(self.other_attribute_source, stripped)
raise AttributeError
m = MyTrickyClass()
assert hasattr(m, "m_other")
MyTrickyClass.magic_prefix = "f_"
assert hasattr(m, "f_other")
答案 7 :(得分:0)
如果您添加的所有内容都以给定的独特前缀开头,则可以在对象的__dict__
中搜索具有该前缀的成员,并在需要恢复时删除它们。
答案 8 :(得分:0)
我找到的最简单的方法是:
def foo_maker():
class Foo(object):
pass
return Foo
Foo = foo_maker()
Foo.x = 1
Foo = foo_maker() # Foo is now clean again
o = Foo() # Does not have any of the previously added attributes
print o.x # Raises exception
编辑:正如评论中指出的那样,实际上并没有重置课程,但在实践中却有同样的效果。
答案 9 :(得分:-1)
要创建类的深层副本,您可以使用new.classobj
函数
class Foo:
pass
import new, copy
FooSaved = new.classobj(Foo.__name__, Foo.__bases__, copy.deepcopy(Foo.__dict__))
# ...play with original class Foo...
# revert changes
Foo = FooSaved
UPD:不推荐使用模块new
。相反,你应该使用types.ClassType
使用相同的args