使用标志值在Python中实现高效的双向映射

使用标志值在Python中实现高效的双向映射,python,Python,在Python中,对象双向映射(每对对象都有标志值)的最佳数据结构是什么?例如,让我们假设我有两组我想匹配的男性和女性。我需要一个数据结构来存储de matches,这样我就可以访问每个女人对应的男人,每个男人对应的女人,比如说,一个代表这对夫妇价值的数字 关键特性是,我希望以恒定的时间(大约是字典中键访问的时间)访问所有这些数据,而不会为构造浪费资源 如果不是因为“标志值”的特殊性,在中建议的图书馆中提供的a将是绝对完美的。事实上,每次我在我的全明星夫妇数据结构中添加一对夫妇时,它都会自动更新

在Python中,对象双向映射(每对对象都有标志值)的最佳数据结构是什么?例如,让我们假设我有两组我想匹配的男性和女性。我需要一个数据结构来存储de matches,这样我就可以访问每个女人对应的男人,每个男人对应的女人,比如说,一个代表这对夫妇价值的数字

关键特性是,我希望以恒定的时间(大约是字典中键访问的时间)访问所有这些数据,而不会为构造浪费资源

如果不是因为“标志值”的特殊性,在中建议的图书馆中提供的a将是绝对完美的。事实上,每次我在我的全明星夫妇数据结构中添加一对夫妇时,它都会自动更新以避免一夫多妻制:

couples = bidict({ 
    'leonard' : 'penny',
    'howard'  : 'bernadette',
    'sheldon' : 'amy'
})
couples.forceput('stephen', 'amy')
print couples

>> bidict({'stephen': 'amy', 'leonard': 'penny', 'howard': 'bernadette'})
我现在正在征求关于实现
质量
功能的最有效和最具吸引力的方法的建议,例如:

quality('stephen', 'amy')

>> 0.4

couples.forceput('sheldon', 'amy', quality = 1.0)
quality('sheldon', 'amy')

>> 1.0

quality('stephen', 'amy')

>> Raise KeyError

由于您使用的键是可散列的,因此直接的解决方案是添加一个使用of
{man,woman}
作为键并且具有
质量
作为值的词汇表

然而,您已经到了一个临界点,指定您的所有需求并将其适合于适当的对象体系结构开始起作用。这里有一个图形体系结构,即数据与节点(人名和性别)和链接(关系和质量)相关联。我可能会借用或实现一个图形结构来表示这一点,根据速度/内存的考虑选择最佳的结构,该结构将使未来的扩展变得简单

考虑到每个节点最多只有一个链接,并且您希望对人员及其合作伙伴进行O(1)访问,您可以通过以下方式实现图形进行优化:

class People ():
    def __init__(self, name, sex):
        self.name = name
        self.sex = sex
        self.relationship = None

    def force_link(self, partner, quality = None):
        #You can implement a sex check here for example
        self.relationship = Relationship (quality)
        partner.relationship = self.relationship

class Relationship ():
    def __init__(self, quality):
        #May grow over time
        self.quality = quality

class Graph ():
    def __init__(self):
        # Indexing by name
        self.nodes = { name : People(name, sex) for name, sex in zip(namelist,sexlist) }
        # Linking example
        self.nodes['brandon'].force_link(self.nodes['amy'],0.2)
        # Get quality example
        print (self.nodes['amy'].relationship.quality)

考虑元组是可散列的。您可以创建一个
dict
,将一对数据映射到您想要的任何数据,包括质量:

quality = dict()
quality[ ('stephen', 'amy') ] = 0.4
解决方案1:快速和肮脏 以下是在
bidict
基础上构建的一个简单实现,它是根据和提出的建议完成的:

通过这种方式,可以获得以下行为:

bbt = couple()
bbt.add( 'stephen', 'amy', 0.4 )
bbt.flag( 'stephen', 'amy' )

>> 0.4

bbt.add( 'sheldon', 'amy', 1.0 )
bbt.flag( 'sheldon', 'amy' )

>> 1.0

bbt.flag( 'stephen', 'amy' )

>> KeyError: ('stephen', 'amy')
解决方案2:特定和独立 因为我最终编写了自己的结构。它是独立的,如果经过这里的任何人需要:

class FlaggedDoubleMapper :
    """Flagged Double Mapper"""

    def __init__ ( self, keys = [], vals = [], flags = [] ) :
        """Initializes a flagged double mapper with lists of keys, values and
        flags.
        """

        self._flg = {}     # Flags dictionary
        self._fwd = {}     # Forward dictionary
        self._bwd = {}     # Backward dictionary

        for key, val, flag in zip( keys, vals, flags ) :
            self.add( key, val, flag )

    def __repr__ ( self ) :
        """Representation bidict-style."""

        return 'fdm({})'.format( self._fwd )

    def contains ( self, key, val ) :
        """Returns True if and only if self contains the key-val binding."""

        try :
            return ( self._fwd[ key ] == val )
        except KeyError :
            return False

    def add ( self, key, val, flag ) :
        """Adds a flagged binding, overwriting all corresponding bindings."""

        try :
            _val = self._fwd[ key ]
            del self._bwd[ _val ]
        except KeyError :
            pass

        try :
            _key = self._bwd[ val ]
            del self._flg[ _key ]
            del self._fwd[ _key ]
        except KeyError :
            pass

        self._flg[ key ] = flag
        self._fwd[ key ] = val
        self._bwd[ val ] = key

    def remove ( self, key, *args ) :
        """Removes a binding.
         - remove( key ) will send a KeyError( key ) if no binding with key as a
         forward key is found.
         - remove( key, val ) will send a KeyError( key, val ) if no forward
         key-val binding is found.
        """

        try :
            _val = args[0]
            if ( _val != self._fwd[ key ] ) :   # Can raise a KeyError( key )
                raise KeyError( key, _val )
        except IndexError :
            _val = self._fwd[ key ]             # Can raise a KeyError( key )

        del self._flg[ key ]
        del self._fwd[ key ]
        del self._bwd[ _val ]

    def flag ( self, key, *args ) :
        """Returns the flag of a binding.
         - flag( key ) will send a KeyError( key ) if no binding with key as a
         forward key is found.
         - flag( key, val ) will send a KeyError( key, val ) if no forward
         key-val binding is found.
        """

        try :
            _val = args[0]
            if ( _val != self._fwd[ key ] ) :   # Can raise a KeyError( key )
                raise KeyError( key, _val )
        except IndexError :
            pass

        return self._flg[ key ]

你可以使用嵌套列表。我会向
bidict
提交一个功能请求,或者在他们的代码分支中实现它,然后发送一个pull请求。当然,但是这个
质量
实现将是一个真正的痛苦更新(或者将存储很多无用的信息:前几对)。你必须决定这是一个多大的问题。如果这确实是一个问题,请将
bidict
封装到一个类中,该类在执行插入操作时保持
质量。我只需要确保改变每一个相关的操作,或者只是拦截它们。编写一个实现
\uuuu setitem\uuuu
的类并捕获密钥。(我猜这就是
bidict
所做的,但可能在C中?)然后您可以放弃质量值(如果有)。请注意,您已经创建了
\u flg
\u fwd
\u bwd
类属性,因此,您将无法使用此类定义维护两个具有不同值的独立对象。@chthonicdaemon您能阐述您的观点吗?我没有得到它。尝试创建
o1=FlaggedDoubleMapper()
o2=FlaggedDoubleMapper()
,然后
o1。添加('a',b',1)
,现在
o2。flag('a')
将返回1。这是因为两个对象共享类属性。解决方案是在
\uuuuu init\uuuuu
中而不是在类定义中创建这些属性。
class FlaggedDoubleMapper :
    """Flagged Double Mapper"""

    def __init__ ( self, keys = [], vals = [], flags = [] ) :
        """Initializes a flagged double mapper with lists of keys, values and
        flags.
        """

        self._flg = {}     # Flags dictionary
        self._fwd = {}     # Forward dictionary
        self._bwd = {}     # Backward dictionary

        for key, val, flag in zip( keys, vals, flags ) :
            self.add( key, val, flag )

    def __repr__ ( self ) :
        """Representation bidict-style."""

        return 'fdm({})'.format( self._fwd )

    def contains ( self, key, val ) :
        """Returns True if and only if self contains the key-val binding."""

        try :
            return ( self._fwd[ key ] == val )
        except KeyError :
            return False

    def add ( self, key, val, flag ) :
        """Adds a flagged binding, overwriting all corresponding bindings."""

        try :
            _val = self._fwd[ key ]
            del self._bwd[ _val ]
        except KeyError :
            pass

        try :
            _key = self._bwd[ val ]
            del self._flg[ _key ]
            del self._fwd[ _key ]
        except KeyError :
            pass

        self._flg[ key ] = flag
        self._fwd[ key ] = val
        self._bwd[ val ] = key

    def remove ( self, key, *args ) :
        """Removes a binding.
         - remove( key ) will send a KeyError( key ) if no binding with key as a
         forward key is found.
         - remove( key, val ) will send a KeyError( key, val ) if no forward
         key-val binding is found.
        """

        try :
            _val = args[0]
            if ( _val != self._fwd[ key ] ) :   # Can raise a KeyError( key )
                raise KeyError( key, _val )
        except IndexError :
            _val = self._fwd[ key ]             # Can raise a KeyError( key )

        del self._flg[ key ]
        del self._fwd[ key ]
        del self._bwd[ _val ]

    def flag ( self, key, *args ) :
        """Returns the flag of a binding.
         - flag( key ) will send a KeyError( key ) if no binding with key as a
         forward key is found.
         - flag( key, val ) will send a KeyError( key, val ) if no forward
         key-val binding is found.
        """

        try :
            _val = args[0]
            if ( _val != self._fwd[ key ] ) :   # Can raise a KeyError( key )
                raise KeyError( key, _val )
        except IndexError :
            pass

        return self._flg[ key ]