3
votes

Vérification de l'égalité des objets présentant des attributs différents

J'ai deux listes d'objets et j'ai besoin de trouver des objets correspondants selon deux ensembles d'attributs différents. Disons que j'ai des objets Vehicle () et que je dois d'abord faire correspondre tous les véhicules de la première liste qui correspondent aux véhicules de la seconde, en recherchant d'abord les couleurs correspondantes, puis les marques correspondantes. J'ai deux solutions, mais je ne suis pas sûr que ce soit la meilleure que je puisse faire. (J'ai vraiment besoin d'optimiser ces performances)

Disons que j'ai:

intersect_color_wise = [x for x in cars1 if x in cars2]

et des listes d'objets en tant que tels:

class Car(Vehicle):
    def __init__(self, color, brand):
        Vehicle.__init__(self,color, brand)


def __hash__(self):
    return Vehicle.__hash__

def __eq__(self, other):
    if isinstance(other, Car):
        return other._color == self._color
    return False


def change_to_car(obj):
    obj.__class__ = Car
    return obj


cars1 = map(change_to_car, vehicles1)
cars2  = map(change_to_car, vehicles2)


8 commentaires

Que faire si vous obtenez d'abord le ensemble de marques et couleurs dans la liste de comparaison? c'est-à-dire marques = {v._brand pour v dans les véhicules2}; matches = [v pour v dans les véhicules1 si v._marque dans les marques] ? Il élimine la complexité de l'itération sur véhicules2 pour chaque itération véhicules1 .


L'exigence explicite obtient-elle deux intersections distinctes?


obj .__ class__ = Voiture - woahhh, mauvaise idée.


@meowgoesthedog pouvez-vous expliquer cela?


@kebanus Oui, j'ai besoin de deux intersections séparées


Les attributs tels que __class__ sont réservés et ne doivent pas être écrits; vous devriez plutôt construire une nouvelle instance de Car . (Bien que pour cet exemple, tout va bien)


@meowgoesthedog c'est ce que je voulais en premier lieu mais quelque chose comme Car (myvehicle) jette une TypeError. Avez-vous par hasard une suggestion?


Pourriez-vous ajouter quelques exemples d'attentes? Je pense que je comprends votre exigence, mais ... Ce que je ferais: construire 2 dictionnaires clés (couleur, marque). Construisez ensuite 2 ensembles à partir de leurs clés. Trouvez une intersection d'ensemble, qui donne des clés communes.


3 Réponses :


0
votes

Quelle est la performance dans ce cas? Vous n'avez pas un ensemble complet de données pour émuler les performances pour des tests appropriés ...:

>>> get_intersections(vehicles1, vehicles2, "_brand", "_color")

{'_brand': [<__main__.Vehicle object at 0x03588910>], '_color': [<__main__.Vehicle object at 0x035889D0>, <__main__.Vehicle object at 0x03588910>, <__main__.Vehicle object at 0x035889F0>]}

>>> get_intersections(vehicles1, vehicles2, "_brand")

{'_brand': [<__main__.Vehicle object at 0x03588910>]}

Vous pouvez également écrire des intersections individuelles si vous le souhaitez:

def get_intersections(list1, list2, *attrs:str):
    getter = attrgetter(*attrs)
    if len(attrs) > 1:
        sets = list(map(set, zip(*[getter(v) for v in list2])))
    else:
        sets = [{getter(v) for v in list2}]
    results = {attr: [v for v in vehicles1 if getattr(v, attr) in sets[s]] for s, attr in enumerate(attrs)}
    return results


3 commentaires

Merci pour votre suggestion. Mes données sont assez volumineuses en fait, donc je ne sais pas si je duplique des choses comme ça ... Et sur mon problème réel, je dois vérifier une liste d'attributs ...


Voir ma mise à jour, vous pouvez l'exécuter sur des attributs individuels avec attrgetter . Pour autant que je puisse voir, le problème avec votre implémentation d'origine est la complexité de pour x en v1 ... pour y en v2 . mais vous n'avez vraiment pas besoin de vérifier plus d'une fois la v2 .


Ok, cela semble mieux, je vais vérifier et revenir vers vous Cependant, le problème est toujours que votre t_set duplique essentiellement mes données (car, généralement, les couleurs et les marques ne sont jamais les mêmes dans chaque liste - j'aurais dû présenter un exemple avec UserIds, etc. pour être plus clair)



0
votes

En fait, Python est un langage dynamique. Cela signifie que vous pouvez modifier à volonté la classe Vehicle pour l'adapter à vos besoins. Vous préparez 2 autres classes (je leur ai fait des sous-classes de Vehicle pour que l'auto-complétion fonctionne dans l'IDE) avec respectivement l'égalité de marque et l'égalité de couleur, et affectez simplement leurs membres à la classe Vehicle:

Vehicle.__eq__ = Vehicle_color.__eq__
Vehicle.__hash__ = Vehicle_color.__hash__
intersect_color_wise = [x for x in vehicles1 if x in vehicles2]

Pour obtenir intersection de marque:

Vehicle.__eq__ = Vehicle_brand.__eq__
Vehicle.__hash__ = Vehicle_brand.__hash__
intersect_brand_wise = [x for x in vehicles1 if x in vehicles2]

Ensuite pour obtenir l'intersection de couleur:

class Vehicle(object):
    def __init__(self, color, brand):
        self._color = color
        self._brand = brand

class Vehicle_brand(Vehicle):
    def __eq__(self, other):
        return self._brand == other._brand
    def __hash__(self):
        return hash(self._brand)


class Vehicle_color(Vehicle):
    def __eq__(self, other):
        return self._color == other._color
    def __hash__(self):
        return hash(self._color)

La bonne nouvelle ici, c'est que si votre La classe Véhicule a d'autres membres, ils ne sont pas touchés lorsque vous modifiez la partie égalité, et vous ne copiez ni ne dupliquez aucun objet: seulement 2 méthodes dans les objets de classe.

Il se peut très OO pur, mais ça devrait marcher ...


0 commentaires

0
votes

Question: Vérification de l'égalité des objets suivant différentes attrbutes

Au lieu de trouver des objets égaux par la suite, en effectuant une boucle en boucle , vérifiez l ' égalité lors de l'instanciation.
Pour enregistrer la mémoire , enregistrez uniquement les hachages égaux des objets.

  • Utilisez des attributs de classe pour contenir un dict des objets de set 1
    et une liste pour contenir le _hash d'objets égaux .

    intersection:[2125945310]
    vehicle:color:red, brand:fiat
    
  • Enregistrer une référence d'objets set 1 dans le dict ref1 .
    Vérifiez uniquement les objets de set 2 par rapport au dict ref1 et enregistrez uniquement si égal .

    for vehicle in VehicleDiff.intersection():
        print('vehicle:{}'.format(vehicle))
    
  • Aide methode intersection pour obtenir un objet VehicleDiff à partir de _hash .

    for p in [('blue', 'fiat'), ('red', 'volvo'), ('red', 'fiat')]:
        VehicleDiff(1, *p)
    
    for p in [('blue', 'volvo'), ('red', 'BMW'), ('red', 'fiat')]:
        VehicleDiff(2, *p)
    
  • Représentation sous forme de chaîne d'un objet VehicleDiff .

        def __str__(self):
            return 'color:{}, brand:{}'.format(self.color, self.brand)
    
  • Il n'est pas nécessaire de conserver les objets dans une liste .

    Remarque : comme les données d'exemple données n'ont pas d'intersection, j'ai ajouté ('red', 'fiat') à set 2

        @staticmethod
        def intersection():
            print('intersection:{}'.format(VehicleDiff._intersection))
            for _hash in VehicleDiff._intersection:
                yield VehicleDiff.ref1[_hash]
    
  • Imprimez le résultat, le cas échéant .

            _hash = hash((color, brand))
            if set == 1:
                VehicleDiff.ref1[_hash] = self
    
            elif _hash in VehicleDiff.ref1:
                VehicleDiff._intersection.append(_hash)
    

Sortie :

class VehicleDiff:
    ref1 = {}
    _intersection = []

    def __init__(self, set, color, brand):
        self.color = color
        self.brand = brand

Testé avec Python: 3.4.2


0 commentaires