向Python字典键添加前缀的最有效方法
因此,我发现自己需要在Python字典中添加前缀 基本上,我希望这个字典的用户能够在实例化字典时添加前缀,在这种情况下,字典保留前缀,每次添加一个新键时,它都会在前缀前面加上前缀。但是,如果由于某种原因没有提供或更改前缀,我还希望对字典进行修改,这意味着旧字典键需要在保留各自值的同时将前缀预先添加到它们 用例: 基本上,我正在完成本系列的最后一个API。 我构建api的理念是每个调用都需要使用特定的参数, 比如: 不幸的是,最后两个API更难以这种方式实现,因为它们利用了Amazon引入的名为向Python字典键添加前缀的最有效方法,python,performance,dictionary,Python,Performance,Dictionary,因此,我发现自己需要在Python字典中添加前缀 基本上,我希望这个字典的用户能够在实例化字典时添加前缀,在这种情况下,字典保留前缀,每次添加一个新键时,它都会在前缀前面加上前缀。但是,如果由于某种原因没有提供或更改前缀,我还希望对字典进行修改,这意味着旧字典键需要在保留各自值的同时将前缀预先添加到它们 用例: 基本上,我正在完成本系列的最后一个API。 我构建api的理念是每个调用都需要使用特定的参数, 比如: 不幸的是,最后两个API更难以这种方式实现,因为它们利用了Amazon引入的名为数
数据类型的新“功能”
这些“数据类型
”是嵌套结构。
例如:
我想从InboundShipmentAPI
调用CreateInboundShipping
操作
该操作采用以下参数:
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\u amazon
方法执行以下操作:
ShipmentHeader.prefix = InboundShipmentHeader
您可以将dict
子类化并添加一个方法(我不确定该如何称呼它,所以让我们假设dict
):
现在,界面更具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'}
从外观上看,您在抽象类中需要的翻译比在字典键前面加前缀要复杂一些
我可能会将转换逻辑封装在一个基类中,并为每种类型创建子类,如下所示
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'}
…假设这是Amazon所期望的格式。在添加新前缀之前,您不也需要删除旧前缀吗?在前缀的setter中,我正在清除字典,并用新值替换_prefix的值。但是,您的字典键将同时包含旧前缀和新前缀。刚刚用完好奇,你为什么要这么做?@interjay我明白你的意思:(,我想现在可以忽略这一点。
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'}