在Python脚本中未调用析构函数

时间:2018-09-04 16:18:32

标签: python python-3.x destructor

下面是我期望执行的模块。

class Z():
    def Y(self):
        return
    def __del__(self):
        print('Z deleted.')
def W(v):
    class Form:
        def X(self):
            #v.Y()
            return  
    return
def U():
    t = Z()
    W(t)
U()

运行上述模块会产生以下输出

Z deleted.

当我删除如下所示的注释时,不会产生任何输出。

class Z():
    def Y(self):
        return
    def __del__(self):
        print('Z deleted.')
def W(v):
    class Form:
        def X(self):
            v.Y()
            return  
    return
def U():
    t = Z()
    W(t)
U()

为什么不调用析构函数?

我正在以下实用程序中运行此模块。操作系统为Windows 10 Pro版本1803,操作系统内部版本17134.165

capture

1 个答案:

答案 0 :(得分:1)

您编写的脚本以一种不太明显的方式创建了一个参考循环。非显而易见的循环是 all public class PaginatedList<T> : List<T> { public int PageIndex { get; private set; } public int TotalPages { get; private set; } public PaginatedList(List<T> items, int count, int pageIndex, int pageSize) { PageIndex = pageIndex; TotalPages = (int)Math.Ceiling(count / (double)pageSize); this.AddRange(items); } public bool HasPreviousPage { get { return (PageIndex > 1); } } public bool HasNextPage { get { return (PageIndex < TotalPages); } } public static async Task<PaginatedList<T>> CreateAsync( IQueryable<T> source, int pageIndex, int pageSize) { var count = await source.CountAsync(); var items = await source.Skip( (pageIndex - 1) * pageSize) .Take(pageSize).ToListAsync(); return new PaginatedList<T>(items, count, pageIndex, pageSize); } } 声明固有循环的结果,因此public class IndexModel : PageModel { private readonly AthlosifyWebArchery.Data.ApplicationDbContext _context; public IndexModel(AthlosifyWebArchery.Data.ApplicationDbContext context) { _context = context; } public string AtheleteNameSort { get; set; } public string GenderSort { get; set; } public string TotalRankingScoreSort { get; set; } public string CurrentSort { get; set; } public string CurrentFilter { get; set; } public class TournamentAtheleteViewModel { public string AtheleteName { get; set; } public string Gender { get; set; } public string Contingent { get; set; } public double TotalRankingScore { get; set; } } [BindProperty] public IList<TournamentAtheleteViewModel> TournamentAtheletes { get;set; } public async Task OnGetAsync(string sortOrder, string currentFilter, string searchString, int? pageIndex) { CurrentSort = sortOrder; AtheleteNameSort = String.IsNullOrEmpty(sortOrder) ? "atheletename_desc" : ""; GenderSort = sortOrder == "gender" ? "gender_desc" : "gender"; TotalRankingScoreSort = sortOrder == "totalrankingscore" ? "totalrankingscore_desc" : "totalrankingscore"; if (searchString != null) { pageIndex = 1; } else { searchString = currentFilter; } CurrentFilter = searchString; IQueryable<TournamentAtheleteViewModel> atheletes = _context.TournamentBatchItem .GroupBy(t => new { t.AtheleteName, t.Gender, t.Contingent }) .Select(t => new TournamentAtheleteViewModel { AtheleteName = t.Key.AtheleteName, Gender = t.Key.Gender, Contingent = t.Key.Contingent, TotalRankingScore = t.Sum(i => i.RankingScore) }); if (!String.IsNullOrEmpty(searchString)) { atheletes = atheletes.Where(s => s.AtheleteName.Contains(searchString) || s.Contingent.Contains(searchString)); } switch (sortOrder) { case "atheletename_desc": atheletes = atheletes.OrderByDescending(s => s.AtheleteName); break; case "gender": atheletes = atheletes.OrderBy(s => s.Gender); break; case "gender_desc": atheletes = atheletes.OrderByDescending(s => s.Gender); break; case "totalrankingscore": atheletes = atheletes.OrderByDescending(s => s.TotalRankingScore); break; case "totalrankingscore_desc": atheletes = atheletes.OrderBy(s => s.TotalRankingScore); break; default: atheletes = atheletes.OrderBy(s => s.AtheleteName); break; } int pageSize = 3; //TournamentAtheletes = await atheletes.AsNoTracking().ToListAsync(); TournamentAtheletes = await PaginatedList<TournamentAtheleteViewModel>.CreateAsync( atheletes.AsNoTracking(), pageIndex ?? 1, pageSize); } } class声明的简单存在意味着将会有一些循环垃圾。我不确定这是否是所有Python解释器的必要条件,但对于CPython的实现(从我检查过的至少2.7到3.6,绝对是正确的)来说确实如此。

class实例中循环并触发您观察到的行为的是,在声明时,您将W(是对Z实例的引用)与闭包作用域一起使用v作为Z声明的一部分。闭合范围意味着只要存在对调用Form.x定义的class,闭合变量class Form(最终为W的实例)将保持活动状态

当您使用IDLE运行模块时,它会运行该模块,并在执行模块中的代码后将您转储至交互式提示,但Python仍在运行,因此它不会对全局变量或立即运行循环GC。 v的实例最终将被清除(至少在CPython 3.4+上),但是循环GC通常仅在相当数量的分配之后才运行,而没有匹配的解除分配(我的默认值为700解释器,尽管这是实现细节)。但是该收集可能要花费任意长时间(在解释器退出之前进行了最后的循环清理,但除此之外,没有任何保证)。

通过注释引用Z的行,您不再需要关闭Z,因此循环v不再使v保持活动,并且{{ 1}}会在最后一个引用消失时迅速清除(无论如何,在CPython引用的计数解释器上都没有;对Jython,PyPy,IronPython等没有保证)。

如果要强制执行清理,请在运行模块后在所得的交互式shell中运行以下命令以强制进行第0代清理:

class

或者只需在脚本末尾添加相同的行即可自动触发它。