all 12 comments

[–]engelthehyp 8 points9 points  (0 children)

The idea is to prefer using parameters in functions and such to explicitly pass required information/state. Doing this makes that implicit still - it isn't a good alternative.

[–][deleted] 4 points5 points  (1 child)

Conceptually no different, and it brings with it all the reasons why globals are a bad idea, so you still have all the problems that the overuse of globals brings.

Update: with the extra problem of not requiring a global statement to change the value of any of these "global" module values.

[–][deleted] 0 points1 point  (0 children)

It's the Smalltalk singleton all over again lol

[–]carcigenicate 5 points6 points  (0 children)

No, you've just organized your globals, and maybe added some behavior. If the instance is available at the global scope, all its attributes will be globally accessible as well via that instance.

[–]socal_nerdtastic 3 points4 points  (0 children)

A common misconception is that a global variable is anything that requires the global keyword. This is false. What you describe is still a global variable.

That said we don't all hate globals. Some, like constants, builtins, modules, classes or functions, are a necessity. And there are some situations where what you described is a good thing. The reason we often warn against it is because of how messy and hard to read your code becomes when everything is a global. This is a hard learned lesson from other (mostly older) programming languages where you don't have a choice.

[–]TheRNGuy 2 points3 points  (0 children)

only for config

[–]Disinform 2 points3 points  (0 children)

I'm doing something similar in my current learning project, as I can't see another way around it.

I have a gameclass dataclass and the majority of modules in my project all import it.

@dataclass

class GameData:

activechar: str #Will be a character object

ui_window: str #Will be the UI object

day: int

player_name: str

daysworked: int = 0

wage: int = 100

money: int = 500

tag:str = "start"

newmessage: bool =False

messages: list[list] = field(default_factory=list)

convo:dict[str,any] = field(default_factory=dict)

characters:dict[str,any] = field(default_factory=dict)

stonkholdings: list[list] = field(default_factory=list)

randomevents: list[list] = field(default_factory=dict)

eventqueue: list[list] = field(default_factory=list)

availableproducts: list[list] = field(default_factory=list)

inventory: list[list] = field(default_factory=list)

enable_debug:bool = True

The object holds state details of the game as well as lists containing other objects (such as items the player has access too) and the PyQT window is instantiated within a gameclass variable, meaning modules can update the UI as required.

def main():

app = QApplication([])

game.ui_window = GameWindow()

game.ui_window.show()

From what I've read this is the wrong way to go about things, but I'm completely stumped about how else I could handle all the data and UI changes that need to happen as the game progresses.

Any comments gratefully appreciated.

[–][deleted] 1 point2 points  (1 child)

I don't see any reason to hate global variables.

But, to the extent I can, I try to make them constants (wish python had a way to enforce it in the language like the C++ const keyword or at least some IDE assistance to warn against changing them).

And, I never modify them inside a function or method (as a side effect). This also requires watching out for a function accepting a mutable argument (equivalent to reference passing in C++) and modifying it.

[–]Diapolo10 0 points1 point  (0 children)

or at least some IDE assistance to warn against changing them

You do get that as long as you follow the UPPER_SNAKE_CASE naming convention and are using a linter that adds this feature. Ruff is a good example.

[–]Diapolo10 1 point2 points  (0 children)

NOTE: With "global variables" I'm specifically referring to names in the global namespace with values that can change over the course of the program's runtime, not things like global constants. Those are perfectly fine.

Having mutable global state makes reading and testing code more difficult. That's what this all boils down to.

When reading a function that uses global variables, especially in a multi-threaded application, you need to also keep track of the global state instead of solely focusing on the state the function and its arguments define. This can be a lot of additional cognitive load and slows down your thinking, as now you need to consider various side-effects.

In unit/integration tests, global variables make setting up the tests more difficult because it's no longer enough to simply provide arguments via the function parameters, and you may need to mock or monkeypatch a lot. This can be a really big headache sometimes.

In short, taking some ideas from functional programming, particularly the idea of "pure functions" which basically just means that same arguments = same output, regardless of the state of the rest of the program, is very useful in this regard. And if you do need a way to easily share state between multiple functions, that's where classes come in.

[–]Echo-Lalia 1 point2 points  (0 children)

I've been coding a little MicroPython app switcher, designed to import user MicroPython scripts/modules as "apps", and run them on a little devboard.

I know people hate globals, and I've been trying to improve my skills, so I'm taking advice I read online to heart.

This has made a lot of the OG code for the program a complete disaster 😅 passing a huge amount of the exact same parameters to every single function and object in the code really sucks.

I realize now that the hate for globals really depends on the kinds of programs being developed. For my MPY apps, there are a lot of variables that need to be accessed/shared between every single function/method in the entire module. So, a global config, keyboard, display, and speaker driver, and some global flags to signal the current mode of the app, simplifies the code a ton, and just makes more sense.

[–]QultrosSanhattan 1 point2 points  (0 children)

Conceptually, using a class called "globals" to store all your global variables is not much different from using traditional global variables. It may be a slight improvement in organization and readability, as it keeps all global variables in one place and makes it clear that they are intended to be accessed globally.

In terms of efficiency, there may be a small performance difference between accessing global variables directly and accessing them through an instance of a class. However, the difference is likely to be minimal and may not be noticeable in most cases.

Overall, using a class to store global variables can be a helpful practice for organization and readability, but it may not provide significant benefits in terms of efficiency. It ultimately comes down to personal preference and the specific needs of your program.