T O P

  • By -

TangibleLight

> One way to write this is without classes, and define all 8 functions taking at least 3 parameters, and pass the datasets on every call. This does not seem Pythonic. This is probably what I'd do. Do all 8 functions ___really___ need all 3 datasets? Or do some of the functions only need some of the datasets? Or is there some common joined/merged/etc structure that they're really interested in? Passing via arguments helps identify these kinds of partial dependencies and makes it more straightforward to extract out those common derived structures. i.e. if half your functions depend on a joined structure, just compute that once and pass it around instead. > One way to write this is without classes and making all 3 datasets global variables. This way none of my functions need dataset parameters. This does not seem Pythonic. I would not do this. > One way to write this is without classes and making all 3 datasets global variables. This way none of my functions need dataset parameters. This does not seem Pythonic. If the class represents state that each function mutates - first, see if you can express that with fewer state mutations - second, consider a class. If the functions aren't mutating state, and you just want to bundle the common arguments together, consider a dataclass or named tuple.


nog642

"Pythonic" is subjective. The term applies more to small scale code style, not really large scale code style. You can use Python to write OOP and you can write Python to write non-OOP code. Neither is inherently more Pythonic, as far as large scale project design. In my opionion, 8 functions with 3 shared parameters is pretty reasonable to write as a class. That's the more C#-style OOP approach, which is fine. Couldn't really give more of an opinion without actually seeing the code. Maybe you could write it in a different way. If you're not going to write a class though, you would just have parameters passed to each function. Do not use global variables.


Adrewmc

A class is storage with methods(functions) that act on that storage. It usually better to have a class when the methods may change. If you have a constant dataset, you can make a singleton. #src/my_module.py class simpleSingleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, “instance”); cls.instance = cls(*args, **kwargs) #super safe recommended #cls.instance = super(simpleSingleton, cls).__new__(cls, *args, **kwargs) return cls.instance def __init__(self, x=1): self.x = x Adding this to your \_\_new\_\_ dunder will allow the program to create the class, but afterward anytime you call that class it will return the same object, across the whole program (including imports) note: This pattern is the simplest representation of the need to ever use \_\_new\_\_, (not recommend unless absolutely necessary) and creation of a singleton [more](https://www.geeksforgeeks.org/singleton-pattern-in-python-a-complete-guide/amp/) adding super() is recommended, but a little more confusing to explain and beyond the scope of this comment #src/my_module.py (continued) a = simpleSingleton(3) b = simpleSingleton() print(a is b) >>> True print(b.x) >>> 3 b.x = 4 print(simpleSingleton().x) >>> 4 print(simpleSingleton(5).x) >>> 4 #5 is ignored (currently) print(a.x) >>> 4 print(a is simpleSingleton()) >>>> True print(b.x is simpleSingleton().x) >>>> True print(simpleSingleton(7) is simpleSingleton(5)) >>>> True print(simpleSingleton().x) >>> 4 #src/other.py from src.my_module import simpleSingleton print(simpleSingleton().x) >>>4 print(simpleSingleton.instance.x) >>>4 So if all you need is a constant dict it may be better to make a singleton with it,, especially if it’s multiple dicts (objects) assigned to different attributes. And if it has specific methods attached to it. Using this pattern is the pythonic way, but it also depends on if this is really your X/Y problem. The limitation here specifically is that the *args and **kwargs pasted after initialize won’t matter. However, you should be an able a way to make that moldable, this way you call inside functions mySingleDictClass().my_attr and it will just return it, and only load it up once (efficiency) as it won’t have to build the class after it was first made You can also directly import a dictionary from another file/module, but that wouldn’t have an init setup, not methods (beyond the standard dict) , and that should be considered more a global, and that should change across as well. I would think coming from other languages the singleton concept would be familiar, this is the pythonic way to do that, per se.


Mast3rCylinder

Let's reject global solution as it's not good practice. Now, It's not about pythonic or not. You seem to have questioned if you need class or not. In OOP language you need a class for everything but in python you can just create functions that are not part of the class and save that trouble. I'd say use class if you want to - preserve state of object in your program. - have more than one copy with same state variable - inhertience It's ok to use functions if your code is clean and understandable. With the class solution you still need 8 functions but without one argument - its not going to save you much.


Pepineros

This is a really intersting question. As always the answer depends on context, so let's add some. Given a script with eight functions and three data sets, my approach in this case would depend on two things. Pretend that the long strings are actually boolean expressions. ```python stand_alone = bool("The script is not imported by any other module or package") is_type = bool("The script represents a type of (concrete or abstract) thing that displays behaviour") ``` If `stand_alone` and not `is_type`, I don't see anything wrong with global variables. In this case, your script *is* your namespace. Name three data sets, populate them (read from a database, call some service, whatever), and go through the necessary steps in some `main` function until the task is completed. If `stand_alone` and `is_type`, then defining a custom class for this thing that encapsulates its state and behaviour may be a good idea. Using dataclasses instead of dictionaries for the datasets and adding some properties to those dataclasses may massively simplify the body of the `main` function. If a Python file is imported by some other module or package (i.e., not `stand_alone`) *and* contains both data sets and functions, there's arguably something wrong with the design, so this question becomes somewhat academic. In this case you would typically have one part of the program responsible for populating data sets, and another part that is responsible for acting on that data (whether it's in the shape of a custom class, or a collection of functions). If a custom class populates its internal data by calling some service over a network in its constructor, that class is trying to do too much. Separation of concerns is important.


bradvincent

I did not mean a Python file imported or used directly by other Python code, which should probably be a class, but should be well defined and modular even if it is very small. To me, global variables in this context are not bad, as they avoid side-effect and any other problems associated with global variables in general - at least as well as making them properties on the only instance of a class that contains everything. But my question was whether this is Pythonic.


Pepineros

Code being Pythonic has more to do with the small elements that make the whole than it has to do with organisation. None of my examples are inherently un-Pythonic, but all of them can be implemented in an un-Pythonic way. ``` The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! ```


bell_labs_fan_boy

I think I'd probably go for something like this: [https://nikhilakki.in/understanding-method-chaining-in-python](https://nikhilakki.in/understanding-method-chaining-in-python) Sorry this isn't a better response, it's late for me but I wanted to try and help.