2008-10-28 3 views
31

Есть ли библиотечный метод для копирования всех свойств между двумя (уже существующими) экземплярами одного и того же класса в Python? Я имею в виду что-то вроде имущества Apache Commons 'PropertyUtilsBean.copyProperties()Как скопировать все свойства объекта в другой объект, в Python?

Спасибо!

ответ

26

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

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

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

44

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

+1

Sweet! Теперь я могу передать аргументы argparse и полностью скопировать их в объект в `` __init__` вместо того, чтобы поставлять его вместо объекта пространства имен и после этого ему нужно вызвать другой `init`. Есть ли смысл не использовать `vars()` как в `vars (source)` вместо `source .__ dict__`? – mlt 2013-07-16 18:05:40

+0

@mlt: Чтение описания в документации, а не то, что я знаю. – 2013-07-16 19:26:33

+0

Мне это нравится, поскольку он не выкинет существующие свойства `destination`, когда` source` не имеет никаких аналогичных свойств. – ArtOfWarfare 2015-10-02 13:49:31

2

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

0

С другой стороны, существует ли достойный какой-либо прецедент?

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

Возможно попробовать это:

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

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

2

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

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

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

0

С помощью этого вы можете почти скопировать все от одного объекта к другому:

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

Вы можете только скопировать эти свойства только для чтения, делая это на статической инициализации класса, как и другие встроенные методы магического питона, как __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 вручную преодолеть только для чтения атрибутов.Тем не менее, вы можете написать код питона на лету с метапрограммированием:

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

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

Смежные вопросы