たとえば、スロットを使用してインスタンスを返すサードパーティライブラリによって返されたオブジェクトインスタンスに新しい属性を割り当てたいとします。第三者のライブラリの__slots__ベースのオブジェクトインスタンスを属性割り当てに「開く」方法はありますか?
(たとえば、あなたが戻って、データベースへの書き込みをするつもりはないデータのためのSQLAlchemyののRowProxy行にいくつかの値を設定する。)
インスタンスは__slots__
を持っているので、私ははAttributeErrorなどを取得するつもりです。それは予想されますが、回避策は何ですか?
望ましい行動/制約
- は、サードパーティのライブラリはブラックボックスで、その戻り値の型を変更することはできません考えてみましょう。
- 元のインスタンスの動作を可能な限り保持します(メソッド、プロパティなど)。
- スロット内の属性への割り当ては、元のインスタンスの属性に影響するはずです。
これは私が思いついたものですが、Proxy2は動作しますが、よりエレガントでシンプルなものがありますか?それが壊れるリスクは何ですか? Python 3で動作しますか(私は2.7ですが、最終的に3.xにアップグレードする予定です)。
class Slotted(object):
__slots__ = ("a","b")
def hello(self):
print("hello %s" % (self.a))
def __init__(self, a="1", b="2"):
self.a = a
self.b = b
def test_it(f):
fname = f.__name__
print("\n\ntest_it(%s)" % (fname))
instances = [Slotted(a=11), Slotted()]
try:
li_test = [f(obj) for obj in instances]
except Exception, e:
print("%s.failure at instance modification:%s" % (fname, str(e)[0:100]))
return
for cntr, tgt in enumerate(li_test):
try:
tgt.cntr = cntr
print("tgt.cntr:%s" % (tgt.cntr))
#do I still have my function?
tgt.hello()
tgt.a = 100
except Exception, e:
print("%s.failure:%s" % (fname, str(e)[0:100]))
return
#test that an attribute assignment to the slots actually went there...
for ori in instances:
try:
assert ori.a == 100
except AssertionError:
print("%s.failure:original instance should have its slot-based attribute set to 100, but is still %s" % (fname, ori.a))
break
print "%s.success" % (fname)
class Proxy2(object):
"""this works, can it be improved on?"""
def __init__(self, obj):
self.__dict__["_obj"] = obj
def __setattr__(self, attrname, value):
if attrname in self._obj.__slots__:
setattr(self._obj, attrname, value)
else:
self.__dict__[attrname] = value
def __getattr__(self, attrname):
try:
return getattr(self._obj, attrname)
except AttributeError:
raise
#subclass Slotted
class Opener(Slotted):
"""fails w Slotted' object layout differs from 'Opener'"""
pass
class Opener2(Slotted):
"""fails w Slotted' object layout differs from 'Opener2'"""
__slots__ = Slotted.__slots__ + ("__dict__",)
#functions to modify the original instances
def proxy_instances(obj):
#this works
return Proxy2(obj)
def do_nothing(obj):
#fails, normal, this is the baseline slots behavior
return obj
def modify_class(obj):
#change the instance's class to a subclass that has a __dict__
obj.__class__ = Opener
def modify_class2(obj):
#change the instance's class to a subclass to add __dict__ to the __slots__
obj.__class__ = Opener2
for func in [do_nothing, modify_class, modify_class2, proxy_instances]:
test_it(func)
出力:
test_it(do_nothing)
do_nothing.failure:'Slotted' object has no attribute 'cntr'
test_it(modify_class)
modify_class.failure at instance modification:__class__ assignment: 'Slotted' object layout differs from 'Opener'
test_it(modify_class2)
modify_class2.failure at instance modification:__class__ assignment: 'Slotted' object layout differs from 'Opener2'
test_it(proxy_instances)
tgt.cntr:0
hello 11
tgt.cntr:1
hello 1
proxy_instances.success
しかし、それはすでに私がプロキシでやっていることのようなものですね。あなたはそれが意味することをちょうどチェックして、皮肉っていません。私はちょうどこのタイプの問題でいつものアプローチが何かを理解しようとしています。 –
'Proxy'は興味深い考えです。 2つのコメント:(1)アトリビュートの取得/設定が 'Proxy'オブジェクトまたはその内部に含まれるオブジェクトに向かうかどうかはわかりません。 (2)サードパーティライブラリの実装の詳細である '__slots__'を使ってオブジェクトとしか動作していないかのように見えます。 –