More on Python class, attributes and naming.

I visited the learncodethehardway forum, and someone had an excellent question.

There was a bit of an exercise where there was some confusion because of naming and where/what attributes were being set, what code was actually doing the work within the script.

I made these two tiny examples to show the user what was going on in the python class.

I wanted to put it in the blog for anyone else that may have some confusion with what is going on, because I know when I started, I sure did struggle with it.


I'll put comments in the code to explain:

~~~~~~~~~~~ Example 1 ~~~~~~~~~~
--start code block--


class Spam(object):
    def __init__(self):
         # this is an attribute.  It can be anything-- Even a class
         self.something = Hello()
         
    def spam(self):
         #self.something is an instance of Hello class
         self.something.hiYa()





class Hello(object):
   #This is a python object that holds a method

    def hiYa(self):
         print("Hello")



#Initiate Spam
sillyness = Spam()
#call Spam's function spam.
sillyness.spam()





--End code block--
~~~~~~~~~~~~~~~~~~~~~~~~~


Now  what does it look like if I peek into sillyness's dictionary where the namespaces are being held?

I add this line of code at the end of the above script:

print(sillyness.__dict__)

This line will print in my console:

{'something': <__main__.Hello object at 0x7f71869600b8>}


Here is a great article about class attributes:
https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide

Now for the next example,  this is where the trouble started.  It is a mock/ much simpler version of what was in the exercise code:


~~~~~~~~~~  Example 2 ~~~~~~~~~~~~
-- Start code block --


class Spam(object):

    def __init__(self, another_something):
        #This is where what the original value of the attribute
        # gets messy.  It's a chair, it's a seat, it's a recliner, it's nap_time.   
         self.another_something = another_something

    def spam(self):
         # notice you don't even have to use self but you should.
         #self.another_something.hiYa()  works here too.
         another_something.hiYa()

         # Initiated Hello now has Three names:
         # something, another_something, self.another_something.
         # This just feels messy and confusing.

       



class Hello(object):
    def hiYa(self):
         print("Hello")



something = Hello()
sillyness = Spam(something)
sillyness.spam()





--End code block--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The trouble is that in this one, you can't really see by just looking at that Spam class, that the Hello() class has been passed in under a new name.
It is being initiated at the bottom as 'something' ,
then in Spam being renamed.  When really it's still the active(initiated) Hello class the whole time.

Another issue:
Attributes are not the same as inheritance.  But they sure look the same.
The difference which I think caused some trouble is that when you make this sillyness = Spam(something)  it looks like inheritance,  but inheritance is done when you write the class itself.

If it was inheriting,  it would look more like this:

~~~~~~~~   Example 3 ~~~~~~~~~
--- Start code block ---
## ----  Inheritance ----  ##

class Hello(object):
    def __init__(self):
         self.message = "Howdy!"
         
    def hiYa(self):
         print(self.message)



class Spam(Hello):
    def __init__(self, something):
        Hello.__init__(self)
        self.something = something



    def spam(self):
        print(self.message, self.something)





something = "Nice to meet you."
sillyness = Spam(something)
sillyness.spam()

# Wow, even more variables and declarations to sort out!







--End code block ---

~~~~~~~~~~~~~~~~~~~~~

So in conclusion,
*  An attribute can be almost anything, even a class
*  An attribute is not 'inherited', it is created and placed in the class dictionary
   under a new name, if you've chosen to give it a new name,
   when the class is initialized:  self.something = something
* To trace down where the work is being done,  if your attribute does something
   like self.anAttribute.do_some_function(),  your attribute is a class calling a
   function from it's code.    The < . > is a call to a something.  It's telling the
   machine to look up this thing, and execute some action.

In the example, Spam's function 'spam()'  is just backpacking the Hello's function:  'Hiya()' and I think this over naming and variable creating is what really made it hard to follow (Example 2).

*I'm not sure what to call it when one class borrows a method of another class.
So I'm calling it backpacking.  I believe piggybacking is already used. *

As always, drop a comment, question, anything.
May the spam be ever in your flavor!
  






Comments

Popular posts from this blog

JavaScript Ascii animation with while loops and console.log

playing with trigonometry sin in pygame

JavaScript and a Matrix