简体   繁体   English

为Python字典键添加前缀的最有效方法

[英]Most efficient way to add prefix to Python dictionary keys

So I find myself needing to add a prefix to a Python dictionary. 所以我发现自己需要在Python字典中添加前缀。

Basically what I want is for the user of this dictionary to be able to add a prefix on instantiation of the dictionary, in which case the dictionary holds the prefix and everytime a new key is added, it prepends the prefix. 基本上我想要的是这个字典的用户能够在字典的实例化中添加前缀,在这种情况下字典保存前缀,并且每次添加新密钥时,它都会预先添加前缀。 But I also want mutate the dictionary if for some reason the prefix is not provided or changed, this means that the old dictionary keys need to have the prefix prepended to them while keeping their respective values. 但是如果由于某种原因没有提供或改变前缀,我也想要改变字典,这意味着旧的字典键需要在保留各自值的同时为它们添加前缀。

Use case: 使用案例:

Basically I'm finishing up the last apis of the MWS API . 基本上我正在完成MWS API的最后一次使用 I built the api around the idea that every call needed to take specific parameters, like : 我建立了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

Unfortunately the last two apis are harder to implement this way because they make use of a new "feature" Amazon introduced called Datatypes . 不幸的是,最后两个api更难以这种方式实现,因为它们利用亚马逊引入的一个名为Datatypes的新“特性”。

These " Datatypes " are nested structures. 这些“ Datatypes ”是嵌套结构。 For example: 例如:

I want to call the CreateInboundShipment action from the InboundShipmentAPI , 我想从InboundShipmentAPI调用CreateInboundShipment操作,

The action takes the following arguments: 该操作采用以下参数:

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

the problem happens because InboundShipmentHeader is a datatype that takes another datatype as argument. 问题发生的原因是InboundShipmentHeader是一种数据类型,它采用另一种数据类型作为参数。 In the end Amazon expects the following: 亚马逊最终期望如下:

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',

so I want to make it simple for someone to make this call without having to worry about the names of each argument. 所以我想简单地让某人做这个电话而不必担心每个参数的名字。 My solution is to create a base datatype class and then create the separate datatypes as classes. 我的解决方案是创建一个基本数据类型类,然后创建单独的数据类型作为类。

This is what I have so far: 这是我到目前为止:

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',
    }

then instead of doing 然后而不是做

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)

I want to pass something like 我想通过类似的东西

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)

In the background, the call_amazon method does: 在后台, call_amazon方法执行:

ShipmentHeader.prefix = InboundShipmentHeader

You could subclass dict and add a method (I'm not sure what to call it, so let's say dict ): 你可以继承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

Now, the interface is a little more Pythonic: 现在,界面更加Pythonic:

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'

The output of header.dict() is: header.dict()的输出是:

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

By the looks of it, the translation you require in your abstraction class is a little more complicated that just prefixing dictionary keys. 从它的外观来看,你在抽象类中需要的翻译稍微复杂一点,就是为字典键添加前缀。

I'd probably encapsulate the translation logic in a base class, and create subclasses for each type, with something like this... 我可能会将翻译逻辑封装在基类中,并为每种类型创建子类,使用类似的东西......

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())

...which also handles the recursion for child 'objects', and outputs... ...还处理子'对象'的递归,并输出......

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

...assuming that's the format which Amazon is expecting. ...假设这是亚马逊期待的格式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在迭代过程中,在Python中添加新密钥或附加到字典中的旧密钥的最有效方法是什么? - Most efficient way to add new keys or append to old keys in a dictionary during iteration in Python? 从python字典中删除某些前缀的键的有效方法 - Efficient way of removing keys of certain prefix from python dictionary 将具有空值的键添加到字典的最有效方法是什么? - What is the most efficient way to add keys with empty values to dictionary? 用字典填充字典键的最有效方法 - Most efficient way to populate dictionary keys with dictionary 从python的Counter()多集字典中删除唯一键的最有效方法 - Most efficient way to remove unique keys from python's Counter() multiset dictionary 从 python 的字典中获取键列表/键集的最有效方法是什么? - What is the most efficient way to get a list/set of keys from dictionary in python? 将带有键和值的字符串转换为python dict的最有效方法 - The most efficient way to convert a string with keys and values into a python dict Python-从(扩展)字典中选择第k个值的最有效方法 - Python - Most efficient way to select kth value from (expanded) dictionary 在Python中加载字典的最有效方法是什么? - What's the most efficient way to load a dictionary in Python? 用Python将字典写入csv的最有效方法是什么? - What is the most efficient way of writing a dictionary into a csv in Python?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM