Подтвердить что ты не робот

Как скопировать все свойства объекта на другой объект в Python?

Существует ли метод библиотеки для копирования всех свойств между двумя (уже существующими) экземплярами одного и того же класса в Python?

Я имею в виду, что-то вроде PropertyUtilsBean.copyProperties() Apache Commons PropertyUtilsBean.copyProperties()

4b9b3361

Ответ 1

Если ваш класс не изменяет __getitem__ или __setitem__ для доступа к специальному атрибуту, все ваши атрибуты хранятся в __dict__, чтобы вы могли:

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

Если вы используете свойства python, вы должны посмотреть inspect.getmembers() и отфильтровать те, которые хотите скопировать.

Ответ 2

Попробуйте destination.__dict__.update(source.__dict__).

Ответ 3

Если вам нужно это сделать, я думаю, самый лучший способ - присвоить атрибуту class что-то вроде:

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

Затем итерации их явно и используйте setattr(new, attr, getattr(old, attr)). Я все еще верю, что это может быть решено с лучшим дизайном, хотя и не рекомендую его.

Ответ 4

Я знаю, что вы скопировали копию, но я не согласен. Ясно, что сделать другую копию, чем изменить существующее на месте с помощью dict, как предполагали другие (если вы потеряете существующую копию, переназначив переменную, она сразу же получит сбор мусора). Python не должен быть быстрым, он должен быть читаемым (хотя я действительно считаю, что copy() будет быстрее, чем другие методы).

Ответ 5

Из-за того, что он может быть изменен, есть ли достойный какой-либо прецедент для этого?

Если мы точно не знаем, для чего это, мы не можем разумно назвать его "сломанным", как кажется.

Возможно, попробуйте следующее:

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

Это разумный способ копирования вещей между экземплярами.

Ответ 6

Используя это, вы можете почти скопировать все из одного объекта в другой:

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/info/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/info/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 )

Однако, если вы запустите код выше, вы увидите эти ошибки:

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

Вы можете скопировать эти свойства только для чтения, выполнив это при статической инициализации класса, как и другие встроенные магические методы python, как __str__ чуть выше:

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/info/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/info/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 )

Теперь эта новая версия полностью работает, справляясь со всем:

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

Недостатком этого является то, что вам нужно писать код Python вручную, чтобы преодолеть атрибуты только для чтения. Тем не менее, вы можете написать код Python на лету с помощью метапрограммирования:

  1. Python: Как сгенерировать код на лету?
  2. https://en.wikipedia.org/wiki/Metaprogramming

Таким образом, если вы работаете над этим исходным кодом чуть выше, вы можете написать скрипт, который сгенерирует необходимый код. Следовательно, вы можете динамически и полностью копировать любой объект Python.