How does `edit_message` know to reference the `Message` class?

I still don’t get one thing

class Message:
  def __init__(self, sender, recipient, text):
    self.sender = sender
    self.recipient = recipient
    self.text = text

class User:
  def __init__(self, username):
    self.username = username
    
  def edit_message(self, message, new_text):
    if message.sender == self.username:
      message.text = new_text

When we execute if message.sender == self.username how does the compiler knows what .sender is. It is defined in another class. Does classes can acess each other’s methods just like that?

1 Like

here:

def edit_message(self, message, new_text):

when calling the message method, message parameter should receive a value (from argument) which is an instance of Message class

once we have an instance of a class, we can access instance attributes.

it would be good to do a sanity check, that message parameter indeed gets a legit value (instance of Message class)

1 Like

Thank you for this question . I was confused as well :slight_smile:

1 Like

It doesn’t. And when you say compiler you probably don’t mean something that exists for python.
It’s more that python does whatever you say. So, you would have to put that thing there in order for you to be able to access it later.

The getattr operator (.) doesn’t care at all whether it’s used inside a class or which class.

As for python’s compiler, all it does is translate code into byte code, desugaring python code into something that is easier to execute… All it cares about is whether the code is syntactically valid.

hello
here :

def f():
    print(a)  # undefined, but does exist
    a = 5

how could say a is exist before we assign an object(5) to it ?

1 Like

I was confused here too. This might not be the smartest way to do this (Ok it almost deinitely is not :smile: ) but maybe it will help someone.

class Admin(User):
  def edit_message(self, message, new_text):
    if message.sender == self.username:
      message.text = new_text
    elif message.sender != self.username:
      message.text = new_text

## basically says if message sender is original user/sender they can edit
ELIF
message sender is not the original user/sender, they can edit

the message sender is or isn’t the current user. That are all cases, making the condition reduntant.

I’m curious about what was meant there too. When we call the f() function we get UnboundLocalError: local variable 'a' referenced before assignment.

Hi @fadelm0,

Are you referring to this code?:

def f():
    print(a)
    a = 5

f()

Before the function gets executed, the Python interpreter scans the code to establish the definition of the function. It discovers that a is assigned a value within the function, and that establishes a as a local variable with a scope that extends throughout the entire function, right from its beginning. No error is raised at that time, because the function’s statements are not executed until the function is called.

When the function is called, its statements are executed in order. The Python interpreter has already determined that a is a local variable throughout the function, and raises the error when an attempt is made to display the value of a before a value has been assigned to it.

Edited on May 17, 2020 to add the following:

One could ask why the value 5 is not assigned to a throughout the function at the time that the variable is discovered and established as a local variable with a scope throughout the function. There is a good reason why that is not done, which we can discuss upon request.

1 Like

Thanks! That makes sense. So I see that is how it’s supposed to behave and I didn’t understand it wrong. What I didn’t understand is whether there is any practical use for it, or it was just a demonstration of how python’s interpreter works.

As for why it’s not assigned at the time the variable is discovered, I suspect this is to protect the reassignment ability. When reassigning the same variable more than once inside the function, you need your code to reference its latest assignment above it. Is that the reason?

Yes, while the scope of the variable consists of the entire function, its value is dynamic. Accordingly, it makes sense for the variable to have the most recent value that has been assigned to it, which is consistent with its having no value at all prior to the first assignment.

There is also a good reason why an assignment to a variable within a function definition causes that variable to be local, even when there is a global variable with the same name. Note that this applies, of course, even when the assignment to the variable lies within a conditional block that causes it to never get executed.

This example raises an UnboundLocalError:

a = 9
def f():
    print(a)
    if 3 == 5:
        a = 5

f()

Edited on May 17, 2020 to add the following:

We could do this to make a global:

a = 9
def f():
    global a
    print(a)
    if 3 == 5:
        a = 5

f()

With good reason, the maintainers of Python require that programmers make the effort to explicitly designate a name as global in order for it to be so, if it is the target of an assignment with a function.

We could consider the wisdom of the above Python interpreter design decisions, if anyone wishes to discuss them.

2 Likes

message is a parameter,

when you call edit_message(message, new_text) later, the parameter message could be the instance of the class Message.

May be the gods be with you, thanks for this solution.

I got puzzled with this exercise. It left me scratching my head more than 2 hours only to realise how dumb I am !
just in case You are also feeling that same, this is extension of @appylpye 's explanation.

Codecademy played a cruel joke on us novice by naming both class Message and the first parameter of def edit_message as message which almost looks the same thus puzzling our mind.

here message (you can name it anything, just rename all the message variables after that) has no connection with class Message until we pass an object of class Message as an argument within monty.edit_message where monty is an object of class User.

after we have done this now we’ve got access to all the instance variables we had created in class Message thus we can use .text, .sender as message.text, message.sender.

You’ll never understand it unless you instantiate/create object for all the classes, call the methods through the objects and print the values.

here is my code:

class Message:
  def __init__(self, sender, recipient, text):
    self.sender = sender
    self.recipient = recipient
    self.text = text

class User:
  def __init__(self, username):
    self.username = username
    
  def edit_message(self, message, new_text):
    if message.sender == self.username:
      message.text = new_text

class Admin(User):
  def edit_message(self, message, new_text):
    message.text = new_text

aloha = User('Rock')
boom = Message('arka', 'sita', 'hiiiiiiiiiiii')
print(boom.sender, boom.text, boom.recipient)
aloha.edit_message(boom, 'lolwa')
print(boom.sender, boom.text, boom.recipient)

cool = Admin('rock')
cool.edit_message(boom, 'hojoborolo')
print(boom.sender, boom.text, boom.recipient)

OUTPUT:

arka hiiiiiiiiiiii sita
arka hiiiiiiiiiiii sita // because message.sender != self.username
arka hojoborolo sita

so just looking at this and trying to print out an edited message and use this class, how do i get it to print out a message that is edited, how do i use the method edit_message inside the user class? specfically, this line of code is what i want to do "who.edit_message(“message”, “new message”). I get an error with this code.
here is the error:

Traceback (most recent call last):
File “script.py”, line 23, in
who.edit_message(“message”, “new message”)
File “script.py”, line 12, in edit_message
if message.sender == self.username:
AttributeError: ‘str’ object has no attribute ‘sender’

Also, how to i take screen shots of my code to make it easier to post on here?

class Message:

  def __init__(self, sender, recipient, text):

    self.sender = sender

    self.recipient = recipient

    self.text = text

class User:

  def __init__(self, username):

    self.username = username

    

  def edit_message(self, message, new_text):

    if message.sender == self.username:

      message.text = new_text

class Admin(User):

  def edit_message(self, message,new_text):

    message.text = new_text

    print (message.text)

letter= Message("jason", "ryan", "high buddy")

who=User("jason")

who.edit_message("message", "new message")

print (who.username)

print (letter.sender)

print (letter.recipient)

print (letter.text)

no screenshots preferable, then we can’t run your code. Posting your code with format/markup is much preferred.

here:

who.edit_message("message", "new message")

the first argument should be a Message instance/object, so for example letter variable (which holds a Message instance):

who.edit_message(letter, "new message")

here:

  def edit_message(self, message, new_text):

    if message.sender == self.username:

      message.text = new_text

you can see that message needs a sender property. An instance of Messages has this property:

class Message:

  def __init__(self, sender, recipient, text):

    self.sender = sender

this is why strong/static type hinting is so useful, you know what type the parameter needs, so what argument you need to provide.

1 Like

how do i post my code with format/markup? I just cut and pasted does that do it?

copying posting is the first step.

surely other people run into this issue, so with a little searching you should be able to find how format your code:

[How to] Format code in posts

1 Like

that helped thanks, not my posts look much nicer

Python - Inheritance and Polymorphism

Overriding Methods

I’m going to have to agree with everyone who recommended that we should instantiate objects, try to work with them, and print things out in order to better understand what is going on. Here is a copy of what I used to help me understand this lesson:

## Create an Admin ##
admin = Admin("admin")

## Create a couple Users ##
user1 = User("user1")
user2 = User("user2")

## Create a Message ##
message1 = Message(user1.username, user2.username, "This is my message to user2.")

## Check usernames ##
print(admin.username)
print(user1.username)
print(user2.username)

## Check message ##
print(message1.text)

## Check to see if the permissions logic is working for edit_message() ##
user2.edit_message(message1, "This shouldn't work since I'm not the original sender or an Admin.")
print(message1.text)

## Check to see if the permissions logic is working for edit_message() ##
admin.edit_message(message1, "This should work since I'm an Admin.")
print(message1.text)

## Check to see if the permissions logic is working for edit_message() ##
user1.edit_message(message1, "This should work since I'm the original sender.")
print(message1.text)

And here is the resulting output:

## Checking usernames ##
admin
user1
user2

## Checking message ##
This is my message to user2.

## Editing message as user2 doesn't work since user2 isn't the original sender ##
This is my message to user2.

## Editing the message as admin does work since edit_message() is overridden with new logic ##
This should work since I'm an Admin.

## Editing the message as user1 does work since edit_message() logic verifies user1 as sender ##
This should work since I'm the original sender.
1 Like