[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