Read why programming languages have the structures what they have. Challenge them. They are full with mistakes. One infamous example is the "final" keyword in Java. Or for example, Python's list comprehension. There are better solutions to these. Be annoyed by them, and search for solutions. Read also about why these mistakes were made. Figure out your own version which doesn't have any of the known mistakes and problems.
The same with "principles" or rule of thumbs. Read about the reasons, and break them when the reasons cannot be applied.
And use a ton of programming languages and frameworks. And not just Hello World levels, but really dig deep them for months. Reach their limits, and ask the question, why those limits are there. As you encounter more and more, you will be able to reach those limits quicker and quicker.
One very good language for this, I think, is TypeScript. Compared to most other languages its type inference is magic. Ask why. The good thing of it is that its documentation contains why other languages cannot do the same. Its inference routinely breaks with edge cases, and they are well documented.
Also Effective C++ and Effective Modern C++ were my eye openers more than a decade ago for me. I can recommend them for these purposes. They definitely helped me to loose my "junior" flavor. They explain quite well the reasons as far as I remember.
`[state_dict.values() for mat to mat2 for row for p to p/2]`
Or similar, where data flow is 1->2->f(2)->3->4->f(4). Where right now it is this lovely mess with one more repeating term:
`[p / 2 for mat in state_dict.values() for row in (mat 2) for p in row]`
Where the flow is f(4)->2->1->3->f(2)->4->3
This is not just a Python list comprehension problem obviously. The simple for… in… has a similar problem. It’s only better, because the first term `p/2` is at the end.
List comprehensions are inverted (syntax-wise) compared to regular program flow, but that is pretty easy to learn and adapt to (and is, imo, much better than "a = b if x else c").
items = [ this for iterator ]
because you immediatelly see what type of data ends up in `items`. Syntax inversion overhead is very much paid for with this benefit IMO.Again the bigger problem is the 'if', including in comprehensions.
But that's simply not true at all.
Let's start with token by token:
items
You have no clue what type of data ends up in `items`, or that something should end up in `items` at all. This is obvious. items =
You have no clue what type of data ends up in `items`. You just know now, that something will end up there. items = [
You only know that a list will be in `items`. Not what will be in the list. items = [ this
You only know that a list will be in `items`. Not what will be in the list. You have no clue what is `this`. items = [ this for
You only know that a list will be in `items`. Not what will be in the list. You have no clue what is `this`. items = [ this for iterator
You only know that a list will be in `items`. Not what will be in the list. You have no clue what is `this`. You cannot have, or you break the right to left propagation with nested cases, against what you have with this simple example of yours. items = [ this for iterator ]
This is the only time when you know what type is `items` or `this`.Also `this` is a useless identifier, if you cannot transform or filter in your list comprehension. I don't like mine either that it contains pointless words...
Don't get me wrong, your example is clearly a right to left data flow. Which is not inherently bad, because `items` and `this` are new identifiers, which won't figure out by IDEs, so it doesn't matter.
Also, in my example of Python code (not my version of it, but the valid Python code), there is no need to have `if` at all to break intellisense, or break either left to right or right to left data flow several times inside the list comprehension.
Compare it to
family_adults = [ person for person in family_members if person.age >= 18 ]
Code should be as readable as possible by default: I'd argue you do not even need types in code with good naming, though they do prove their worth in codebases being evolved for a long time and many people.