class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
上面的 __del__(self)
因AttributeError异常而失败。当__del__()
被调用时,我理解Python doesn't guarantee是否存在“全局变量”(此上下文中的成员数据?)。如果是这种情况,这就是异常的原因,我该如何确保对象正确破坏?
答案 0 :(得分:545)
我建议使用Python的with
语句来管理需要清理的资源。使用显式close()
语句的问题是,您必须担心人们忘记完全调用它或忘记将其放在finally
块中以防止在发生异常时资源泄漏。 / p>
要使用with
语句,请使用以下方法创建一个类:
def __enter__(self)
def __exit__(self, exc_type, exc_value, traceback)
在上面的示例中,您将使用
class Package:
def __init__(self):
self.files = []
def __enter__(self):
return self
# ...
def __exit__(self, exc_type, exc_value, traceback):
for file in self.files:
os.unlink(file)
然后,当有人想要使用你的课程时,他们会做以下事情:
with Package() as package_obj:
# use package_obj
变量package_obj将是Package类型的实例(它是__enter__
方法返回的值)。无论是否发生异常,都会自动调用__exit__
方法。
您甚至可以将此方法更进一步。在上面的示例中,有人仍然可以使用其构造函数实例化Package而不使用with
子句。你不希望这种情况发生。您可以通过创建定义__enter__
和__exit__
方法的PackageResource类来解决此问题。然后,将严格在__enter__
方法内定义Package类并返回。这样,调用者永远不会在不使用with
语句的情况下实例化Package类:
class PackageResource:
def __enter__(self):
class Package:
...
self.package_obj = Package()
return self.package_obj
def __exit__(self, exc_type, exc_value, traceback):
self.package_obj.cleanup()
您可以按如下方式使用:
with PackageResource() as package_obj:
# use package_obj
答案 1 :(得分:31)
标准方法是使用atexit.register
:
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
#include <stdlib.h> // added for exit() function
void fail(char *msg) {
endwin();
puts(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
/* Commandline argument currently unused */
(void) argc;
(void) argv;
int i, c;
(void) c; // c is currently unused
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
/* Test to see if terminal has colors */
if (has_colors() == false) {
fail("Colors unavailable\n");
}
if (start_color() != OK) {
fail("Unable to start colors\n");
}
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i = 0; i < 5; i++) {
if(i == zaznacz)
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
if (i == zaznacz)
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz < 0) zaznacz = 0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz > 4) zaznacz = 4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("\nWybrano:%s", opcje[zaznacz]);
refresh();
/* Wait for user to press enter to exit */
getch();
/* Need to cleanup before exit */
endwin();
return 0;
}
但是你应该记住,这将保留所有创建的# package.py
import atexit
import os
class Package:
def __init__(self):
self.files = []
atexit.register(self.cleanup)
def cleanup(self):
print("Running cleanup...")
for file in self.files:
print("Unlinking file: {}".format(file))
# os.unlink(file)
实例,直到Python终止。
使用上面的代码进行演示,保存为 package.py :
Package
答案 2 :(得分:26)
作为Clint's answer的附录,您可以使用contextlib.contextmanager
简化PackageResource
:
@contextlib.contextmanager
def packageResource():
class Package:
...
package = Package()
yield package
package.cleanup()
或者,尽管可能不是Pythonic,但您可以覆盖Package.__new__
:
class Package(object):
def __new__(cls, *args, **kwargs):
@contextlib.contextmanager
def packageResource():
# adapt arguments if superclass takes some!
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
yield package
package.cleanup()
def __init__(self, *args, **kwargs):
...
并简单地使用with Package(...) as package
。
为了简化操作,请将清理函数命名为close
并使用contextlib.closing
,在这种情况下,您可以通过Package
使用未修改的with contextlib.closing(Package(...))
类或覆盖其{ {1}}到更简单的
__new__
这个构造函数是继承的,所以你可以简单地继承,例如
class Package(object):
def __new__(cls, *args, **kwargs):
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
return contextlib.closing(package)
答案 3 :(得分:16)
我不认为在调用__del__
之前删除实例成员是可能的。我的猜测是你的特定AttributeError的原因是在其他地方(也许你错误地删除了其他地方的self.file)。
但是,正如其他人指出的那样,你应该避免使用__del__
。这样做的主要原因是__del__
的实例不会被垃圾回收(只有当它们的引用次数达到0时才会被释放)。因此,如果您的实例涉及循环引用,则只要应用程序运行,它们就会存在内存中。 (虽然我可能会误解所有这些,但我必须再次阅读gc文档,但我确信它的工作方式是这样的。)
答案 4 :(得分:11)
答案 5 :(得分:8)
使用try / except语句包装析构函数,如果已经丢弃了全局变量,则不会抛出异常。
修改强>
试试这个:
from weakref import proxy
class MyList(list): pass
class Package:
def __init__(self):
self.__del__.im_func.files = MyList([1,2,3,4])
self.files = proxy(self.__del__.im_func.files)
def __del__(self):
print self.__del__.im_func.files
它会将文件列表填入 del 函数中,该函数在调用时保证存在。 weakref代理是为了防止Python,或者你自己以某种方式删除self.files变量(如果它被删除,那么它不会影响原始文件列表)。如果不是这种情况被删除,即使有更多的变量引用,那么你可以删除代理封装。
答案 6 :(得分:7)
答案 7 :(得分:5)
似乎这样做的惯用方法是提供close()
方法(或类似方法),并明确地称之为。
答案 8 :(得分:5)
这是一个最小的工作骨架:
class SkeletonFixture:
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def method(self):
pass
with SkeletonFixture() as fixture:
fixture.method()
重要提示:返回自我
如果你像我一样,忽略return self
部分(Clint Miller's correct answer),你会盯着这个废话:
Traceback (most recent call last):
File "tests/simplestpossible.py", line 17, in <module>
fixture.method()
AttributeError: 'NoneType' object has no attribute 'method'
我花了半天时间。希望它能帮助下一个人。
答案 9 :(得分:0)
atexit.register
是ostrakach's answer中已经提到的标准方法。
但是,必须注意,不能依赖删除对象的顺序,如下例所示。
import atexit
class A(object):
def __init__(self, val):
self.val = val
atexit.register(self.hello)
def hello(self):
print(self.val)
def hello2():
a = A(10)
hello2()
a = A(20)
在这里,顺序与创建对象的顺序相反,因为程序将输出显示为:
20
10
但是,在较大的程序中,当python的垃圾回收踢入对象时,该对象将超出其生命周期。