在Python中,如何将一个对象的所有属性复制到另一个对象?

bq3bfh9z  于 2023-01-19  发布在  Python
关注(0)|答案(6)|浏览(261)

在Python中,是否有一个库方法可以在同一个类的两个(已经存在的)示例之间复制所有属性?
我是说,类似于Apache Commons的PropertyUtilsBean.copyProperties()

lyr7nygr

lyr7nygr1#

试试destination.__dict__.update(source.__dict__)

7rtdyuoh

7rtdyuoh2#

如果类没有修改__getitem____setitem__以进行特殊属性访问,则所有属性都存储在__dict__中,因此可以执行以下操作:

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

如果你使用python属性,你应该查看inspect.getmembers()并过滤掉那些你想要复制的。

enxuqcxy

enxuqcxy3#

如果您必须这样做,我想最好的方法是使用class属性,如下所示:

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

然后显式地迭代它们并使用setattr(new, attr, getattr(old, attr))。我仍然相信可以用更好的设计来解决这个问题,但不推荐它。

7kqas0il

7kqas0il4#

使用此选项,几乎可以将所有内容从一个对象复制到另一个对象:

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 )

但是,如果运行上面的代码,您将看到以下错误:

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

你只能在类静态初始化时复制这些只读属性,就像上面的其他内置magic 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/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 )

现在这个新版本完全可以应付一切:

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: How to generate the code on the fly?
  2. https://en.wikipedia.org/wiki/Metaprogramming
    所以,如果你仔细研究了上面的初始代码,你就可以编写一个脚本来生成它所需要的代码,因此,你可以动态地、完整地复制任何Python对象。
enyaitl3

enyaitl35#

冒着被修改的风险,有一个体面的任何用例吗?
除非我们确切地知道它的用途,否则我们不能明智地称之为“坏了”。
或许可以试试这个:

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

这是在示例之间复制事物的明智方式。

tvmytwxo

tvmytwxo6#

对于类对象,我发现最好添加它们自己的copy方法,这样可以解决deepcopy中内部的、不可pickelable的对象:

import copy
 
class Foo:
  def __init__(self):
    self._var1 = 'foo'
    self._var2 = 'goo'
    ...

  def __copy__(self):
    new_obj = self.__class__()
    for k,v in vars(self).items():
      try:
        setattr(new_obj, k, copy.deepcopy(v))
      except:
        pass   # non-pickelable stuff wasn't needed

     return new_obj

  def copy(self):
    return self.__copy__()

  ...

在您的工作代码中,它将变为:

a = Foo()
b = a.copy()

当然,这是一个小问题。实际上,如果类中有复杂的对象,而这些对象不能直接用copy.deepcopy()类型操作复制,你就可以这样做。如果这是一个简单的问题,只需使用b = copy.deepcopy(a),而不在类中显式定义即可。

相关问题