Jump to content

Can anybody help me understand classes in python

steelo
On 5/22/2020 at 3:58 PM, steelo said:

I've read that learning classes/oop isn't a necessity if all you write are shorter, simpler programs.

Even complex software doesn't require OOP. At work, much to the disapproval of some members xD, I write mostly functional javascript (nodejs), I do create classes for some helpers. 

 

 

Basically my point is use the best tool for the current job. 

                     ¸„»°'´¸„»°'´ Vorticalbox `'°«„¸`'°«„¸
`'°«„¸¸„»°'´¸„»°'´`'°«„¸Scientia Potentia est  ¸„»°'´`'°«„¸`'°«„¸¸„»°'´

Link to comment
Share on other sites

Link to post
Share on other sites

On 5/23/2020 at 8:24 PM, colonel_mortis said:

Classes aren't always the answer, and overusing them can make code messy, but if you have groups of data that belong to the same "thing", and that data is being passed around between functions, then classes are good.

 

You may find it difficult at first to decide what should be a class and what shouldn't - it's one of those things where you just need to try it a few times and it will start to feel more natural. The test for whether a class was a good addition is whether it makes the code easier to read or navigate.

So Im a little confused on how you would make a variable local...would you list them inside the function, rather than under main()? In your example, it looks like the variables are global.

Link to comment
Share on other sites

Link to post
Share on other sites

6 hours ago, steelo said:

So Im a little confused on how you would make a variable local...would you list them inside the function, rather than under main()? In your example, it looks like the variables are global.

This can be a confusing topic. As if to offer no help at all, the "truest" answer is that all variables are local to the scope in which they are declared.

 

foo = 5; # I'm in the global scope

def bar():
  baz = foo + 5 # I'm local to this function
  
class Example:
  some_constant = 0xDEADBEEF + foo # I'm local to this class
  
  def class_function():
    braaaaap = some_constant + foo # I'm local to this function
    
  def other_way(your_argument_here):
    baz = your_argument_here + some_constant + foo # I'm local to this function too
    
def another_way():
  global fun_stuff = foo + 0xCAFEBABE # I'm in the global scope
    

 

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

11 hours ago, straight_stewie said:

This can be a confusing topic. As if to offer no help at all, the "truest" answer is that all variables are local to the scope in which they are declared.

 


foo = 5; # I'm in the global scope

def bar():
  baz = foo + 5 # I'm local to this function
  
class Example:
  some_constant = 0xDEADBEEF + foo # I'm local to this class
  
  def class_function():
    braaaaap = some_constant + foo # I'm local to this function
    
  def other_way(your_argument_here):
    baz = your_argument_here + some_constant + foo # I'm local to this function too
    
def another_way():
  global fun_stuff = foo + 0xCAFEBABE # I'm in the global scope
    

 

Thanks, I'm currently working on a 'stranger things' project on a rpi 3, which selects a phrase from a dictionary, breaks the phrase down into individual letters, converts the letters to a gpio pin number and will light up individual leds based on the letters. I tried so hard to use classes, objects and methods, but it just became too confusing for me to follow and things were breaking =(

 

I've figured out that all variables defined within a function are local to that function. So, it looks  like any variable defined in the class is local to the class. If you were to create another class, say Example2 then the variable, 'some_constant' defined in Example wouldn't be defined and if you tried  to use it, python would throw an error....is that correct?

 

I also seem to be struggling with finding the best way to pass values between functions...should I return the value to main() then define another variable as that value, for example: x=addnum() then pass x to the next function? Also, how should I call a function? Is it good habit to call a function from main() or is it okay to call a function from another function?

 

If you look at my code below, you'll probably see my struggles...Initially, I began creating if-then statements for every letter of the alphabet. After quickly realizing how ridiculous that was, I decided to utilize a dictionary. I'm sure it looks every bit like it was written by somebody who learned python basics about a week ago...LOL

Link to comment
Share on other sites

Link to post
Share on other sites

mport random
import time
import RPi.GPIO as GPIO

pins_dict = {} #maps to gpio pin based on letter
pins_dict['a']=2
pins_dict['b']=3   
pins_dict['c']=4
pins_dict['d']=17
pins_dict['e']=27
pins_dict['f']=22
pins_dict['g']=10
pins_dict['h']=9
pins_dict['i']=11
pins_dict['j']=5
pins_dict['k']=6
pins_dict['l']=13
pins_dict['m']=19
pins_dict['n']=26
pins_dict['o']=18
pins_dict['p']=23
pins_dict['q']=24
pins_dict['r']=25
pins_dict['s']=8
pins_dict['t']=7
pins_dict['u']=12
pins_dict['v']=16
pins_dict['w']=20
pins_dict['x']=21
pins_dict['y']=0 #not yet defined
pins_dict['z']=0 #not yet defined

word_dict={} #DO NOT USE SPACES!!!
word_dict[0]="hello"
word_dict[1]="goodbye"
word_dict[2]="boo"
word_dict[3]="scary"
word_dict[4]="silly"



def pull_word(): #Pulls word on list based on random index sent
    i=random.randint(0,4) #picks random index 0-2 to select a word from list above
    word=(word_dict[i])
    return(word) #returns value of word so can be passed to convert_to_output function

def convert_to_pins(i): #pulls pin value from pins_dict
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    pin=pins_dict[i]
    GPIO.setup(pin,GPIO.OUT)
    time.sleep(2)
    GPIO.output(pin,GPIO.HIGH)
    print(str(pin)+" pin is on")
    time.sleep(2)
    GPIO.output(pin,GPIO.LOW)
    print(str(pin)+" pin is off")
    
pull_word()
word=pull_word()

for i in word: #breaks down phrase by letter, sends letter to convert_to_pins to be converted to pin #
    convert_to_pins(i)
    

 

Link to comment
Share on other sites

Link to post
Share on other sites

1 hour ago, steelo said:

I tried so hard to use classes, objects and methods, but it just became too confusing for me to follow and things were breaking =(

The good news is that your code, as presented, is using classes. A dictionary is a class :) 

 

But to further answer your question, there are really two ways (off the top of my head) to do this with classes in the sense that you are thinking. The conceptually easiest method to understand is going to be to have a word class which can write itself to the GPIO pins.

Something like this:

class Word:
  def __init__(self, word, pin_map):
    self.word = word
    self.pin_map = pin_map
    
  def write(self):
    for i in range(0, len(self.word)):
      self.__write_letter(self.word[i])
      
  def __write_letter(self, letter):
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    
    pin = self.pin_map[letter]
    GPIO.setup(pin, GPIO.OUT)
    
    time.sleep(2) # why is this here?
    
    GPIO.output(pin, GPIO.HIGH)
    print(f"pin number {pin} is on")
    
    time.sleep(2)
    
    GPIO.output(pin, GPIO.LOW)
    print(f"pin number {pin} is off")
    
    
    
def create_word_list(pin_map):
  temp_word_list = ["this", "is", "just", "a", "test"]
  
  word_list = []
  for i in range(0, len(temp_word_list)):
    word_list.append(Word(temp_word_list[i], pin_map))
    
  return word_list

def write_all_words(word_list):
  for i in range(0, len(word_list)):
    word_list[i].write()
    
if __name__ == "__main__":
  pin_map = {}
  # Rewrite your pin mapping here
  
  write_all_words(create_word_list(pin_map))
  


You can put this code in your Python 3.x debugger, set a breakpoint on the call to write_all_words, and step through it to investigate how this works. I'm out of time to write a good explanation of it for now.

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

6 minutes ago, straight_stewie said:

The good news is that your code, as presented, is using classes. A dictionary is a class :) 

 

But to further answer your question, there are really two ways (off the top of my head) to do this with classes in the sense that you are thinking. The conceptually easiest method to understand is going to be to have a word class which can write itself to the GPIO pins.

Something like this:


class Word:
  def __init__(self, word, pin_map):
    self.word = word
    self.pin_map = pin_map
    
  def write(self):
    for i in range(0, len(self.word)):
      self.__write_letter(self.word[i])
      
  def __write_letter(self, letter):
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    
    pin = self.pin_map[letter]
    GPIO.setup(pin, GPIO.OUT)
    
    time.sleep(2) # why is this here?
    
    GPIO.output(pin, GPIO.HIGH)
    print(f"pin number {pin} is on")
    
    time.sleep(2)
    
    GPIO.output(pin, GPIO.LOW)
    print(f"pin number {pin} is off")
    
    
    
def create_word_list(pin_map):
  temp_word_list = ["this", "is", "just", "a", "test"]
  
  word_list = []
  for i in range(0, len(temp_word_list)):
    word_list.append(Word(temp_word_list[i], pin_map))
    
  return word_list

def write_all_words(word_list):
  for i in range(0, len(word_list)):
    word_list[i].write()
    
if __name__ == "__main__":
  pin_map = {}
  # Rewrite your pin mapping here
  
  write_all_words(create_word_list(pin_map))
  


You can put this code in your Python 3.x debugger, set a breakpoint on the call to write_all_words, and step through it to investigate how this works. I'm out of time to write a good explanation of it for now.

Ahhh, thanks...that simplifoes it a bit (especially the dictionaries) btw, the sleep() function was added to give each led time to light up so the observer can 'read' what the message is 🙂

Link to comment
Share on other sites

Link to post
Share on other sites

5 minutes ago, steelo said:

Ahhh, thanks. btw, the sleep() function was added to give each led time to light up so the observer can 'read' what the message is 🙂

Oh. Duh. Guess I dropped the ball on that one.

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

1 hour ago, straight_stewie said:

Oh. Duh. Guess I dropped the ball on that one.

Thank you for rewriting all of that! Your code definitely looks a lot cleaner than mine! As far as passing values, should I always return a value to the main() and then call another function from there or is it 'ok' to call another function from inside a function?

Link to comment
Share on other sites

Link to post
Share on other sites

17 minutes ago, steelo said:

As far as passing values, should I always return a value to the main() and then call another function from there or is it 'ok' to call another function from inside a function?

We need to look at how things are actually ran:

def foo():
  return 5

def bar(number):
  return number + 2

if __name__ == "__main__":
  print(bar(foo()))

 

Here's what happens on the print(bar(foo())) line:

  1. foo() is evaluated.
  2. The result of foo() is then used as the argument to bar()
  3. The result of bar() is then used as the argument to print()

This is not passing functions as arguments. This is passing the result of a function call as the argument. You can view this by placing a breakpoint on the print() line and stepping through the program.

Generally speaking, any function that has a parameter of type T will accept as an argument the result of a call to a function that returns type T.


The other option is:

def foo():
  return 5

def bar(number):
  return number + 2

if __name__ == "__main__":
  a = foo()
  b = bar(a)
  print(b)

Which produces identical results.

 

In practice, it is preferred to pick whichever one makes the code more readable, which is largely subjective.

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

32 minutes ago, straight_stewie said:

We need to look at how things are actually ran:


def foo():
  return 5

def bar(number):
  return number + 2

if __name__ == "__main__":
  print(bar(foo()))

 

Here's what happens on the print(bar(foo())) line:

  1. foo() is evaluated.
  2. The result of foo() is then used as the argument to bar()
  3. The result of bar() is then used as the argument to print()

This is not passing functions as arguments. This is passing the result of a function call as the argument. You can view this by placing a breakpoint on the print() line and stepping through the program.

Generally speaking, any function that has a parameter of type T will accept as an argument the result of a call to a function that returns type T.


The other option is:


def foo():
  return 5

def bar(number):
  return number + 2

if __name__ == "__main__":
  a = foo()
  b = bar(a)
  print(b)

Which produces identical results.

 

In practice, it is preferred to pick whichever one makes the code more readable.

Okay, that makes sense...oop is very new to me...really, the last languages I learned were qbasic, pascal and visual basic back in the 90's! I do write simple sql queries at work just to pull the data I need, but I am a novice This is strange new territory for me 😁

 

btw, what do you mean by type T? I've never heard of that before. 🙂

Link to comment
Share on other sites

Link to post
Share on other sites

5 hours ago, steelo said:

btw, what do you mean by type T?

Be warned: Typing and Type Systems are some of the most difficult concepts and fields of research in all of programming. Academically thorough and technically rigorous definitions and explanations are beyond the scope of many graduate students in the field. Fortunately, you need not be an expert on the subject to understand enough to know what a Type is or what the implications of Types are.

Stated rather abruptly, a Type is some metadata that describes how a "thing" (variable, object...) can be used and stored. This is difficult to notice in python because python deliberately hides this from you: This is what people are talking about when they talk about duck typing.

 

For some examples, run this code in your debugger:

print(type(5))
print(type(6.2))
print(type("aaa"))
print(type(False))
print(type([1, 2, 3, 4]))
print(type(("a", "b", "c")))
print(type(range(0, 10)))
print(type({}))


In some other languages, the types are explicit rather than implicit:

// No language in particular

int a = 5;
float b = 6.2;
string c = "aaa";
bool d = false;
int[] e = [1, 2, 3, 4];
tuple f = tuple("a", "b", "c");
range g = range(0, 10);
dictionary h = dictionary();

 

5 hours ago, steelo said:

I've never heard of that before.

That's because you're using Python

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

9 minutes ago, straight_stewie said:

Be warned: Typing and Type Systems are some of the most difficult concepts and fields of research in all of programming. Academically thorough and technically rigorous definitions and explanations are beyond the scope of many graduate students in the field. Fortunately, you need not be an expert on the subject to understand enough to know what a Type is or what the implications of Types are.

Stated rather abruptly, a Type is some metadata that describes how a "thing" (variable, object...) can be used and stored. This is difficult to notice in python because python deliberately hides this from you: This is what people are talking about when they talk about duck typing.

 

For some examples, run this code in your debugger:


print(type(5))
print(type(6.2))
print(type("aaa"))
print(type(False))
print(type([1, 2, 3, 4]))
print(type(("a", "b", "c")))
print(type(range(0, 10)))
print(type({}))


In some other languages, the types are explicit rather than implicit:


// No language in particular

int a = 5;
float b = 6.2;
string c = "aaa";
bool d = false;
int[] e = [1, 2, 3, 4];
tuple f = tuple("a", "b", "c");
range g = range(0, 10);
dictionary h = dictionary();

 

That's because you're using Python

I think I understand the data types...integer, fp, strings but you kind of lost me when you discussed explicit versus implicit. Hopefully, later down the road with more experience, I'll pick this up. Thank you once again!

Link to comment
Share on other sites

Link to post
Share on other sites

1 minute ago, steelo said:

I do not yet know HOW to use it or when

I'm 100% positive that you already know everything you need to know about types, and that you already know how to use that information, and that you do that every time you write a program, even if you don't realize it:
 

a = 5;
b = 6.5;

c = "you subconsciously know what I am"

I'd bet dollars to donuts that you can answer the following questions about the above code snippet, without ever touching a search engine:

  • What type of number is a?
  • What type of number is b?
  • What do we call c?
  • What kinds of things can we do with a and b?
  • What can we do with c?

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

6 minutes ago, straight_stewie said:

I'm 100% positive that you already know everything you need to know about types, and that you already know how to use that information, and that you do that every time you write a program, even if you don't realize it:
 


a = 5;
b = 6.5;

c = "you subconsciously know what I am"

I'd bet dollars to donuts that you can answer the following questions about the above code snippet, without ever touching a search engine:

  • What type of number is a?
  • What type of number is b?
  • What do we call c?
  • What kinds of things can we do with a and b?
  • What can we do with c?

ugh...sorry, I edited my prior post before noticing your reply. 😁

 

to answer your question:

 

integer

floating point

string

math functions

you can add other strings to this, append and delete...I think 🙂

 

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

8 minutes ago, steelo said:

integer

floating point

string

math functions

you can add other strings to this, append and delete...I think 🙂

There you go. You know everything you need to know about types, and you know how to use that information.

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

11 minutes ago, straight_stewie said:

There you go. You know everything you need to know about types, and you know how to use that information.

It worries me that I dont quite understand creating classes yet even after studying it for 3 days...I understand what it is, just not how to use it and when. Its like learning words of a foreign language but not knowing how to put the words together to say what I want. (if that makes any sense) LOL

Link to comment
Share on other sites

Link to post
Share on other sites

9 hours ago, straight_stewie said:

All I can say is:

Welcome to programming.

LOL

Link to comment
Share on other sites

Link to post
Share on other sites

11 hours ago, steelo said:

...I understand what it is, just not how to use it and when. ...

OOP is a style, not an optimization. What I mean by that is that you use objects because you like them / prefer to think that way, not because it's 'better'. There is exactly one exception to this I'm aware of. Say you are programming Noah's Ark. You have two of every kind of animal, and you want to have a feed() function. If you program this without objects, then you need one function for every animal; feedCheeta(), feedFly(), feedBat(), etc.

 

If you use objects, you could write one function abstract: feed(Food f). Every animal's food type would be able to be described by the Food class, and every animal would implement the feed(Food f) prototype, making it work in the specific way that animal eats (lions eat their meat by tearing off chunks, but giraffes chew their food and cud.). But, when you write the FeedingTime() function for Noah, you only need to have a simple for loop that says:

 

Foreach animal in AnimalsOnBoat:
    noah.feed(animal.getFood());

And the language/compiler would automatically pick the correct specific implementation of the feed() function. You *can* re-create this behavior without objects, but to do it as efficiently as an Object Oriented language like Java does it is non-trivial (basically, you make a hashmap of the function prototypes and then end up with a hashmap of function pointers; that makes most people's brains hurt, but that's what Java is doing under the covers).

 

OOP doesn't make feeding Noah's boat any easier to program, understand, or run more efficiently. It just moves the complexity from one spot to another.

 

Note: This one example, of having dozens or hundreds of implementations for the same function (feed()), is the one case where OOP is more efficient than functional programming. Without OOP you end up with a giant switch statement to pick the correct feed function, and that's very inefficient.

Link to comment
Share on other sites

Link to post
Share on other sites

On 5/27/2020 at 4:32 AM, straight_stewie said:

That's because you're using Python

With the newest python as you can use types, still not strongly enforced.

 

def foo() -> int:
  return 5

def bar(number: int) -> int:
  return number + 2

if __name__ == "__main__":
  print(bar(foo()))

 

                     ¸„»°'´¸„»°'´ Vorticalbox `'°«„¸`'°«„¸
`'°«„¸¸„»°'´¸„»°'´`'°«„¸Scientia Potentia est  ¸„»°'´`'°«„¸`'°«„¸¸„»°'´

Link to comment
Share on other sites

Link to post
Share on other sites

6 hours ago, vorticalbox said:

still not strongly enforced.

Is that static type checking or runtime type checking?

 

EDIT:: After reading PEP 3107 - Function Annotations

 

We find out that Python itself does absolutely nothing with this information except ignore it, and that it exists only to allow a standardized way for third party tooling to conduct various types of enforcement:

Quote

Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time.

 

By itself, Python does not attach any particular meaning or significance to annotations. Left to its own, Python simply makes these expressions available as described in Accessing Function Annotations below.

 

The only way that annotations take on meaning is when they are interpreted by third-party libraries. These annotation consumers can do anything they want with a function's annotations. 

 

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

1 hour ago, straight_stewie said:

Is that static type checking or runtime type checking?

 

EDIT:: After reading PEP 3107 - Function Annotations

 

We find out that Python itself does absolutely nothing with this information except ignore it, and that it exists only to allow a standardized way for third party tooling to conduct various types of enforcement:

 

It's not unlike typescript its still better than nothing, it help stop the developervpassimg around wrong values in your ide.

                     ¸„»°'´¸„»°'´ Vorticalbox `'°«„¸`'°«„¸
`'°«„¸¸„»°'´¸„»°'´`'°«„¸Scientia Potentia est  ¸„»°'´`'°«„¸`'°«„¸¸„»°'´

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×