3
class add:
    def add_me(self, *args):
        res = 0
        for x in args:
            res += x
        return res

print(add().add_me(1, 2, 3, 4, 5))             # 15
print(add.add_me(add(), 1, 2, 3, 4, 5))        # 15
print(add.add_me(1, 2, 3, 4, 5))               # 14

calling add.add_me still allows me to call the method, even when add() is not properly instantiated, how come? Is add.add_me in this case a Static method without needing to instantiated add class?

0

3 Answers 3

0

A class is still an object, and in python you can just add arbitrary fields to objects. See for example https://jakevdp.github.io/WhirlwindTourOfPython/03-semantics-variables.html#Everything-Is-an-Object.

Also works for functions,

def hello():
   return "hi"

hello.new = "asdf"

print(hello.new)
Sign up to request clarification or add additional context in comments.

Comments

0
print(add().add_me(1, 2, 3, 4, 5))             # 15

This creates an add object and calls its method. The method is called with the instance itself as the first parameter (self).

print(add.add_me(add(), 1, 2, 3, 4, 5))        # 15

This calls the class method. The method is called with a newly created instance as the first parameter, so it's basically a weird way of doing the same thing as in the first case.

print(add.add_me(1, 2, 3, 4, 5))               # 14

This calls the class method too. This time the first parameter is 1, so the sum is computed on 2,3,4,5 only. This would result in an error as soon as you wanted to use some object members (i.e. something like self.do_somehing() or self.some_value)

Comments

0

You're exploiting python's flexibility. The way you are calling this function might not work or might have unexpected behavior (as in the third case), but it's not invalid syntax.

Modifying the class definition to add some debug printing:

class add:
    def add_me(self, *args):
        print(f"self:   {self}")
        print(f"args:   {args}")
        res = 0
        for x in args:
            res += x
        return res

When you call an instance method on an initialized object, the first argument is always the object itself. Therefore, calling add_me with an initialized instance passes the instance as self and the numbers 1 to 5 as args, resulting in a sum of 15:

In [2]: print(f"result: {add().add_me(1, 2, 3, 4, 5)}")
self:   <__main__.add object at 0x10e127160>
args:   (1, 2, 3, 4, 5)
result: 15

Calling the class method add.add_me using the class means no instance is passed in automatically. However, by passing in a newly initialized instance as the first argument, add() works a similar way, but the first argument self refers to a different object (0x10df28bb0, as opposed to the original instance 0x10e127160). If the method referenced an attribute or modified the state of self, the instance referenced/modified would be the one passed in in this case:

In [3]: print(f"result: {add.add_me(add(), 1, 2, 3, 4, 5)}")
self:   <__main__.add object at 0x10df28bb0>
args:   (1, 2, 3, 4, 5)
result: 15

Finally, simply calling the method on an un-initialized object fails to pass anything in as the first argument, and the first expliclit argument 1 is assigned to the variable self, and is excluded from the total:

In [4]: print(f"result: {add.add_me(1, 2, 3, 4, 5)}")
self:   1
args:   (2, 3, 4, 5)
result: 14

See this realpython guide to instancemethods, classemethods, and staticmethods for more information.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.