我试图覆盖__setattr__Python类的方法,因为每次实例属性更改其值时我都想调用另一个函数.但是,我不希望在该__init__方法中出现这种情况,因为在此初始化期间,我设置了一些稍后将使用的属性:
到目前为止,我有这个解决方案,而不是__setattr__在运行时覆盖:
class Foo(object):
def __init__(self, a, host):
object.__setattr__(self, 'a', a)
object.__setattr__(self, 'b', b)
result = self.process(a)
for key, value in result.items():
object.__setattr__(self, key, value)
def __setattr__(self, name, value):
print(self.b) # Call to a function using self.b
object.__setattr__(self, name, value)
Run Code Online (Sandbox Code Playgroud)
但是,我想避免这些object.__setattr__(...)并__setattr__在__init__方法结束时覆盖:
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
result = self.process(a)
for key, value in result.items():
setattr(self, key, value)
# override self.__setattr__ here
def aux(self, name, value):
print(self.b)
object.__setattr__(self, name, value)
Run Code Online (Sandbox Code Playgroud)
我曾尝试与self.__dict__['__setitem__'] = self.aux和object.__setitem__['__setitem__'] = self.aux,但这些attemps有效果.我已经阅读了数据模型参考的这一部分,但看起来自己的分配__setattr__有点棘手.
怎么可能__setattr__在最后覆盖__init__,或者至少有一个pythonic解决方案,__setattr__只在构造函数中以正常方式调用?
Sin*_*ion 25
不幸的是,没有办法"在init之后覆盖"python特殊方法; 作为查找工作方式的副作用.问题的症结在于python实际上并没有查看实例; 除了上课; 在它开始查找特殊方法之前; 所以没有办法让对象的状态影响查找哪个方法.
如果你不喜欢中的特殊行为__init__,你可以重构你的代码来__setattr__代替特殊知识.就像是:
class Foo(object):
__initialized = False
def __init__(self, a, b):
try:
self.a = a
self.b = b
# ...
finally:
self.__initialized = True
def __setattr__(self, attr, value):
if self.__initialzed:
print(self.b)
super(Foo, self).__setattr__(attr, value)
Run Code Online (Sandbox Code Playgroud)
编辑:实际上,有一种方法可以改变查找哪种特殊方法,只要在初始化之后更改它的类.这种方法会让你远远地进入元类的杂草,所以没有进一步的解释,这是看起来如何:
class AssignableSetattr(type):
def __new__(mcls, name, bases, attrs):
def __setattr__(self, attr, value):
object.__setattr__(self, attr, value)
init_attrs = dict(attrs)
init_attrs['__setattr__'] = __setattr__
init_cls = super(AssignableSetattr, mcls).__new__(mcls, name, bases, init_attrs)
real_cls = super(AssignableSetattr, mcls).__new__(mcls, name, (init_cls,), attrs)
init_cls.__real_cls = real_cls
return init_cls
def __call__(cls, *args, **kwargs):
self = super(AssignableSetattr, cls).__call__(*args, **kwargs)
print "Created", self
real_cls = cls.__real_cls
self.__class__ = real_cls
return self
class Foo(object):
__metaclass__ = AssignableSetattr
def __init__(self, a, b):
self.a = a
self.b = b
for key, value in process(a).items():
setattr(self, key, value)
def __setattr__(self, attr, value):
frob(self.b)
super(Foo, self).__setattr__(attr, value)
def process(a):
print "processing"
return {'c': 3 * a}
def frob(x):
print "frobbing", x
myfoo = Foo(1, 2)
myfoo.d = myfoo.c + 1
Run Code Online (Sandbox Code Playgroud)
@SingleNegationElimination的答案很好,但是它不能与继承一起使用,因为子类的__mro__存储是超类的原始类。受到他的回答的鼓舞,几乎没有改变,
这个想法很简单,切换__setattr__前__init__,后回恢复__init__完成。
class CleanSetAttrMeta(type):
def __call__(cls, *args, **kwargs):
real_setattr = cls.__setattr__
cls.__setattr__ = object.__setattr__
self = super(CleanSetAttrMeta, cls).__call__(*args, **kwargs)
cls.__setattr__ = real_setattr
return self
class Foo(object):
__metaclass__ = CleanSetAttrMeta
def __init__(self):
super(Foo, self).__init__()
self.a = 1
self.b = 2
def __setattr__(self, key, value):
print 'after __init__', self.b
super(Foo, self).__setattr__(key, value)
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.c = 3
>>> f = Foo()
>>> f.a = 10
after __init__ 2
>>>
>>> b = Bar()
>>> b.c = 30
after __init__ 2
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3948 次 |
| 最近记录: |