2013-05-26 87 views
3

所以我發現自己需要爲Python字典添加前綴。向Python字典鍵添加前綴的最有效方法

基本上我希望這本詞典的用戶能夠在詞典的實例化中添加一個前綴,在這種情況下,詞典包含前綴,並且每次添加一個新的鍵時,都會前綴前綴。但是,如果由於某種原因未提供或更改前綴,我還想要對詞典進行變異,這意味着舊字典鍵需要在保留各自的值的同時將前綴添加到前綴中。

使用案例:

基本上我整理了MWS API最後的API。 我建周圍的想法API,每一個需要調用採取具體參數, ,如:

def get_report(self, marketplaceids): 
    # Here I process marketplaceids which is a python list 
    # and send the following to Amazon: 

    MarketplaceIdList.Id.1: 123, 
    MarketplaceIdList.Id.2: 345, 
    MarketplaceIdList.Id.3: 4343 

    # By doing this I eliminate the complexity of the arguments Amazon expects 

遺憾的是,最後兩個API是困難,因爲他們利用一個新的「功能」亞馬遜推出來實現這種方式稱爲Datatypes

這些「Datatypes」是嵌套結構。 例如:

我想打電話從InboundShipmentAPICreateInboundShipment行動,

的操作採用下列參數:

ShipmentId - String 
InboundShipmentHeader - InboundShipmentHeader datatype 
InboundShipmentItems - A list of InboundShipmentItem datatypes 

問題是因爲InboundShipmentHeader是需要其它數據類型的數據類型論據。 到底亞馬遜預計如下:

ShipmentId=102038383 
InboundShipmentHeader.ShipmentName': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.Name': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.AddressLine1': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.City': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.StateOrProvinceCode': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.PostalCode': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.CountryCode': 'somevalue', 
InboundShipmentHeader.DestinationFulfillmentCenterId': 'somevalue', 
InboundShipmentHeader.ShipmentStatus': 'somevalue', 
InboundShipmentHeader.LabelPrepPreference': 'somevalue', 
InboundShipmentItems.member.1.QuantityShipped': 'somevalue', 
InboundShipmentItems.member.2.QuantityShipped': 'somevalue', 
InboundShipmentItems.member.1.SellerSKU': 'somevalue', 
InboundShipmentItems.member.2.SellerSKU': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.AddressLine2': 'somevalue', 
InboundShipmentHeader.ShipFromAddress.DistrictOrCounty': 'somevalue', 

,所以我希望把它簡單的人,使這個呼叫而不需要擔心每個參數的名稱。 我的解決方案是創建一個基本數據類型類,然後創建單獨的數據類型 作爲類。

這是我到目前爲止有:

class AmazonDataType(dict): 
    """ 
    Base for all Amazon datatypes. 
    """ 

    def __init__(self, *args, **kwargs): 
     self._prefix = kwargs.pop('prefix', '') 
     self.update(*args, **kwargs) 

    @property 
    def prefix(self): 
     return self._prefix 

    @prefix.setter 
    def prefix(self, value): 
     self._prefix = value 
     newdict = {'%s.%s' % (value, key): dictvalue for key, dictvalue in self.iteritems()} 
     self.clear() 
     dict.update(self, newdict) 

    def __setitem__(self, key, value): 
     try: 
      original_key = self.fields[key] 
     except KeyError, e: 
      raise e 
     if isinstance(value, AmazonDataType): 
      value.prefix = original_key 
      dict.update(self, value) 
     else: 
      newkey = self.prefix + original_key if self.prefix else original_key 
      dict.__setitem__(self, newkey, value) 

    def update(self, *args, **kwargs): 
     """ 
     Props to Matt Anderson (http://stackoverflow.com/a/2390997/389453) 
     """ 
     for k, v in dict(*args, **kwargs).iteritems(): 
      self[k] = v 


class InboundShipmentHeader(AmazonDataType): 
    fields = { 
     'name': 'ShipmentName', 
     'address': 'ShipFromAddress', 
     'fulfillment_center_id': 'DestinationFulfillmentCenterId', 
     'label_preference': 'LabelPrepPreference', 
     'cases_required': 'AreCasesRequired', 
     'shipment_status': 'ShipmentStatus', 
    } 

然後,而不是做

somedict = { 
    'InboundShipmentHeader.ShipmentName': 'somevalue', 
    'InboundShipmentHeader.ShipFromAddress.Name': 'somevalue', 
    'InboundShipmentHeader.ShipFromAddress.AddressLine1': 'somevalue', 
    'InboundShipmentHeader.ShipFromAddress.City': 'somevalue', 
    'InboundShipmentHeader.ShipFromAddress.StateOrProvinceCode': 'somevalue', 
    'InboundShipmentHeader.ShipFromAddress.PostalCode': 'somevalue', 
    'InboundShipmentHeader.ShipFromAddress.CountryCode': 'somevalue', 
    'InboundShipmentHeader.DestinationFulfillmentCenterId': 'somevalue', 
    'InboundShipmentHeader.ShipmentStatus': 'somevalue', 
    'InboundShipmentHeader.LabelPrepPreference': 'somevalue', 
} 

call_amazon(somedict) 

的我想通過類似

ShipmentHeader = InboundShipmentHeader() 
ShipmentHeader['name'] = 'somevalue' 
ShipmentHeader['address'] = address_datatype_instance 
ShipmentHeader['fulfillment_center_id'] = 'somevalue' 
ShipmentHeader['label_preference'] = 'somevalue' 
ShipmentHeader['cases_required'] = 'somevalue' 
ShipmentHeader['shipment_status'] = 'somevalue' 

call_amazon(ShipmentHeader, otherparams) 

在此背景下,call_amazon方法確實:

ShipmentHeader.prefix = InboundShipmentHeader 
+0

您是否還需要在添加新前綴之前刪除舊的前綴? – interjay

+0

在前綴的設置器中,我正在清除字典並用新值替換_prefix的值。 – Paulo

+0

但是,然後你的字典鍵會有新的前綴和前綴。 – interjay

回答

4

你可以繼承dict並添加一個方法(我不知道該怎麼稱呼它,讓我們說dict):

class AmazonDataType(dict): 
    """ 
    Base for all Amazon datatypes. 
    """ 

    def __init__(self, *args, **kwargs): 
     self._prefix = kwargs.pop('prefix', self.__class__.__name__) 

     super(AmazonDataType, self).__init__(*args, **kwargs) 

    def __getattr__(self, key): 
     return self.__getitem__(key) 

    def __setattr__(self, key, value): 
     return self.__setitem__(key, value) 

    def dict(self): 
     result = {} 

     for key, value in self.items(): 
      if key.startswith('_'): 
       continue 

      key = self.fields.get(key, key) 

      if isinstance(value, AmazonDataType): 
       for skey, svalue in value.dict().items(): 
        result['%s.%s' % (self._prefix, skey)] = svalue 
      else: 
       result['%s.%s' % (self._prefix, key)] = value 

     return result 

現在,界面有點更Python:

class InboundShipmentHeader(AmazonDataType): 
    fields = { 
     'name': 'ShipmentName', 
     'address': 'ShipFromAddress', 
     'fulfillment_center_id': 'DestinationFulfillmentCenterId', 
     'label_preference': 'LabelPrepPreference', 
     'cases_required': 'AreCasesRequired', 
     'shipment_status': 'ShipmentStatus', 
    } 

class Address(AmazonDataType): 
    fields = { 
     'name': 'Name', 
     'address': 'AddressLine1', 
     'city': 'City' 
    } 

address = Address(prefix='ShipFromAddress') 
address.name = 'Foo' 

header = InboundShipmentHeader() 
header.name = 'somevalue' 
header.address = address 
header.fulfillment_center_id = 'somevalue' 
header.label_preference = 'somevalue' 
header.cases_required = 'somevalue' 
header.shipment_status = 'somevalue' 

header.dict()的輸出是:

{'InboundShipmentHeader.AreCasesRequired': 'somevalue', 
'InboundShipmentHeader.DestinationFulfillmentCenterId': 'somevalue', 
'InboundShipmentHeader.LabelPrepPreference': 'somevalue', 
'InboundShipmentHeader.ShipFromAddress.Name': 'Foo', 
'InboundShipmentHeader.ShipmentName': 'somevalue', 
'InboundShipmentHeader.ShipmentStatus': 'somevalue'} 
2

通過它的外觀,您在抽象類中需要的翻譯稍微複雜一些,只是爲字典鍵加前綴。

我可能會封裝在一個基類的轉換邏輯,併爲每種類型創建的子類,像這樣的東西......

class AmazonDict(dict): 
    translation_dict = {} 

    def __init__(self, prefix): 
     self.prefix = prefix 

    def translate(self): 
     result = {} 
     for k, v in self.iteritems(): 
      if k not in self.translation_dict: 
       continue 
      if isinstance(v, AmazonDict): 
       for sk, sv in v.translate().iteritems(): 
        sk = '%s.%s' % (self.prefix, sk) 
        result[sk] = sv 
      else: 
       k = '%s.%s' % (self.prefix, self.translation_dict[k]) 
       result[k] = v 
     return result 


class ShipmentAddress(AmazonDict): 
    translation_dict = {'name': 'Name', 
         'line1': 'AddressLine1'} 


class ShipmentHeader(AmazonDict): 
    translation_dict = {'name': 'ShipmentName', 
         'address': 'ShipFromAddress'} 


address = ShipmentAddress('ShipFromAddress') 
address['name'] = 'Fred Bloggs' 
address['line1'] = '123 High Street' 

header = ShipmentHeader('InboundShipmentHeader') 
header['name'] = 'Something' 
header['address'] = address 

pprint.pprint(header.translate()) 

...也可以用來處理遞歸兒童「對象',並輸出...

{'InboundShipmentHeader.ShipFromAddress.AddressLine1': '123 High Street', 
'InboundShipmentHeader.ShipFromAddress.Name': 'Fred Bloggs', 
'InboundShipmentHeader.ShipmentName': 'Something'} 

...假設這是亞馬遜預計的格式。

相關問題