mabel.ink

Six Python gotchas from a PowerShell habit

October 7, 2026

I write a lot of PowerShell and I took an intro C# course earlier this year, so Python is my third language of the sort, and the first one I am learning by just building small things. I am about a week in. This is not a tutorial, and I am not claiming to know Python yet. It is a record of what I built and the mistakes that taught me something.

What I built

The first project was Rock Paper Scissors, best of five. It validates the input (lowercase and trimmed, then checked against the three allowed choices), numbers the rounds, announces an overall winner, and asks whether to play again until it gets a clear yes or no. The scoring does not depend on the message text, because check_win returns "win", "lose" or "tie" and the game logic reads that.

The second project is an expense tracker, and it is not finished. I am building it in stages. So far there is a menu loop (add, check, exit) and a way to add an expense with an amount, a description and a date. The date parser accepts written-out months only, like jan 23 2025, so a date can never be ambiguous between day and month. Viewing expenses is still a stub, and bad amounts can still crash it. Summaries and saving to a file are on the list.

What went wrong

Each of these cost me some time. I wrote them down so they would stick.

1. x != "a" or "b" is always true. Python reads that as (x != "a") or "b", and a non-empty string counts as true, so the whole thing is true no matter what x is. The fix is x not in ["a", "b"]. PowerShell has the same trap: $x -ne "a" -or "b" is also always true, because the non-empty string "b" is truthy there too. The fix there is -notin.

2. try/except does not retry anything. I wrapped the input in a try and expected it to ask again after a failure. It does not. It catches the error and moves on. The thing that re-asks is a loop around it.

3. Local variables reset on every call. A round counter I kept inside a function started over each time the function ran, which was a bug. But the same behavior is useful in play_game(), because the scores reset to zero for each new game without any extra code.

4. return a, b returns a tuple, and in checks whole items. Checking "roc" in ("rock", "paper") is false, because it compares against each full item. The same check against the string "rock" is true, because then it is a substring search. Same keyword, different meaning depending on what is on the right.

5. Do not name variables after built-ins. Calling a variable round, list or sum works fine until you need the real one, and then it is quietly gone.

6. exit without parentheses does nothing. Python does not complain. The name just refers to the function, and without () it is never called. It looks like a command and behaves like a variable.

What is next

The open items on my list are finishing the expense tracker stage by stage: catch bad amounts with try/except inside a loop, then totals, a total per category and the biggest expense, then saving and loading with the json module. The stretch goals are deleting an expense, filtering by category and working with dates through datetime.

Takeaway

It is early days. What stands out so far is how often the interpreter lets a mistake through without complaint, where the C# compiler would have stopped me. That makes the small habits matter: test the odd inputs, and write down the surprises.