2017-07-12 5 views
0

たとえば、スロットを使用してインスタンスを返すサードパーティライブラリによって返されたオブジェクトインスタンスに新しい属性を割り当てたいとします。第三者のライブラリの__slots__ベースのオブジェクトインスタンスを属性割り当てに「開く」方法はありますか?

(たとえば、あなたが戻って、データベースへの書き込みをするつもりはないデータのためのSQLAlchemyののRowProxy行にいくつかの値を設定する。)

インスタンスは__slots__を持っているので、私ははAttributeErrorなどを取得するつもりです。それは予想されますが、回避策は何ですか?

望ましい行動/制約

  1. は、サードパーティのライブラリはブラックボックスで、その戻り値の型を変更することはできません考えてみましょう。
  2. 元のインスタンスの動作を可能な限り保持します(メソッド、プロパティなど)。
  3. スロット内の属性への割り当ては、元のインスタンスの属性に影響するはずです。

これは私が思いついたものですが、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 

答えて

2

私は結果がサードパーティのライブラリーから返さ取り、他のデータと一緒にいくつかの並べ替え(例えばクラスや辞書)の私自身のコンテナオブジェクトに格納します私は保存したい。

このアプローチは、明示的でわかりやすく、わかりやすく、わかりやすく、難解です。サードパーティのデータを猿でパッチするのは難しいので、不思議なバグを避ける可能性が高いです。

+0

しかし、それはすでに私がプロキシでやっていることのようなものですね。あなたはそれが意味することをちょうどチェックして、皮肉っていません。私はちょうどこのタイプの問題でいつものアプローチが何かを理解しようとしています。 –

+0

'Proxy'は興味深い考えです。 2つのコメント:(1)アトリビュートの取得/設定が 'Proxy'オブジェクトまたはその内部に含まれるオブジェクトに向かうかどうかはわかりません。 (2)サードパーティライブラリの実装の詳細である '__slots__'を使ってオブジェクトとしか動作していないかのように見えます。 –

関連する問題