aaaidan
aaaidan

Reputation: 315

Strange Python OOP function call error

I've created some Object Oriented Python code below, where a user instance has been created (called Ben). The functions work correctly however, I have found that when the last line is changed to ben.check_money = 100, no error is thrown. I know that this is not correct syntax. Adding the correct function call back in however, throws the listed error: TypeError: 'int' object is not callable

Original Code:

class User:

    def __init__(self):
        self.money = 200
        self.score = 0
        print('Created ')

    def increase_money(self, amount):
        self.money += amount

    def decrease_money(self, amount):
        self.money -= amount

    def check_money(self):
        print(self.money)


ben = User()

ben.check_money() # No error is thrown here

Modified Code 1;

class User:

    def __init__(self):
        self.money = 200
        self.score = 0
        print('Created ')

    def increase_money(self, amount):
        self.money += amount

    def decrease_money(self, amount):
        self.money -= amount

    def check_money(self):
        print(self.money)


**ben = User()
ben.check_money = 100 # No error thrown here
ben.check_money() # Error is thrown here**

Modified Code 2;

class User:

    def __init__(self):
        self.money = 200
        self.score = 0
        print('Created ')

    def increase_money(self, amount):
        self.money += amount

    def decrease_money(self, amount):
        self.money -= amount

    def check_money(self):
        print(self.money)


**ben = User()
ben.check_money = 100 # No error thrown here
ben.check_money # No Error is thrown here**

My question is; why does the error only occur in certain situations, depending on how you have previous called it? One would assume that it should throw an error for both Modification code 1 and Modification code 2.

Upvotes: 3

Views: 73

Answers (3)

Copperfield
Copperfield

Reputation: 8510

Python is dynamic language, that mean that you can add, remove, or replace anything from almost any object you want at any time you want, unless measures are taken from doing that

for example

>>> class Fuu:
    def show(self):
        print("This is Esparta")


>>> y=Fuu()
>>> y.show()
This is Esparta
>>> y.show
<bound method Fuu.show of <__main__.Fuu object at 0x000000000357CC50>>
>>> y.show = 10
>>> y.show
10
>>> y.a
Traceback (most recent call last):
  File "<pyshell#51>", line 1, in <module>
    y.a
AttributeError: 'Fuu' object has no attribute 'a'
>>> y.a=42
>>> y.a
42
>>> def buu():
    print("42 is the ultimate answer")

>>> y.fun = buu
>>> y.fun()
42 is the ultimate answer
>>>     

in this example the class Fuu only define 1 attribute/method, show, but you can add additional ones like a or fun or replace existing ones like show at any time as show in the example. In your code what you are doing is replacing your original check_money method for a integer and python said nothing because it is in its nature to allow this unlike more static lenguajes

Upvotes: 2

Chris
Chris

Reputation: 136918

When you execute ben.check_money = 100 you replace the function that used to be called check_money on the ben object with the integer 100. Calling an integer as a function doesn't work:

>>> 100()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

You will get a similar error when running ben.check_money() after setting it to 100.

But if you simply access ben.check_money you're not asking Python to run check_money, simply to give it back to you, so you get the integer value back.

Upvotes: 4

spacegoing
spacegoing

Reputation: 5306

When you do this ben.check_money=100 you assigned an integer 100 to the ben.check_money attribute. And this attribute shadowed the method ben.check_money(). Now ben only has attribute check_money. So if you call ben.check_money you are actually calling the attribute, which is an int 100. ben.check_money() is calling the method but the method is shadowed now.

The right way to do this is probably writing a setter() method:

class User:

    def __init__(self):
        self.money = 200
        self.score = 0
        print('Created ')

    def increase_money(self, amount):
        self.money += amount

    def decrease_money(self, amount):
        self.money -= amount

    def set_money(self,amount):
        self.money = amount

    def check_money(self):
        print(self.money)


**ben = User()
ben.set_money(100)
ben.check_money() 

Upvotes: 3

Related Questions