使用标志值在Python中进行高效的双向映射

时间:2016-05-25 10:02:09

标签: python

在Python中,对象的双向映射以及每对的标志值有哪些最佳数据结构?例如,让我们想象一下,我想要两个男女混合在一起。我想要一个数据结构来存储de matches,这样我就可以访问每个女人的相应男人,每个女人的相应女人,比方说,这个数字代表了这对夫妇的价值。

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

如果没有“标志值”的特殊性,bidict中建议的库中的this post绝对是完美的。实际上,每当我在 all-stars-couples 数据结构中添加一对时,它会自动更新以避免一夫多妻制:

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

>> bidict({'stephen': 'amy', 'leonard': 'penny', 'howard': 'bernadette'})

我现在正在寻求有关实施quality功能的最有效和pythonic方式的建议,例如:

quality('stephen', 'amy')

>> 0.4

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

>> 1.0

quality('stephen', 'amy')

>> Raise KeyError

3 个答案:

答案 0 :(得分:1)

考虑元组是可以清除的。您可以创建一个dict,从一对夫妇到您想要的任何数据,包括质量:

quality = dict()
quality[ ('stephen', 'amy') ] = 0.4

答案 1 :(得分:1)

解决方案1:快速而肮脏

以下是基于bidictadrin提出的建议在class couple ( bidict ) : self._flags = {} def add ( self, key, val, flag ) : try : del self._flags[ self._inv[ val ] ] except KeyError : pass self._put(key, val, overwrite_key=True, overwrite_val=True) self._flags[ key ] = flag def flag ( self, key, val ) : if ( self._fwd.get( key ) == val ) : return self._flags[ key ] else : raise KeyError( key, val ) 之上构建的简单实现:

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

这样,可以获得以下行为:

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 ]

解决方案2:具体和独立

因为我最终编码了我自己的结构。它是独立的,如果有人通过这里需要Austin

import android.Manifest;
import android.app.Activity;

import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

/**
 * This class uses GoogleApiClient to track location changes.
 * In order to connect the GoogleApiClient correctly, this class must be instanciated in onCreate callback.
 */
public class Locator implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    private static final String TAG = "Locator";
    private Activity activity;
    private GoogleApiClient googleApiClient;
    private LocationRequest locationRequest;
    private static final long UPDATE_INTERVAL = 60 * 1000;
    private static final long FASTEST_UPDATE_INTERVAL = 10 * 1000;
    private static final int LOCATION_REQUEST = 1;
    private static boolean locationPermissionRequested = false;
    private static boolean locationPermissionGranted = false;
    private Location location;

    /**
     * Default constructor. Ex. Locator locator = new Locator(getActivity());
     *
     * @param activity The activity to instanciate from.
     */
    public Locator(Activity activity) {
        this.activity = activity;
        if (activity != null) {
            googleApiClient = new GoogleApiClient.Builder(activity)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).build();
            locationRequest = new LocationRequest();
            locationRequest.setInterval(UPDATE_INTERVAL);
            locationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL);
            locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
            googleApiClient.connect();
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        requestLocation();
    }

    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onLocationChanged(Location location) {
        this.location = location;
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    }

    /**
     * Requests the current location to the GoogleApiClient.
     * NB: If device os is Marshmallow or higher, il will also ask permission to acces location services.
     */
    private void requestLocation() {
        if (googleApiClient.isConnected()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                locationPermissionGranted = ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
                if (!locationPermissionGranted) {
                    if (!locationPermissionRequested) {
                        ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_REQUEST);
                        locationPermissionRequested = true;
                        requestLocation();
                    }
                }
            } else {
                locationPermissionGranted = true;
            }
            if (locationPermissionGranted) {
                LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
            } else {
            }
        }
    }

    /**
     * Requests the current location et gets.
     *
     * @return The location.
     */
    public Location getLocation() {
        requestLocation();
        return location;
    }

    /**
     * Stops updating the location. (save battery power).
     */
    public void removeUpdates() {
        LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
    }
}

答案 2 :(得分:0)

由于您使用可清除的密钥,因此立即解决方案是添加使用frozenset {man, woman}作为密钥并且quality为值的字典。

然而,你达到了这样的程度,即指明你所有的需求并使其适合于适当的对象架构开始变得重要。在这里你有一个图形架构,从某种意义上说,你的数据与节点(人名和性别)和链接(关系和质量)相关联。我可能会借用或实现一个图形结构来表示这一点,根据速度/内存考虑选择最佳的结构,结构将使未来扩展变得容易。

考虑到每个节点最多只有一个链接,并且您希望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)