下面是我期望执行的模块。
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
答案 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
或者只需在脚本末尾添加相同的行即可自动触发它。