DOC: clarify difference between G.nodes/G.nodes() and G.edges/G.edges() in tutorial#8300
Conversation
…() in Graph docstring Added a note to the Graph class docstring explaining that: - G.nodes is a NodeView (set-like) - G.nodes() returns a NodeDataView (with optional data/default arguments) - G.edges is an EdgeView (set-like) - G.edges() returns an EdgeDataView (with optional nbunch/data/default arguments) This helps new users distinguish between attribute views and their callable forms, as suggested in networkx#8253.
|
Apologies if this is a naive question, but I noticed that the ci/circleci: documentation job failed on my PR. I came across issue #8297 which mentions intermittent doc build failures (possibly related to the new iplotx example). Could you please confirm if this failure is related to my changes, or if it’s part of the ongoing issue? I just want to make sure I’m not overlooking something on my end. |
|
There are indeed problems with NX's circleci setup at the moment. The root cause has not yet been chased down but for now it's safe to simply ignore the red-x's from circleci. Sorry for the inconvenience! |
|
Got it — thanks a lot for clarifying! |
rossbar
left a comment
There was a problem hiding this comment.
Thanks for the proposal @Aka2210 !
I think this is on the right track but there's additional information + other perspectives that I think should be emphasized.
IMO one of the most important aspects is that the .nodes/.edges properties can be used to set node and edge attributes respectively. In principle, both *View and *DataView classes support attribute assignment, but in practice the convention (at least for me) is to use .nodes/.edges with attribute-like (i.e. no parentheses) access patterns for setting individual node/edge attributes, and .nodes()/.edges()` (i.e. property-like access patterns) for reporting. In other words --- attribute-like for setting, property/method-like for getting. Additionally, IMO the exact type of the return object (i.e. *View or *DataView) is very often an implementation detail that doesn't carry a whole lot of concrete meaning for typical users (aside from pointing them towards which docs to continue reading for more info). I'd prefer to de-emphasize the type of the returned objects and instead highlight conventional/recommended access patterns.
Related to the above - there is one key (ha!) difference in how multigraphs behave with .edges vs. edges() --- the former includes the key by default whereas the latter doesn't. This too agrees with the "attr-for-setting, "property-for-getting" pattern that I personally think is the bit most worth emphasizing. This also means that the Graph docstring is probably not the right place for this to live - I'd vote somewhere more central/general such as the introduction or tutorial.
|
Got it — thanks for the clarification! |
… in tutorial This adds a new subsection to the tutorial explaining the distinction between attribute-like (`G.nodes`, `G.edges`) and method-like (`G.nodes()`, `G.edges()`) graph views. It also adds a concise note and example for multigraphs(`MultiGraph`, `MultiDiGraph`)
|
I’ve made the suggested changes — de-emphasized the *View / *DataView type details, I also placed the new subsection right after “Examining elements of a graph”, |
doc/tutorial.md
Outdated
| list(G.nodes), list(G.nodes()), list(G.edges), list(G.edges()) | ||
| ``` | ||
|
|
||
| Although they behave identically, each form is typically used differently in practice. |
There was a problem hiding this comment.
But G.edges does not behave identically to G.edges().
When iterated over, G.edges yields 3-tuples (u, v, edge_key) for multigraphs and 2-tuples (u, v) for graphs.
G.edges() yields 2-tuples for each edge for all graph objects. So, if you want to write code that will work for all graph objects, G.edges() helps avoide checking for multigraphs when you will ignore the edge key anyway.
But this is probably way too much for a tutorial. Can we qualify the "identically" somehow to make it accurate?
Maybe basically the same, or identically for Graphs?
There was a problem hiding this comment.
Thanks for pointing that out!
I’ve revised the sentence to “Although they behave identically for the simple graphs…” to clarify that the statement only applies to Graph/DiGraph, not to MultiGraph objects.
|
I merged the MultiGraph initialization and the first list(MG.edges) call into a single code cell to avoid the stray output (1) that appeared in the rendered tutorial. Before: (Rendered output included an unintended 1 because add_edge returns the edge key.) After: Combining them in one cell ensures that the last executed expression is the intended example output (list(MG.edges)) rather than the return value of add_edge. |
rossbar
left a comment
There was a problem hiding this comment.
Thanks for the updates @Aka2210 - after reading through this and the previous discussion, the impression I was left with was this was a lot of nitty-gritty detail for the tutorial.
I've left a concrete suggestion in the inline review comments that IMO at least is a slightly higher-level look that flows with the tutorial. Now, it doesn't cover everything - in fact the lack of any explicit mention of views is intentional - but I think it may fit with the tutorial better, while some of the finer-grained detail could be added later/elsewhere, e.g. the documentation on view objects. WDYT?
Co-authored-by: Ross Barnowski <rossbar@caltech.edu>
Thanks again for the detailed explanation and suggested wording! I’ve updated the tutorial section to match your “Access patterns” version and verified that the I've also fixed the style issues noted in CI. |
Added a note to the Graph class docstring explaining that:
This helps new users distinguish between attribute views and their callable forms, as suggested in #8253.
Related to #8253