Hoe kopieer je alle eigenschappen van een object naar een ander object, in Python?

Is er een bibliotheekmethode om alle eigenschappen tussen twee (reeds aanwezige) instanties van dezelfde klasse in Python te kopiëren?

Ik bedoel, zoiets als Apache Commons’ PropertyUtilsBean.copyProperties()


Antwoord 1, autoriteit 100%

Probeer destination.__dict__.update(source.__dict__).


Antwoord 2, autoriteit 67%

Als je klas __getitem__of __setitem__niet aanpast voor speciale attribuuttoegang, worden al je attributen opgeslagen in __dict__zodat je het volgende kunt doen:

nobj.__dict__ = oobj.__dict__.copy()    # just a shallow copy

Als je python-eigenschappen gebruikt, moet je naar inspect.getmembers()kijken en degene die je wilt kopiëren eruit filteren.


Antwoord 3, autoriteit 3%

Als je dit moet doen, denk ik dat de beste manier is om een ​​class-attribuut te hebben, zoiets als :

Class Copyable(object):
    copyable_attributes = ('an_attribute', 'another_attribute')

Itereer ze dan expliciet en gebruik setattr(new, attr, getattr(old, attr)). Ik geloof echter nog steeds dat het kan worden opgelost met een beter ontwerp, en raad het niet aan.


Antwoord 4

Op het gevaar af om te worden aangepast, is er een fatsoenlijkeuse-case hiervoor?

Tenzij we precies weten waar het voor is, kunnen we het niet zo “kapot” noemen als het lijkt.

Probeer dit eens:

firstobject.an_attribute = secondobject.an_attribute
firstobject.another_attribute = secondobject.another_attribute

Dat is de verstandige manier om dingen tussen instanties te kopiëren.


Antwoord 5

Hiermee kun je bijna alles van het ene object naar het andere kopiëren:

import sys
_target_object = sys.stderr
_target_object_class_type = type( _target_object )
class TargetCopiedObject(_target_object_class_type):
    """
        Which special methods bypasses __getattribute__ in Python?
        https://stackoverflow.com/questions/12872695/which-special-methods-bypasses
    """
    if hasattr( _target_object, "__abstractmethods__" ):
        __abstractmethods__ = _target_object.__abstractmethods__
    if hasattr( _target_object, "__base__" ):
        __base__ = _target_object.__base__
    if hasattr( _target_object, "__bases__" ):
        __bases__ = _target_object.__bases__
    if hasattr( _target_object, "__basicsize__" ):
        __basicsize__ = _target_object.__basicsize__
    if hasattr( _target_object, "__call__" ):
        __call__ = _target_object.__call__
    if hasattr( _target_object, "__class__" ):
        __class__ = _target_object.__class__
    if hasattr( _target_object, "__delattr__" ):
        __delattr__ = _target_object.__delattr__
    if hasattr( _target_object, "__dict__" ):
        __dict__ = _target_object.__dict__
    if hasattr( _target_object, "__dictoffset__" ):
        __dictoffset__ = _target_object.__dictoffset__
    if hasattr( _target_object, "__dir__" ):
        __dir__ = _target_object.__dir__
    if hasattr( _target_object, "__doc__" ):
        __doc__ = _target_object.__doc__
    if hasattr( _target_object, "__eq__" ):
        __eq__ = _target_object.__eq__
    if hasattr( _target_object, "__flags__" ):
        __flags__ = _target_object.__flags__
    if hasattr( _target_object, "__format__" ):
        __format__ = _target_object.__format__
    if hasattr( _target_object, "__ge__" ):
        __ge__ = _target_object.__ge__
    if hasattr( _target_object, "__getattribute__" ):
        __getattribute__ = _target_object.__getattribute__
    if hasattr( _target_object, "__gt__" ):
        __gt__ = _target_object.__gt__
    if hasattr( _target_object, "__hash__" ):
        __hash__ = _target_object.__hash__
    if hasattr( _target_object, "__init__" ):
        __init__ = _target_object.__init__
    if hasattr( _target_object, "__init_subclass__" ):
        __init_subclass__ = _target_object.__init_subclass__
    if hasattr( _target_object, "__instancecheck__" ):
        __instancecheck__ = _target_object.__instancecheck__
    if hasattr( _target_object, "__itemsize__" ):
        __itemsize__ = _target_object.__itemsize__
    if hasattr( _target_object, "__le__" ):
        __le__ = _target_object.__le__
    if hasattr( _target_object, "__lt__" ):
        __lt__ = _target_object.__lt__
    if hasattr( _target_object, "__module__" ):
        __module__ = _target_object.__module__
    if hasattr( _target_object, "__mro__" ):
        __mro__ = _target_object.__mro__
    if hasattr( _target_object, "__name__" ):
        __name__ = _target_object.__name__
    if hasattr( _target_object, "__ne__" ):
        __ne__ = _target_object.__ne__
    if hasattr( _target_object, "__new__" ):
        __new__ = _target_object.__new__
    if hasattr( _target_object, "__prepare__" ):
        __prepare__ = _target_object.__prepare__
    if hasattr( _target_object, "__qualname__" ):
        __qualname__ = _target_object.__qualname__
    if hasattr( _target_object, "__reduce__" ):
        __reduce__ = _target_object.__reduce__
    if hasattr( _target_object, "__reduce_ex__" ):
        __reduce_ex__ = _target_object.__reduce_ex__
    if hasattr( _target_object, "__repr__" ):
        __repr__ = _target_object.__repr__
    if hasattr( _target_object, "__setattr__" ):
        __setattr__ = _target_object.__setattr__
    if hasattr( _target_object, "__sizeof__" ):
        __sizeof__ = _target_object.__sizeof__
    if hasattr( _target_object, "__str__" ):
        __str__ = _target_object.__str__
    if hasattr( _target_object, "__subclasscheck__" ):
        __subclasscheck__ = _target_object.__subclasscheck__
    if hasattr( _target_object, "__subclasses__" ):
        __subclasses__ = _target_object.__subclasses__
    if hasattr( _target_object, "__subclasshook__" ):
        __subclasshook__ = _target_object.__subclasshook__
    if hasattr( _target_object, "__text_signature__" ):
        __text_signature__ = _target_object.__text_signature__
    if hasattr( _target_object, "__weakrefoffset__" ):
        __weakrefoffset__ = _target_object.__weakrefoffset__
    if hasattr( _target_object, "mro" ):
        mro = _target_object.mro
    def __init__(self):
        """
            Override any super class `type( _target_object )` constructor,
            so we can instantiate any kind of replacement object.
            Assures all properties were statically replaced just above. This
            should happen in case some new attribute is added to the python
            language.
            This also ignores the only two methods which are not equal,
            `__init__()` and `__getattribute__()`.
            How do you programmatically set an attribute?
            https://stackoverflow.com/questions/285061/how-do-you-programmatically
        """
        different_methods = set(["__init__", "__getattribute__"])
        attributes_to_check = set( dir( object ) + dir( type ) )
        attributes_to_copy = dir( _target_object )
        # Check for missing magic built-ins methods on the class static initialization
        for attribute in attributes_to_check:
            if attribute not in different_methods \
                    and hasattr( _target_object, attribute ):
                base_class_attribute = self.__getattribute__( attribute )
                target_class_attribute = _target_object.__getattribute__( attribute )
                if base_class_attribute != target_class_attribute:
                    sys.stdout.write(
                            "    The base class attribute `%s` is different from the "
                            "target class:\n%s\n%s\n\n" % ( attribute,
                                                    base_class_attribute, 
                                                    target_class_attribute ) )
        # Finally copy everything it can
        different_methods.update( attributes_to_check )
        for attribute in attributes_to_copy:
            if attribute not in different_methods:
                print( "Setting:", attribute )
                try:
                    target_class_attribute = _target_object.__getattribute__(attribute)
                    setattr( self, attribute, target_class_attribute )
                except AttributeError as error:
                    print( "Error coping the attribute `%s`: %s" % (attribute, error) )
o = TargetCopiedObject()
print( "TargetCopiedObject:", o )

Als u echter de bovenstaande code uitvoert, ziet u deze fouten:

python test.py
Setting: _CHUNK_SIZE
Setting: __del__
Setting: __enter__
Setting: __exit__
Setting: __getstate__
Setting: __iter__
Setting: __next__
Setting: _checkClosed
Setting: _checkReadable
Setting: _checkSeekable
Setting: _checkWritable
Setting: _finalizing
Setting: buffer
Error coping the attribute `buffer`: readonly attribute
Setting: close
Setting: closed
Error coping the attribute `closed`: attribute 'closed' of '_io.TextIOWrapper' objects is not writable
Setting: detach
Setting: encoding
Error coping the attribute `encoding`: readonly attribute
Setting: errors
Error coping the attribute `errors`: attribute 'errors' of '_io.TextIOWrapper' objects is not writable
Setting: fileno
Setting: flush
Setting: isatty
Setting: line_buffering
Error coping the attribute `line_buffering`: readonly attribute
Setting: mode
Setting: name
Error coping the attribute `name`: attribute 'name' of '_io.TextIOWrapper' objects is not writable
Setting: newlines
Error coping the attribute `newlines`: attribute 'newlines' of '_io.TextIOWrapper' objects is not writable
Setting: read
Setting: readable
Setting: readline
Setting: readlines
Setting: seek
Setting: seekable
Setting: tell
Setting: truncate
Setting: writable
Setting: write
Setting: writelines
TargetCopiedObject: <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>

Je kunt deze alleen-lezen eigenschappen alleen kopiëren door het te doen op de statische initialisatie van de klasse, zoals de andere ingebouwde magische python-methoden als __str__net hierboven:

import sys
_target_object = sys.stderr
_target_object_class_type = type( _target_object )
class TargetCopiedObject(_target_object_class_type):
    """
        Which special methods bypasses __getattribute__ in Python?
        https://stackoverflow.com/questions/12872695/which-special-methods-bypasses
    """
    if hasattr( _target_object, "__abstractmethods__" ):
        __abstractmethods__ = _target_object.__abstractmethods__
    if hasattr( _target_object, "__base__" ):
        __base__ = _target_object.__base__
    if hasattr( _target_object, "__bases__" ):
        __bases__ = _target_object.__bases__
    if hasattr( _target_object, "__basicsize__" ):
        __basicsize__ = _target_object.__basicsize__
    if hasattr( _target_object, "__call__" ):
        __call__ = _target_object.__call__
    if hasattr( _target_object, "__class__" ):
        __class__ = _target_object.__class__
    if hasattr( _target_object, "__delattr__" ):
        __delattr__ = _target_object.__delattr__
    if hasattr( _target_object, "__dict__" ):
        __dict__ = _target_object.__dict__
    if hasattr( _target_object, "__dictoffset__" ):
        __dictoffset__ = _target_object.__dictoffset__
    if hasattr( _target_object, "__dir__" ):
        __dir__ = _target_object.__dir__
    if hasattr( _target_object, "__doc__" ):
        __doc__ = _target_object.__doc__
    if hasattr( _target_object, "__eq__" ):
        __eq__ = _target_object.__eq__
    if hasattr( _target_object, "__flags__" ):
        __flags__ = _target_object.__flags__
    if hasattr( _target_object, "__format__" ):
        __format__ = _target_object.__format__
    if hasattr( _target_object, "__ge__" ):
        __ge__ = _target_object.__ge__
    if hasattr( _target_object, "__getattribute__" ):
        __getattribute__ = _target_object.__getattribute__
    if hasattr( _target_object, "__gt__" ):
        __gt__ = _target_object.__gt__
    if hasattr( _target_object, "__hash__" ):
        __hash__ = _target_object.__hash__
    if hasattr( _target_object, "__init__" ):
        __init__ = _target_object.__init__
    if hasattr( _target_object, "__init_subclass__" ):
        __init_subclass__ = _target_object.__init_subclass__
    if hasattr( _target_object, "__instancecheck__" ):
        __instancecheck__ = _target_object.__instancecheck__
    if hasattr( _target_object, "__itemsize__" ):
        __itemsize__ = _target_object.__itemsize__
    if hasattr( _target_object, "__le__" ):
        __le__ = _target_object.__le__
    if hasattr( _target_object, "__lt__" ):
        __lt__ = _target_object.__lt__
    if hasattr( _target_object, "__module__" ):
        __module__ = _target_object.__module__
    if hasattr( _target_object, "__mro__" ):
        __mro__ = _target_object.__mro__
    if hasattr( _target_object, "__name__" ):
        __name__ = _target_object.__name__
    if hasattr( _target_object, "__ne__" ):
        __ne__ = _target_object.__ne__
    if hasattr( _target_object, "__new__" ):
        __new__ = _target_object.__new__
    if hasattr( _target_object, "__prepare__" ):
        __prepare__ = _target_object.__prepare__
    if hasattr( _target_object, "__qualname__" ):
        __qualname__ = _target_object.__qualname__
    if hasattr( _target_object, "__reduce__" ):
        __reduce__ = _target_object.__reduce__
    if hasattr( _target_object, "__reduce_ex__" ):
        __reduce_ex__ = _target_object.__reduce_ex__
    if hasattr( _target_object, "__repr__" ):
        __repr__ = _target_object.__repr__
    if hasattr( _target_object, "__setattr__" ):
        __setattr__ = _target_object.__setattr__
    if hasattr( _target_object, "__sizeof__" ):
        __sizeof__ = _target_object.__sizeof__
    if hasattr( _target_object, "__str__" ):
        __str__ = _target_object.__str__
    if hasattr( _target_object, "__subclasscheck__" ):
        __subclasscheck__ = _target_object.__subclasscheck__
    if hasattr( _target_object, "__subclasses__" ):
        __subclasses__ = _target_object.__subclasses__
    if hasattr( _target_object, "__subclasshook__" ):
        __subclasshook__ = _target_object.__subclasshook__
    if hasattr( _target_object, "__text_signature__" ):
        __text_signature__ = _target_object.__text_signature__
    if hasattr( _target_object, "__weakrefoffset__" ):
        __weakrefoffset__ = _target_object.__weakrefoffset__
    if hasattr( _target_object, "mro" ):
        mro = _target_object.mro
    # Copy all the other read only attributes
    if hasattr( _target_object, "buffer" ):
        buffer = _target_object.buffer
    if hasattr( _target_object, "closed" ):
        closed = _target_object.closed
    if hasattr( _target_object, "encoding" ):
        encoding = _target_object.encoding
    if hasattr( _target_object, "errors" ):
        errors = _target_object.errors
    if hasattr( _target_object, "line_buffering" ):
        line_buffering = _target_object.line_buffering
    if hasattr( _target_object, "name" ):
        name = _target_object.name
    if hasattr( _target_object, "newlines" ):
        newlines = _target_object.newlines
    def __init__(self):
        """
            Override any super class `type( _target_object )` constructor,
            so we can instantiate any kind of replacement object.
            Assures all properties were statically replaced just above. This
            should happen in case some new attribute is added to the python
            language.
            This also ignores the only two methods which are not equal,
            `__init__()` and `__getattribute__()`.
            How do you programmatically set an attribute?
            https://stackoverflow.com/questions/285061/how-do-you-programmatically
        """
        # Add the copied read only atribute to the ignored list, so they
        # do not throw new errors while trying copy they dynamically
        different_methods = set\
        ([
            "__init__",
            "__getattribute__",
            "buffer",
            "closed",
            "encoding",
            "errors",
            "line_buffering",
            "name",
            "newlines",
        ])
        attributes_to_check = set( dir( object ) + dir( type ) )
        attributes_to_copy = dir( _target_object )
        # Check for missing magic built-ins methods on the class static initialization
        for attribute in attributes_to_check:
            if attribute not in different_methods \
                    and hasattr( _target_object, attribute ):
                base_class_attribute = self.__getattribute__( attribute )
                target_class_attribute = _target_object.__getattribute__( attribute )
                if base_class_attribute != target_class_attribute:
                    sys.stdout.write(
                            "    The base class attribute `%s` is different from the "
                            "target class:\n%s\n%s\n\n" % ( attribute,
                                                    base_class_attribute,
                                                    target_class_attribute ) )
        # Finally copy everything it can
        different_methods.update( attributes_to_check )
        for attribute in attributes_to_copy:
            if attribute not in different_methods:
                print( "Setting:", attribute )
                try:
                    target_class_attribute = _target_object.__getattribute__(attribute)
                    setattr( self, attribute, target_class_attribute )
                except AttributeError as error:
                    print( "Error coping the attribute `%s`: %s" % (attribute, error) )
o = TargetCopiedObject()
print( "TargetCopiedObject:", o )

Nu werkt deze nieuwe versie volledig om alles aan te kunnen:

python test.py
Setting: _CHUNK_SIZE
Setting: __del__
Setting: __enter__
Setting: __exit__
Setting: __getstate__
Setting: __iter__
Setting: __next__
Setting: _checkClosed
Setting: _checkReadable
Setting: _checkSeekable
Setting: _checkWritable
Setting: _finalizing
Setting: close
Setting: detach
Setting: fileno
Setting: flush
Setting: isatty
Setting: mode
Setting: read
Setting: readable
Setting: readline
Setting: readlines
Setting: seek
Setting: seekable
Setting: tell
Setting: truncate
Setting: writable
Setting: write
Setting: writelines
TargetCopiedObject: <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>

Het nadeel hiervan is dat je de Python-code handmatig moet schrijven om de alleen-lezen attributen te omzeilen. U kunt echter on-the-fly python-code schrijven met metaprogrammering:

  1. Python: hoe kan ik de code on-the-fly genereren?
  2. https://en.wikipedia.org/wiki/Metaprogramming

Dus, als je aan deze initiële code net hierboven werkt, kun je een script schrijven dat de code genereert die het nodig heeft. Daarom kunt u elk Python-object dynamisch en volledig kopiëren.

Other episodes