Slicer script repository: module_name.logic vs slicer.module_name.widgetRepresentation().self().logic() vs slicer.modules.logic()

Slicer API explanation:

Hello all I have been trying to understand the Slicer API and I am having a lot of trouble understanding exactly how the ecosystem works.

I want to specifically understand how the logic aspect of modules works in Slicer.
In the script repository, I have seen the logic() incorporated in 3 different ways:

Example 1:
SegmentStatistics is often imported and accessed like this:

import SegmentStatistics
segStatLogic = SegmentStatistics.SegmentStatisticsLogic().Compute

However, I have seen other modules logic method accessed this way:
Example 2:

slicer.modules.markups.logic().ExportControlPointsToCSV(markupsNode, "/path/to/MyControlPoints.csv")

so how come I cannot access ComputeStatistics() like this with the code below?

slicer.modules.segmentstatistics.logic()

I can also see that i can access ComputeStatistics() if I access it via widgetRepresentation
Example 3:

slicer.modules.segmentstatistics.widgetRepresentation().self().logic.computeStatistics()

Overall what is the difference between the 3 ways? Which one the correct way and the theory behind the code. I understand that widgetRepresentation handles the gui and by qt, but I cant wrap my head behind Slicer design pattern especially with all the vtk/qt bindings.
Finally, whats the intuition behind accessing the self() in widget-representation from Slicer? Are the modules not already instantiated when slicer is loaded? I have seen this often used with segment editor example to access the different modes like pen, eraser and etc.

Thank you
Tas

Good question! To facilitate dynamic reloading, we don’t use slicer.modules.segmentstatistics.logic() in scripted modules. Instead, we usually let the widget create and own the logic in Python scripted modules. See more information in slide 11 in the STC-DEV-101: Slicer Scripting and Module Development tutorial.

Thank you @Iassoan
Just to make sure, if i want to access all the attributes and instance of logic modules, you always recommend accessing from the widget itself via WidgetRepresenation() instead of accessing it with slicer.modules.segmentstatistics ?

For a scripted module, you cannot reach module logic via the module object directly like slicer.modules.segmentstatistics.logic(), as this returns a placeholder object, not the real module logic object (that the widget instantiates and owns). This may change in the future, so the safest and most future-proof method is to use slicer.util.getModuleLogic() function.

A small addition: segStatLogic = SegmentStatistics.SegmentStatisticsLogic() instantiates a new logic class. It does not access the logic class of the built-in module. This type of access is useful if you just want a temporary logic class that you use for some computations without affecting the state of the “Segment Statistics” module. You need to ensure that the logic is properly initialized. In many cases, no initialization is needed, but you can double-check by looking at how the module widget class does it.