How to copy all properties of an object to another object, in Python?

后端 未结 5 1174
自闭症患者
自闭症患者 2020-12-02 16:30

Is there a library method to copy all the properties between two (already present) instances of the same class, in Python?

I mean, something like Apache Commons\'

相关标签:
5条回答
  • 2020-12-02 17:09

    At the risk of being modded down, is there a decent any use-case for this?

    Unless we know exactly what it's for, we can't sensibly call it as "broken" as it seems.

    Perhaps try this:

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

    That's the sane way of copying things between instances.

    0 讨论(0)
  • 2020-12-02 17:11

    Using this you can almost copy everything from one object to another:

    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 )
    

    However, if you run the code above, you will see these errors:

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

    You can only copy these read-only properties by doing it on the class static initialization, like the other built-in magic python methods as __str__ just above:

    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 )
    

    Now this new version completely works coping everything:

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

    The disadvantage of it is that you need to write the Python code manually to overcome the read only attributes. However, you can write python code on the fly with metaprogramming:

    1. Python: How to generate the code on the fly?
    2. https://en.wikipedia.org/wiki/Metaprogramming

    So, if you work over this initial code just above, you can write a script which generates the code it needs to. Hence, you can dynamically and completely copy any Python object.

    0 讨论(0)
  • 2020-12-02 17:18

    If you have to do this, I guess the nicest way is to have a class attribute something like :

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

    Then iterate them explicitly and use setattr(new, attr, getattr(old, attr)). I still believe it can be solved with a better design though, and don't recommend it.

    0 讨论(0)
  • 2020-12-02 17:20

    Try destination.__dict__.update(source.__dict__).

    0 讨论(0)
  • 2020-12-02 17:21

    If your class does not modify __getitem__ or __setitem__ for special attribute access all your attributes are stored in __dict__ so you can do:

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

    If you use python properties you should look at inspect.getmembers() and filter out the ones you want to copy.

    0 讨论(0)
提交回复
热议问题