3

jsonデータをモデル化するクラスの1つに、同様のフィールドがいくつかあります。すべてのフィールドはNoneに初期化され、静的ツールが存在することを認識します。その後、ヘルパー関数は、モデリングしているjsonデータに基づいてフィールドを初期化するのに役立ちます(知りたい場合はSecondHandSongs API )。

一部のデータは、フェッチする必要のある余分なデータのURIのみを取得します。したがって、隠れた変数をNoneに初期化し、最初のリクエストでデータをフェッチ/デコードするという古いトリックを使用したいと思います。しかし、setattr(self.__class__)醜いように見えます。

(Pythonでプロパティを動的に設定する)より良い方法はありますか?

def  _initialize_url_fields(self, attrNamesToFactoryFunction, json_data):
    for (name, factoryFunction) in attrNamesToFactoryFunction.iteritems():
        try:
            url = json_data[name]
        except KeyError:
            continue
        setattr(self, name + "_url", url)
        setattr(self, "_" + name, None)    
        setattr(self.__class__, name, property(lambda s: s._getter("_" + name, url, factoryFunction)))        

def _getter(self, hidden_prop_name, url, factoryFunction):
    if not getattr(self, hidden_prop_name):
        json_data = SHSDataAcess.getSHSData(url)
        setattr(self, hidden_prop_name, factoryFunction(json_data))
    return getattr(self, hidden_prop_name)

編集: initから呼び出されたインスタンスメソッドにプロパティを設定しようとしていることに気づきました 。予想通り、2回目は失敗しました。

編集2:

オブジェクトごとにプロパティを設定していることに気付いた後、これを修正した方法は次のとおりです(シングルトンクラスでない場合は不可能です)

class ShsData(object):
    def  _initialize_url_fields(self, attrNamesToFactoryFunctions, json_data):
        for (name, factoryFunction) in attrNamesToFactoryFunctions.items():
            self._getter_factory_functions[name] = factoryFunction 
            uri = None
            try:
                uri = json_data[name]
            except KeyError:
                pass
            setattr(self, name + "_uri", uri)
            setattr(self, "_" + name, None)

def _fetch_shs_data_on_first_access_getter(base_prop_name):
    def getter(self):
        factoryFunction = self._getter_factory_functions[base_prop_name]
        hidden_prop_name = "_" + base_prop_name 
        uri_prop_name = base_prop_name + "_uri"
        if not getattr(self, hidden_prop_name):
            if getattr(self, uri_prop_name):
                json_data = SHSDataAcess.getSHSData(getattr(self, uri_prop_name))
                setattr(self, hidden_prop_name, factoryFunction(json_data))
            else:
                return None
        return getattr(self, hidden_prop_name)
    return getter

class ShsArtist(ShsData):

    performances_data = property(_fetch_shs_data_on_first_access_getter("performances"))
    creditedWorks_data = property(_fetch_shs_data_on_first_access_getter("creditedWorks"))
    releases_data = property(_fetch_shs_data_on_first_access_getter("releases"))

    def __init__(self, json_data):
        ...
        self._initialize_url_fields({"performances": lambda xs: [ShsPerformance(x) for x in xs],
                                     "creditedWorks": lambda xs: [ShsWork(x) for x in xs],
                                     "releases": lambda xs: [ShsRelease(x) for x in xs]},
                                    json_data)
4

1 に答える 1

0

一般的なケースを処理するために、プロパティをサブクラス化する場合があります。このようなもの:

class shs_klass_property(property):

    def __init__(self, name, klass):
        self.name = name
        self.klass = klass
        self.cached_name = '_%s' % name
        super(shs_klass_property, self).__init__(self.compute)

    def compute(self, obj):
        if not hasattr(obj, self.cached_name):
            if self.name in obj._json_data:
                # if needed handle cases where this isn't a list
                val = [self.klass(x) for x in obj._json_data[self.name]]
            else:
                val = None
            setattr(obj, self.cached_name, val)
        return getattr(obj, self.cached_name)

class ShsData(object):
    def __init__(self, json_data):
        self._json_data = json_data

class ShsWork(ShsData):
    pass

class ShsArtist(ShsData):
    works = shs_klass_property('works', ShsWork)

常に uri も設​​定したい場合は、次のようにすることができます。

# if you change this to pass in "_json_data" too,
# you'd have a simple general purpose delegation decorator
class shs_json_property(property):

    def __init__(self, name):
        self.name = name
        super(shs_json_property, self).__init__(self.compute)

    def compute(self, obj):
        return obj._json_data.get(self.name, None)

# a helper to set both. not necessary but saves a line of code.
def shs_property_pair(name, klass):
    return (shs_klass_property(name, klass),
            shs_json_property(name))

class ShsArtist(ShsData):
    works, works_uri = shs_property_pair('works', ShsWork)
于 2012-09-06T03:38:30.567 に答える