LINUX.ORG.RU

__new__ и __del__

 ,


2

2

Шалом.
Как известно, __init__ не возвращает ничего, кроме None, поэтому использую __new__, дабы возвращать при инициализации экземпляра _io.TextIOWrapper. После отработки сборщик мусора почему-то не закрывает файл, даже при явно прописанном del myfile.

class file:
	def __new__(self, filename):
		self.openfile = open(filename, 'r')
		return self.openfile
		
	def __del__(self):
		print('File', filename, ' closes.')
		self.openfile.close()

	def read(filename):
		for filestr in filename:
			print(filestr)

myfile = file(r'/etc/rc.conf')
print(type(myfile))
file.read(myfile)
del myfile


Подскажите, пожалуйста, почему так происходит? Есть другой пример с __init__, в котором всё отрабатывает как надо:

# class
class Awesome:

    # the init method
    def __init__(self, filename):

        print("Inside the __init__ method.")

        # open file
        self.fobj = open(filename, "w")

    # method
    def writeContent(self, data):

        print("Inside the writeContent method.")

        # write the data
        self.fobj.write(data)

    # the del method
    def __del__(self):

        print("Inside the __del__ method.")

        # close file
        self.fobj.close()

# object
obj = Awesome("helloworld.txt")
obj.writeContent("Hello World")

★★★★★

doing it wrong, используй with.

Просто скажу - из за того, что ты переопределил __new__(), и не возвращаешь из него экземпляр класса, ты не создаёшь экземпляра класса вообще. это тоже самое, как если бы ты просто напрямую вызывал open().

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 1)
Ответ на: комментарий от eternal_sorrow

doing it wrong, используй with.

Объясни, пожалуйста, подробнее.

Просто скажу - из за того, что ты переопределил __new__(), и не возвращаешь из него экземпляр класса, ты не создаёшь экземпляра класса вообще. это тоже самое, как если бы ты просто напрямую вызывал open().

И вот это тоже не понял :)

IPR ★★★★★ ()
Ответ на: комментарий от IPR

Объясни, пожалуйста, подробнее.

with open(filename) as file:
    # тут делаешь что то с файлом

# тут, после выхода из блока with файл закрыт

И вот это тоже не понял :)

Что именно не понял? Если тебе это непонятно, то лезть в __new__() тебе точно рановато. В большинстве случаев, если ты не делаешь что то странное, __new__() должен возвращать инстанс класса. И первый аргумент этого метода - класс а не инстанс (который ещё не существует).

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 3)
Ответ на: комментарий от eternal_sorrow

Мне вообще много чего рано ещё, я только пытаюсь разобраться с ООП в python'е. С __new__ понял, что его переопределять вообще не стоит и бессмыслено в моём случае. Переделал так:

class file:
	def __init__(self, filename):
		self.filename = filename
		self.openfile = open(self.filename, 'r')
		#return self.openfile
		
	def __del__(self):
		print('File', self.filename, ' closes.')
		self.openfile.close()

	def read(filename):
		for filestr in filename:
			print(filestr)

myfile = file(r'/etc/rc.conf')
print(type(myfile.openfile))
file.read(myfile.openfile)
#del myfile

Вроде теперь по-человечески выглядит и работает.
// Спасибо за подсказки!

IPR ★★★★★ ()
Ответ на: комментарий от IPR

так нормально, но не питонически. тебе этот класс вообще не нужен, если не стоит задача всегда логировать открытие и закрытие файла, из какого бы места в коде этот файл ни был открыт

eternal_sorrow ★★★★★ ()
Ответ на: комментарий от eternal_sorrow

так нормально, но не питонически. тебе этот класс вообще не нужен, если не стоит задача всегда логировать открытие и закрытие файла, из какого бы места в коде этот файл ни был открыт

Я понимаю, это не настоящий код, это я пытался разобраться с классами и их методами, наглядно увидеть работу __init__ и __del__ etc. Пока путаюсь и не могу до конца понять смысл self, как-то в мозгу пораженном /bin/sh не очень это всё укладывается. Пока две самые непонятные темы это self и инкапсуляция.

IPR ★★★★★ ()
Ответ на: комментарий от IPR

self это ссылка на текущий инстанс класса. инкапсуляции в питоне как таковой нет. она держится на честном слове разработчика и возможно на линтере, который будет разработчику мозг компостировать. но при желании можно вызывать напрямую и _приватные() методы и свойства и даже особые __магические__(). но не надо так делать.

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 2)
Ответ на: комментарий от eternal_sorrow

Т.е. все обращения к переменным, кот. используются в экземпляре класса я должен вызывать через self, верно?
Просто вот этот код:

class file:
	def __init__(self, filename):
		self.filename = filename
		self.openfile = open(self.filename, 'r')

работает также, как и этот:
class file:
	def __init__(self, filename):
		#self.filename = filename
		self.openfile = open(filename, 'r')

где я к filename обращаюсь без self, хотя в примерах, кот. я видел обращались именно через self.

// Ещё раз спасибо за разъяснения.

IPR ★★★★★ ()
Ответ на: комментарий от IPR

Т.е. все обращения к переменным, кот. используются в экземпляре класса я должен вызывать через self, верно?

не к переменным, а к свойствам объекта. если тебе filename не надо хранить в объекте (чтобы потом позже к нему обратиться), то работай с ним как с обычной переменной

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 2)
Ответ на: комментарий от IPR
	def read(filename):
		for filestr in filename:
			print(filestr)

только сейчас обратил внимание. первый аргумент должен быть self, ты по сути тут пытаешься итерироваться по self

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 1)
Ответ на: комментарий от eternal_sorrow

С self не работает:

	def read(self, filename):
		self.filename = filename
		for filestr in self.filename:
			print(filestr)

Traceback (most recent call last):
  File "123.py", line 18, in <module>
    file.read(myfile.openfile)
TypeError: read() missing 1 required positional argument: 'filename'
File /etc/rc.conf  closes.

IPR ★★★★★ ()
Ответ на: комментарий от IPR

def read(self, filename):

зачем вообще нужен аргумент filename?

self.filename = filename

зачем ты это делаешь?

self.openfile = open(filename, 'r')

вот ты сохранил открытый файл в аттрибут. почему ты его дальше не используешь? читай строки из него

eternal_sorrow ★★★★★ ()
Ответ на: комментарий от eternal_sorrow
class file:
	def __init__(self, filename):
		self.filename = filename
		self.openfile = open(self.filename, 'r')
	def __del__(self):
		print('File', self.filename, 'closes')		
	def read(self):
		with self.openfile as file:
			print(file.read())
	

my = file(r'/etc/rc.conf')
print(my.filename, 'is',  my.openfile)
my.read()


Так лучше? Если да, то благодаря тебе кое-что понял, спасибо!

IPR ★★★★★ ()
Ответ на: комментарий от IPR

https://docs.python.org/3/reference/datamodel.html#object.__del__

«It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.»

«Note del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x’s reference count reaches zero.»

«CPython implementation detail: It is possible for a reference cycle to prevent the reference count of an object from going to zero.»

Согласно доке, это НЕ деструктор как он понимается, например, в С++. Кстати, раз исключения в __del__ игнорируются, это открывает возможность для многих незаметных багов, видимых при использовании __enter__/__exit__.

Алсо https://stackoverflow.com/a/2452895.

lu4nik ★★★ ()
Последнее исправление: lu4nik (всего исправлений: 1)