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

playing with color in powershell python

JavaScript Ascii animation with while loops and console.log

playing with trigonometry sin in pygame