txt - python guardar datos
Subclasificar objetos de archivo(para extender las operaciones de apertura y cierre) en Python 3 (3)
Supongamos que quiero extender la abstracción de archivos incorporada con operaciones adicionales en tiempo de open
y close
. En Python 2.7 esto funciona:
class ExtFile(file):
def __init__(self, *args):
file.__init__(self, *args)
# extra stuff here
def close(self):
file.close(self)
# extra stuff here
Ahora estoy buscando actualizar el programa a Python 3, en el que open
es una función de fábrica que podría devolver una instancia de cualquiera de varias clases diferentes del módulo io
según cómo se llame. Podría, en principio, subclasificarlos a todos, pero eso es tedioso, y tendría que volver a implementar el envío que hace open
. (En Python 3, la distinción entre archivos binarios y de texto es bastante más importante que en 2.x, y necesito ambos). Estos objetos se pasarán al código de la biblioteca que podría hacer casi cualquier cosa con ellos, por lo que el idioma de hacer una clase de tipo pato "similar a un archivo" que envuelva el valor de retorno de open
métodos open
y reenviados será más detallado.
¿Alguien puede sugerir un enfoque 3.x que involucre la menor cantidad de información adicional posible más allá del código 2.x mostrado?
Puedes usar un administrador de contexto. Por ejemplo este:
class SpecialFileOpener:
def __init__ (self, fileName, someOtherParameter):
self.f = open(fileName)
# do more stuff
print(someOtherParameter)
def __enter__ (self):
return self.f
def __exit__ (self, exc_type, exc_value, traceback):
self.f.close()
# do more stuff
print(''Everything is over.'')
Entonces puedes usarlo así:
>>> with SpecialFileOpener(''C://test.txt'', ''Hello world!'') as f:
print(f.read())
Hello world!
foo bar
Everything is over.
Usar un bloque de contexto with
es preferible para objetos de archivo (y otros recursos) de todos modos.
Tuve un problema similar, y el requisito de ser compatible con Python 2.xy 3.x. Lo que hice fue similar al siguiente ( versión completa actual ):
class _file_obj(object):
"""Check if `f` is a file name and open the file in `mode`.
A context manager."""
def __init__(self, f, mode):
if isinstance(f, str):
self.file = open(f, mode)
else:
self.file = f
self.close_file = (self.file is not f)
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
if (not self.close_file):
return # do nothing
# clean up
exit = getattr(self.file, ''__exit__'', None)
if exit is not None:
return exit(*args, **kwargs)
else:
exit = getattr(self.file, ''close'', None)
if exit is not None:
exit()
def __getattr__(self, attr):
return getattr(self.file, attr)
def __iter__(self):
return iter(self.file)
Pasa todas las llamadas a los objetos de archivo subyacentes y puede inicializarse desde un archivo abierto o desde un nombre de archivo. También funciona como gestor de contexto. Inspirado por esta respuesta .
tl; dr Usa un administrador de contexto. Vea al final de esta respuesta las precauciones importantes sobre ellos.
Los archivos se complicaron más en Python 3. Si bien hay algunos métodos que se pueden usar en las clases de usuarios normales, esos métodos no funcionan con las clases integradas. Una forma es mezclar una clase deseada antes de crear una instancia, pero esto requiere saber cuál debe ser la primera clase de mezcla:
class MyFileType(???):
def __init__(...)
# stuff here
def close(self):
# more stuff here
Debido a que hay muchos tipos, y es posible que se agreguen más en el futuro (poco probable, pero posible), y no sabemos con certeza cuál se devolverá hasta después de la open
la llamada, este método no funciona.
Otro método es cambiar nuestro tipo personalizado para que tenga el archivo devuelto ___bases__
, y modificar el atributo __class__
la instancia __class__
a nuestro tipo personalizado:
class MyFileType:
def close(self):
# stuff here
some_file = open(path_to_file, ''...'') # ... = desired options
MyFileType.__bases__ = (some_file.__class__,) + MyFile.__bases__
pero esto rinde
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __bases__ assignment: ''_io.TextIOWrapper'' deallocator differs from ''object''
Otro método más que podría funcionar con clases de usuario puro es crear el tipo de archivo personalizado sobre la marcha, directamente desde la clase de la instancia devuelta, y luego actualizar la clase de la instancia devuelta:
some_file = open(path_to_file, ''...'') # ... = desired options
class MyFile(some_file.__class__):
def close(self):
super().close()
print("that''s all, folks!")
some_file.__class__ = MyFile
pero otra vez:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __class__ assignment: only for heap types
Entonces, parece que el mejor método que funcionará en Python 3, y afortunadamente también funcionará en Python 2 (útil si quiere que la misma base de código funcione en ambas versiones) es tener un administrador de contexto personalizado:
class Open(object):
def __init__(self, *args, **kwds):
# do custom stuff here
self.args = args
self.kwds = kwds
def __enter__(self):
# or do custom stuff here :)
self.file_obj = open(*self.args, **self.kwds)
# return actual file object so we don''t have to worry
# about proxying
return self.file_obj
def __exit__(self, *args):
# and still more custom stuff here
self.file_obj.close()
# or here
y para usarlo:
with Open(''some_file'') as data:
# custom stuff just happened
for line in data:
print(line)
# data is now closed, and more custom stuff
# just happened
Un punto importante a tener en cuenta: cualquier excepción no controlada en __init__
o __enter__
evitará que __exit__
ejecute, por lo que en esas dos ubicaciones aún debe usar la función try
/ except
y / o try
/ finally
para asegurarse de que no haya fugas. recursos