2

We all know the following code is bad because of the use of global variable:

a = 3.14

def inc():
  global a
  a += 1

inc()
b = a

It is so bad that in python one has to intentionally declare global to "break" the ban. But in the name of OO, how come the code below is justified:

class A:
  def __init__(self):
    self.a = 3.14

  def inc(self):
    self.a += 1

A_instance = A()
A_instance.inc()
b = A_instance.a

Within the scope of the class instance, isn't the member variable simply the same as global variable? "self" basically grant unrestricted access to all variable to any member function regardless the necessity, making it very stateful, prone to mistakes, and difficult to read (basically all the bad traits of global var)?

We also know the clean and better way is:

def inc(_a):
  return _a+1

a = 3.14
b = inc(a)

So that the function inc() is at least stateless and has a predictable behavior. What's the best practice to use class and make it as stateless as possible? Or by definition, is OO always stateful and one has to go functional programming without classes at all? I also heard people say @static methods are stupid, break encapsulation, and should be avoided...

lunchbreak
  • 131
  • 1
  • 6
  • If you make your classes small and focused, the problems you mention probably do not occur. Classes make sense when there is some state to store in each instance, and sometimes a bit of shared state between all instances (stored in the class), Also when you want to group related methods. If there is no state or just a method, you are better with functions. – progmatico Jan 05 '20 at 20:19

2 Answers2

2

It comes down to whether you need to save state or not. If you do need to save state then it needs to be saved somewhere. Classes allow you to save state alongside the methods that operate on that state and enforce a class-local scope that is less error prone than storing state within a global scope.

As a concrete example, I recently implemented a Tree class for a forest simulation. One of the variables that is important to associate with a tree is its age. Where should the age of the tree be stored? It makes more sense to store it within the Tree class than outside of it.

If protecting the age of the tree from being manipulated by code outside of the class was important, I could have used Python's excellent @property syntax for that purpose, or less directly through the Python _varname variable naming convention.

Within the class, yes, the age of the tree is visible to the other methods, but that is a relatively narrow scope, certainly not akin to a global scope if your classes are written with a single responsibility in accord with SOLID object oriented design principles.

rhurwitz
  • 2,557
  • 2
  • 10
  • 18
1

Now the reason why the concept of self is not considered bad practice is that if you are to create a class everything within that class ought to be related in one way or another which usually is class attributes that class instances are initialized with via the __init__ dunder method. Basically, if you are going to create a class then there ought to be a link of some sort, a class global if you will which are class attributes

usually the idea that creating classes with just self like this

class A:
  def __init__(self):
    self.a = 3.14

  def inc(self):
    self.a += 1

is not something that is often seen, rather people chose the way which is generally in many opinions considered cleaner and more predictable

class A:
  def __init__(self, a):
    self.a = a

  def inc(self):
    self.a += 1

With this when you declare class instances you have more control of what is actually there, so class instances are hence declared with variables

A_instance = A(3.14)   #a will be 3.14 in this instance
B_instance = A(3.142)  #a will be 3.142 in this instance

and so on. Class variables that are declared outside the __init__ function but inside the class, itself are considerably different and that would look like

class A:
  a = 22/7

  def __init__(self):
      ....

with this a would be a global variable throughout the class(not the instance but the class itself). So what does that look like

class A:
    a = 22/7

    def __init__(self, item):
        self.item = item

A_instance = A(3.14)
B_instance = A(5)

Although A_instance and B_instance are completely different and A_instance.item will equal 3.14 while B_instance.item will equal 5 both of these will have the class variable a as 22/7

A_instance.a  #this will return a value of 22/7
B_instance.a  #this will also return a value of 22/7

So the class variable remains consistent/the same throughout all instances of that class unless it is actively altered.

Point is if you are going to create a class it should have specific variables that are intertwined with each other and affect all a particular group of functions, these are the functions you make into class methods as such the use of self within classes is not wrong if used properly

maestro.inc
  • 796
  • 1
  • 6
  • 13