2008-10-28 12 views

Respuesta

26

Si su clase no modifica __getitem__ o __setitem__ para acceder atributo especial todos sus atributos se almacenan en __dict__ lo que puede hacer:

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

Si utiliza las propiedades pitón usted debe buscar en inspect.getmembers() y filtrar la unos que quieres copiar

44

Probar destination.__dict__.update(source.__dict__).

+1

¡Dulce! Ahora puedo pasar argparse argumentos y copiarlos todos a un objeto a la vez en su '__init__' en lugar de suministrarlo en lugar de objeto de espacio de nombres y tener que llamar a otro' init' a partir de entonces. ¿Hay algún punto para no usar 'vars()' como en 'vars (source)' en lugar de 'source .__ dict__'? – mlt

+0

@mlt: Leer la descripción en la documentación, no que yo sepa. –

+0

Me gusta esta, ya que no arrojará las propiedades existentes de 'destino' cuando' fuente' carece de propiedades análogas. – ArtOfWarfare

2

Conozco tu copia modificada, pero no estoy de acuerdo. Es más claro hacer otra copia que modificar la existente en el lugar con dict manipulación, como otros sugirieron (si pierde la copia existente al reasignar la variable, se recolectará de forma inmediata). Python no pretende ser rápido, debe ser legible (aunque en realidad creo que copy() será más rápido que los otros métodos).

+0

Acepto, una copia es más clara, pero el "caso de uso" en el que estoy trabajando requiere una modificación en el lugar. Gracias por su opinión :) – Joril

0

A riesgo de ser modded, ¿hay un decente para este uso?

A menos que sepamos exactamente para qué sirve, no podemos llamarlo "roto" como parece.

Tal vez intente esto:

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

Esa es la manera sensata de copiar cosas entre instancias.

+1

Sé que puedo copiar cada propiedad a mano, me preguntaba si había una forma de hacerlo automáticamente :) En cuanto al caso de uso, estaba tratando de implementar un método refresh_from_persistence (obj) que actualizaría el objeto in situ, evitando tener que actualizar cada referencia actualmente en la memoria. – Joril

+0

Ah, creo que ya veo. Es un caso de uso desagradable, ya que si quieres un nuevo objeto, usa un nuevo objeto (imo). Haré una respuesta adecuada;) –

2

Si usted tiene que hacer esto, supongo que la mejor manera es tener un atributo de clase algo como:

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

A continuación, recorra explícitamente y utilizar setattr(new, attr, getattr(old, attr)). Todavía creo que se puede resolver con un mejor diseño, y no lo recomiendo.

0

El uso de este casi se puede copiar todo, desde un objeto a otro:

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) 

Sin embargo, si ejecuta el código anterior, verá estos errores:

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

sólo se puede copiar estas propiedades de solo lectura al hacerlo en la inicialización estática de la clase, al igual que los otros métodos incorporados de python mágica como __str__ justo arriba:

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) 

Ahora bien, esta nueva versión funciona completamente todo afrontamiento:

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

La desventaja de esto es que tiene que escribir el código Python manualmente para superar la lectura solamente atributos.Sin embargo, se puede escribir código Python sobre la marcha con metaprogramming:

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

Por lo tanto, si se trabaja sobre el código inicial justo por encima, se puede escribir un script que genera la código que necesita. Por lo tanto, puede copiar de forma dinámica cualquier objeto de Python.

Cuestiones relacionadas