9

I am currently learning Python. Since I am a big fan of OO (object-oriented) programming, obviously it's not hard to apply it in Python. But when I tried it, it seems very different to C#.

As you can see below, I am trying to create a character class, with three attributes Id, Hp, and Mana. The score is calculated by adding up Hp and Mana and then times 10.

As you can see, after defining MyChar where id=10 hp=100 mana=100, I was expecting MyChar.Score is (100+100)*10, which is 2000, but weirdly, it says:

bound method Character.Score of <__main__.Character object at 0x0000021B17DD1F60> as the result of print(MyChar.Score).

How can I fix this problem?

Here is my code:

class Character:

    def __init__(self, Id, Hp, Mana):
        self.Id = Id;
        self.Hp = Hp;
        self.Mana = Mana;


    def Score(self):
        return (self.Hp + self.Mana)*10;

MyChar = Character(10, 100, 100);

print(MyChar.Score)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
FrankW
  • 109
  • 10
  • 5
    Score is not an attribute but a member function, invoke it like print(MyChar.Score()) – Kunal Mukherjee Jun 11 '19 at 11:23
  • @KunalMukherjee please check your facts - `Score` IS an attribute (of the `MyChar` class object). – bruno desthuilliers Jun 11 '19 at 11:26
  • @brunodesthuilliers I mean its not meant to be invoked as an attribute – Kunal Mukherjee Jun 11 '19 at 11:27
  • 1
    @Kunal It *could* be, though. This is especially useful when passing functions/methods into higher-order functions such as map/filter. :) – TrebledJ Jun 11 '19 at 11:28
  • 5
    I would swear that this kind of functions are also called methods and are invoked with `()` in C#. – Stop harming Monica Jun 11 '19 at 11:30
  • But in the context of OP its not meant to be called without parenthesis / passed around – Kunal Mukherjee Jun 11 '19 at 11:30
  • 2
    @KunalMukherjee yes it is - the `MyChar.Score()` expression _first_ resolves the `"Score"` attribute on `MyChar` object (yielding a method object), _then_ applies the call operator (the parens) on it. – bruno desthuilliers Jun 11 '19 at 11:30
  • 1
    @Goyo you may want to read this about what Python "methods" really are: https://wiki.python.org/moin/FromFunctionToMethod - as a general rule, Python's object model is wildly different from C#'s one, so while you'll find the same basic concepts of class, instance, attribute, method etc, you won't have a 1:1 mapping with the way C# implement those concepts. – bruno desthuilliers Jun 11 '19 at 11:33
  • @brunodesthuilliers Yet I always see `()` being used to call methods in C#. I might be missing something though. – Stop harming Monica Jun 11 '19 at 11:40
  • @Goyo I'm afraid I didn't read your comment properly, sorry. – bruno desthuilliers Jun 11 '19 at 11:43
  • You don't need the semicolons at the end. Your output, by the way, was `__repr__(method)` – Eric Jin Jun 11 '19 at 11:54
  • @Goyo I'm guessing OP expects `Score` to work as a get-property. – JAD Jun 12 '19 at 06:27
  • @JAD I realize that, I just don't think that mistaken expectation can be related to how Python "seems very different to C#" because as far as I know they work pretty much the same in this particular case. – Stop harming Monica Jun 12 '19 at 07:31

4 Answers4

25

If you want to use it like a property in C#, decorate the function with @property, like so:

class Character:

    def __init__(self,Id,Hp,Mana):
        self.Id=Id;
        self.Hp=Hp;
        self.Mana=Mana;

    @property
    def Score(self):
        return (self.Hp+self.Mana)*10;

MyChar=Character(10,100,100);

print(MyChar.Score)

So you don't have to call it like a function.

For more advanced usage of properties (e.g. also having a setter func), see the official docs: https://docs.python.org/3/library/functions.html#property

Radeonx
  • 191
  • 2
  • 14
Adam.Er8
  • 12,675
  • 3
  • 26
  • 38
  • 6
    While that's a nice suggestion, it doesn't really answer the OP's question. – bruno desthuilliers Jun 11 '19 at 11:38
  • 1
    @brunodesthuilliers I don't understand. This adds a single line (the decorator) to OP's example and fixes the problem. How does this not answer the question? – Teepeemm Jun 12 '19 at 03:25
  • 1
    @Teepeemm The OP asked why the expression `MyChar.Score` didn't eval to the return value of the `Score` method, not how to make it a computed attribute instead of a method. – bruno desthuilliers Jun 12 '19 at 06:58
  • 2
    While the title does ask how to use the function, the body simply asks "How can I fix this problem?" It seems that either `MyChar.Score()` or `@property def Score` cause the print statement to work as OP wanted, and this approach has the advantage of encapsulating more logic into the class definition. – Teepeemm Jun 12 '19 at 11:34
19

tl;dr

Use it like any other function by calling it: print(MyChar.Score()) (note the additional pair of parentheses).


As you've correctly stated, MyChar.Score is a "function under a class" (aka "method"). So just use it like any other function by calling it: suffixing it with a pair of parentheses.

print(MyChar.Score())
#                 ^^

Without the call, simply doing print(MyChar.Score) prints <bound method blah blah>, i.e. the informal string representation of the method. The print function internally calls __str__() magic method (or __repr__(), if the former isn't defined). Hence, the following print equivalent lines:

print(MyChar.Score.__str__())
print(str(MyChar.Score))
print(MyChar.Score.__repr__())
print(repr(MyChar.Score))

In Python, functions are first-class citizens, hence they are objects and have the __str__() and __repr__() methods.

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
  • 7
    Without the parentheses, you were not calling the function. Instead, you were printing a representation of the function itself, which is a method of the character class called 'Score'. – Deepstop Jun 11 '19 at 11:25
7

In Python, everything is an object, including classes, functions and methods, so MyChar.Score (without the parens) only resolves the Score attribute on MyChar object. This yields a method object, which happens to be a callable object (an object that implements the __call__ special method). You then have to apply the call operator (the parens) to actually call it.

You may want to check the official documentation for more on Python's object model.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
0
class Character(object):
    def __init__(self):
        print ('Starting')

    def method(self):
        print ('This is a method()')
ch = Character()
'''When we don't add the bracket after the method call, it would lead to a method bound error as in your case'''
print (ch.method)
'''This can be solved by doing the following line'''
ch.method()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Saurav Rai
  • 2,171
  • 1
  • 15
  • 29