2

Consider the following codes

class Bank_acount:
    def password(self):
        return 'code:123'

Now consider a few cases when executing the class as below

denny = Bank_acount()
denny.password()            # function call

>> 'code:123'

Next

denny.password             # password is function name
 
>> "bound method Bank_acount.password of <__main__.Bank_acount object at 0x00000167820DDCA0>>"

Now if I changed the function name

denny.password = 'code:456'     # I changed the function name

I got

denny.password

>> 'code:456'

However,

denny.password()

>>TypeError: 'str' object is not callable

I am confused

  1. denny.password = 'code:456' does not make any change to return 'code:123' in the original class, right?
  2. Has the original method password(self) been destroyed?
  3. After changing the function name, a new function code:456() pops out?

Thanks!

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Denny
  • 223
  • 2
  • 15

2 Answers2

4

Has the original method password(self) been destroyed?

The method still exists, but it has been shadowed by another value only for the denny instance.

a new function code:456() pops out?

It's not a function; as the error says, strings are not callable


You can change the code with a separate attribute, not by a function change

class Bank_acount:
    def __init__(self, code):
        self.code = code
    def password(self):
        return 'code:' + str(self.code) 

denny = Bank_acount(123)
print(denny.password())
denny.code = 456
print(denny.password())
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
3

You are not "changing the function name". All you are doing is setting an instance attribute with key password and value 'code:456'

The class attribute (which is a method) will be unaffected. But when you access password on the instance it will evaluate to the string you set it to because of how object attribute lookup works

Here's an example based on yours where we reset the instance afterwards to get a better understanding:

class Bank_acount:
    def password(self):
        return 'code:123'

denny = Bank_acount()
print(denny.password())
>>> code:123

denny.password = "code:456"
print(denny.password)
>>> code:456

del denny.password
print(denny.password())
>>> code:123

Possibly helpful answer for the naming conventions and some attribute examples

Mandera
  • 2,647
  • 3
  • 21
  • 26
  • "it will resolve to the string you set it to because of the Method Resolution Order" <- That's wrong. The MRO is a sequence of *classes*; it determines what happens when multiple *classes* have the same attribute. Resolving an *instance* attribute doesn't depend on the MRO at all. (Ok, technically it does, but that's a different topic.) – Aran-Fey Jul 24 '22 at 07:32
  • Oh alright interesting! Object attribute lookup is probably the right name? Found [this](https://stackoverflow.com/a/32295861/3936044) – Mandera Jul 24 '22 at 07:35
  • I don't think there's a name for it. This is just one small part of how attribute lookup works. You could maybe say that instance attributes shadow class attributes or something along those lines, but that's not even really true because [data descriptors](https://docs.python.org/3/howto/descriptor.html#descriptor-protocol) take priority over instance attributes. – Aran-Fey Jul 24 '22 at 07:40
  • Since you've edited the answer, shall we delete these comments? Or leave them so other people can learn a thing or two about the MRO and descriptors? – Aran-Fey Jul 24 '22 at 07:45
  • I think they can stay! :) – Mandera Jul 24 '22 at 07:47