Can I efficiently swap two class instances by swapping __dict__?

后端 未结 4 1177
别那么骄傲
别那么骄傲 2021-01-08 00:03

I have a big class with lots of members, and quite a few references to instances of this class lying around. Unfortunately (for reasonable reasons) all these references are

相关标签:
4条回答
  • 2021-01-08 00:33

    This swapping should be implemented in your sorted data structures, not in this class for its instances.

    0 讨论(0)
  • 2021-01-08 00:38

    For your own sanity, and that of whoever may use your code after you, if you decide to use this function, you should document the hell out of it so that you know what's going on. Your more self-documenting alternative, of course, is to write a function that swaps all the data members individually. Obviously this isn't very future-proof if your data model will be changing, but it will be less confusing. If it's not something you want to write out by hand, you could define a function to swap one data field, and run it over all your class attributes:

    def swap (a, b):
      for key in a.__dict__.keys():
        swapOneField(key, a, b)
    

    and for swapOneField get the values from a.__dict__ and b.__dict__ and use the swapping algorithm of your choice. It's more verbose, and probably less efficient, but if you save more time in not debugging it than you lose in running it, it's probably worth your time.

    0 讨论(0)
  • 2021-01-08 00:44

    If I understand correctly, you are actually swapping instances of a class, not classes.

    Instance state is kept in two possible places: __slots__ and __dict__. If you swap those, you've basically swapped instances while retaining the original name bindings. One caveat is that the class cannot be immutable (must not define __hash__()) as any instances that were already members of a set or keys in a dictionary would then become irretrievable.

    If it were me, I think I would have the .swap() method be a class method instead -- I think it would read easier:

    class SomeBigClass():
        @staticmethod
        def swap_dict(instance_a, instance_b):
            instance_a.__dict__, instance_b.__dict__ = \
                    instance_b.__dict__, instance_a.__dict__
        @classmethod
        def swap_slot(cls, instance_a, instance_b):
            values = []
            for attr in cls.__slots__:
                values.append(
                    (attr,
                     getattr(instance_a, attr),
                     getattr(instance_b, attr),
                     ))
            for attr, val1, val2 in values:
                setattr(instance_a, attr, val2)
                setattr(instance_b, attr, val1)
    

    and then later

    SomeBigClass.swap_dict(this_instance, other_instance)
    

    or

    SomeBigClass.swap_slot(this_instance, other_instance)
    

    Why shouldn't you do this? If you have instances bound to names you should not do this. Consider:

    frubbah = SomeBigClass(attr="Hi, Mom!")
    zikroid = SomeBigClass(attr='Hi, Dad!")
    SomeBigClass.swap_dict(frubbah, zikroid)
    

    After the swap, potentially everything you thought you knew about zikroid has changed.

    0 讨论(0)
  • 2021-01-08 00:51

    Edit

    What you're doing is possible, although it will make people cringe because it is hackish. If at all possible, I would suggest that you look at rewriting/refactoring your comparison operators. That will give you the best outcome by far. Of course, not knowing the scope or time-frame involved, it is very difficult to tell whether this is immediately practical, but trust me, you will spend less time re-writing in the long term if you can do things "right".

    Original

    Realistically, it sounds like you're dealing with three classes -- a data object and two utility classes -- but that is another issue.

    This will break, so I am going to go ahead and say, "no, you cannot swap classes by swapping __dict__s":

    >>> class Foo:
    ...    def __init__(self):
    ...        self.__bar = 1
    ...    def printBar(self):
    ...        print self.__bar
    ... 
    >>> class Bar:
    ...    def __init__(self):
    ...        self.__bar=2
    ...    def printBar(self):
    ...        print self.__bar
    ... 
    >>> f=Foo()
    >>> f.printBar() # works as expected
    1
    >>> f=Foo()
    >>> b=Bar()
    >>> f.__dict__, b.__dict__ = b.__dict__, f.__dict__
    >>> f.printBar() # attempts to access private value from another class
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in printBar
    AttributeError: Foo instance has no attribute '_Foo__bar'
    
    0 讨论(0)
提交回复
热议问题