代码之家  ›  专栏  ›  技术社区  ›  Joril

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

  •  40
  • Joril  · 技术社区  · 16 年前

    在Python中,是否有一个库方法来复制同一类的两个(已经存在的)实例之间的所有属性?

    我是说,有点像阿帕奇公地 PropertyUtilsBean.copyProperties()

    6 回复  |  直到 6 年前
        1
  •  34
  •   ArtOfWarfare    9 年前

    如果你的类不修改 __getitem__ __setitem__ 对于特殊属性访问,所有属性都存储在 __dict__ 所以你可以这样做:

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

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

        2
  •  50
  •   Peter Hosey    16 年前

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

        3
  •  2
  •   J S    16 年前

    我知道你抄袭了,但我不同意。制作另一个副本比用 双关语 如其他人建议的那样,操作(如果通过重新分配变量而丢失现有副本,则会立即对其进行垃圾收集)。python并不意味着要快,而是要可读(尽管我实际上相信copy()比其他方法更快)。

        4
  •  2
  •   Ali Afshar    16 年前

    如果必须这样做,我想最好的方法是使用类属性,比如:

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

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

        5
  •  1
  •   Ali Afshar    16 年前

    冒着被改造的危险,有没有 像样的 有这个的用例吗?

    除非我们确切地知道它是用来干什么的,否则我们不能明智地把它称为“坏的”,就像它看起来的那样。

    也许可以试试这个:

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

    这是在实例之间复制东西的明智方法。

        6
  •  0
  •   Evandro Coan    7 年前

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

    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对象。