In [21]:
class MyClass:
    test = 'test'
    def method(self):
        return 'instance method called', self, self.__class__
    
    @classmethod
    def classmethod(cls):
        return 'class method called', cls
    
    @staticmethod
    def staticmethod():
        return 'static method called'

In [22]:
p1 = MyClass()
p2 = MyClass()
print("p1.test = ",p1.test)
print("p2.test = ", p2.test)
print("p1.__class__.test = ",p1.__class__.test)
print("p2.__class___.test = ", p2.__class__.test)
print('MyClass.test', MyClass.test)
print("=======")
p1.test = 'stop'
print("p1.test = ",p1.test)
print("p2.test = ", p2.test)
print("p1.__class__.test = ",p1.__class__.test)
print("p2.__class___.test = ", p2.__class__.test)
print('MyClass.test', MyClass.test)
print("=======")
p1.__class__.test = 'next'
print("p1.test = ",p1.test)
print("p2.test = ", p2.test)
print("p1.__class__.test = ",p1.__class__.test)
print("p2.__class___.test = ", p2.__class__.test)
print('MyClass.test', MyClass.test)
print("=======")
del p1.test
print("p1.test = ",p1.test)
print("p2.test = ", p2.test)
print("p1.__class__.test = ",p1.__class__.test)
print("p2.__class___.test = ", p2.__class__.test)
print('MyClass.test', MyClass.test)
print("=======")
MyClass.test = 'test'
print("p1.test = ",p1.test)
print("p2.test = ", p2.test)
print("p1.__class__.test = ",p1.__class__.test)
print("p2.__class___.test = ", p2.__class__.test)
print('MyClass.test', MyClass.test)
print("=======")


p1.test =  test
p2.test =  test
p1.__class__.test =  test
p2.__class___.test =  test
MyClass.test test
=======
p1.test =  stop
p2.test =  test
p1.__class__.test =  test
p2.__class___.test =  test
MyClass.test test
=======
p1.test =  stop
p2.test =  next
p1.__class__.test =  next
p2.__class___.test =  next
MyClass.test next
=======
p1.test =  next
p2.test =  next
p1.__class__.test =  next
p2.__class___.test =  next
MyClass.test next
=======
p1.test =  test
p2.test =  test
p1.__class__.test =  test
p2.__class___.test =  test
MyClass.test test
=======

Instance method


In [7]:
obj = MyClass()
obj.method()


Out[7]:
('instance method called', <__main__.MyClass at 0x4756400>, __main__.MyClass)

In [8]:
obj.__class__


Out[8]:
__main__.MyClass

In [9]:
MyClass.method(obj)


Out[9]:
('instance method called', <__main__.MyClass at 0x4756400>, __main__.MyClass)

In [10]:
MyClass.method()


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-7f5ee7a23fc9> in <module>()
----> 1 MyClass.method()

TypeError: method() missing 1 required positional argument: 'self'

Class method


In [11]:
obj.classmethod()


Out[11]:
('class method called', __main__.MyClass)

In [12]:
MyClass.classmethod()


Out[12]:
('class method called', __main__.MyClass)

Static Method


In [13]:
obj.staticmethod()


Out[13]:
'static method called'

In [14]:
MyClass.staticmethod()


Out[14]:
'static method called'

Bare-bone Pizza class


In [15]:
class Pizza:
    def __init__(self, ingredients):
        self.ingredients = ingredients
        
    def __repr__(self):
        return "Pizza({})".format(self.ingredients)

In [16]:
Pizza(['cheeze', 'tomatoes'])


Out[16]:
Pizza(['cheeze', 'tomatoes'])

Delicious Pizza Factories With @classmethod


In [17]:
class Pizza:
    def __init__(self, ingredients):
        self.ingredients = ingredients
        
    def __repr__(self):
        return "Pizza({})".format(self.ingredients)
    
    @classmethod
    def margherita(cls):
        return cls(['mozzarella', 'tomatoes'])
    
    @classmethod
    def prosciutto(cls):
        return cls(['mozzarella', 'tomatoes', 'ham'])

In [18]:
Pizza.margherita()


Out[18]:
Pizza(['mozzarella', 'tomatoes'])

In [19]:
Pizza.prosciutto()


Out[19]:
Pizza(['mozzarella', 'tomatoes', 'ham'])

When to use static methods


In [23]:
# The classmethod decorator is clearly useful, but I’ve never seen a
# compelling use case for staticmethod. If you want to define a
# function that does not interact with the class, just define it in the
# module. Maybe the function is closely related even if it never
# touches the class, so you want to them nearby in the code. Even so,
# defining the function right before or after the class in the same
# module is close enough for all practical purposes.


# Leonardo Rochael, one of the technical reviewers of this book disagrees with my low opinion of staticme
# thod, and recommends the blog post “The Definitive Guide on How to Use Static, Class or Abstract Methods
# in Python” by Julien Danjou as a counter-argument. Danjou’s post is very good; I do recommend it. But it
# wasn’t enough to change my mind about staticmethod. You’ll have to decide for yourself.
# https://julien.danjou.info/guide-python-static-class-abstract-methods/

In [31]:
class Pizza:
    def __init__(self, ingredients):
        self.ingredients = ingredients
        
    def __repr__(self):
        return "Pizza({})".format(self.ingredients)
    
    @staticmethod
    def mix_ingredients(x, y):
        return x + y
    
    @classmethod
    def margherita(cls):
        return cls(['mozzarella', 'tomatoes'])
    
    @classmethod
    def prosciutto(cls):
        return cls(['mozzarella', 'tomatoes', 'ham'])
    
    @classmethod
    def margherita_with_olive_topping(cls):
        return cls(cls.mix_ingredients(['mozzarella', 'tomatoes'], ['olive']))

In [32]:
Pizza.margherita()


Out[32]:
Pizza(['mozzarella', 'tomatoes'])

In [34]:
Pizza.margherita_with_olive_topping()


Out[34]:
Pizza(['mozzarella', 'tomatoes', 'olive'])

In [37]:
Pizza.mix_ingredients(1, 2) is Pizza("test").mix_ingredients(1, 2)


Out[37]:
True

In [38]:
Pizza(["test"]).mix_ingredients(1, 2) is Pizza("stop").mix_ingredients(1, 2)


Out[38]:
True

In [39]:
Pizza("test").margherita() is Pizza.margherita()


Out[39]:
False

In [40]:
Pizza("test").margherita() is Pizza("test").margherita()


Out[40]:
False

In [41]:
Pizza("test").margherita() is Pizza("stop").margherita()


Out[41]:
False

In [ ]: