[Tutor] Counting objects: method vs. staticmethod vs. classmethod

Peter Otten __peter__ at web.de
Mon Jun 22 10:46:21 EDT 2020


boB Stepp wrote:

> What I want to do:  Every time I instantiate an object of a particular
> class I wish to generate a unique id dependent on a particular
> attribute value and the current number of objects created with that
> attribute value.  Playing around tonight I have come up with the same
> technique expressed slightly differently.  Which is best for what I
> want to do?  Is there a better way to do what I want to do?
> 
> 3.7.5:  import random
> 3.7.5:  from collections import Counter
> 3.7.5:  import string
> 3.7.5:  items = list(string.ascii_uppercase)
> 3.7.5:  def make_objs(n, items, cls):
> ...         for i in range(n):
> ...             obj = cls(random.choice(items))
> ...
> 3.7.5:  class A:
> ...         c = Counter()
> ...         def count(item):
> ...             A.c.update([item])
> ...         def __init__(self, item):
> ...             A.count(item)
> ...
> 3.7.5:  make_objs(1000, items, A)
> 3.7.5:  A.c
> Counter({'U': 49, 'J': 47, 'Q': 47, 'A': 45, 'X': 45, 'Z': 44, 'S':
> 43, 'D': 42, 'T': 41, 'F': 41, 'O': 41, 'E': 40, 'Y': 38, 'W': 37,
> 'K': 37, 'H': 37, 'G': 37, 'N': 36, 'M': 35, 'B': 34, 'C': 33, 'R':
> 33, 'V': 32, 'I': 32, 'L': 29, 'P': 25})
> 3.7.5:  sum(A.c.values())
> 1000
> 3.7.5:  class B:
> ...         c = Counter()
> ...         @staticmethod
> ...         def count(item):
> ...             B.c.update([item])
> ...         def __init__(self, item):
> ...             B.count(item)
> ...
> 3.7.5:  make_objs(1000, items, B)
> 3.7.5:  B.c
> Counter({'O': 52, 'X': 51, 'M': 50, 'G': 49, 'A': 49, 'P': 46, 'I':
> 45, 'H': 43, 'Z': 42, 'J': 42, 'L': 42, 'Q': 42, 'F': 41, 'D': 37,
> 'E': 37, 'N': 36, 'S': 33, 'C': 32, 'K': 32, 'R': 31, 'U': 31, 'W':
> 31, 'V': 30, 'T': 26, 'Y': 26, 'B': 24})
> 3.7.5:  class C:
> ...         c = Counter()
> ...         @classmethod
> ...         def count(cls, item):
> ...             C.c.update([item])
> ...         def __init__(self, item):
> ...             C.count(item)
> ...
> 3.7.5:  make_objs(1000, items, C)
> 3.7.5:  C.c
> Counter({'R': 45, 'C': 45, 'Y': 44, 'A': 44, 'B': 44, 'K': 43, 'X':
> 43, 'E': 43, 'H': 42, 'P': 41, 'S': 41, 'Q': 41, 'L': 38, 'F': 38,
> 'I': 38, 'M': 37, 'U': 37, 'J': 37, 'O': 36, 'W': 35, 'G': 34, 'T':
> 34, 'V': 31, 'D': 31, 'Z': 30, 'N': 28})
> 
> The final counts are immaterial.  I would use the value of c at the
> time of instantiation to generate its id attribute.  The id would be
> used for __str__() to generate something like:  "R22 MyObjectName".

If you want to associate behaviour with a class that does not depend on the 
instance use static methods; if you want/expect that behaviour to change in 
subclasses use a class method.

> ...         @classmethod
> ...         def count(cls, item):
> ...             C.c.update([item])

That implementation defeats the purpose of class methods.

>From within the class method access the class via the first argument:

    @classmethod
    def count(cls, item):
        cls.c.update([item])

That way you can tweak the code in subclassses. Likewise the initializer

> ...         def __init__(self, item):
> ...             C.count(item)

becomes

    def __init__(self, item):
        self.count(item)

A complete example based on the code you posted:

$ cat classmethod_demo.py       
import random
import string

from collections import Counter


class C:
    c = Counter()

    @classmethod
    def count(cls, item):
        cls.c.update([item])

    def __init__(self, item):
        self.count(item)

class D(C):
    c = Counter()

classes = C, D

for i in range(10):
    code = random.choice(string.ascii_uppercase)
    cls = random.choice(classes)
    obj = cls(code)

for cls in classes:
    print("Created {} instances of {}:".format(
        sum(cls.c.values()), cls.__name__
    ))
    print(cls.c)
    print()

$ python3 classmethod_demo.py
Created 4 instances of C:
Counter({'M': 2, 'K': 1, 'Z': 1})

Created 6 instances of D:
Counter({'J': 2, 'H': 1, 'D': 1, 'N': 1, 'I': 1})





More information about the Tutor mailing list