You dont need to create an instance with a classmethod. Formatter.format(“foo”) vs Formatter().format(“foo”).
That also means you cant store state/configuration on the instance because there is no instance. Also inheritance and polymorphism may not work as well with classmethods - i.e. if I was planning to bring them into play on a classmethod I’d be very cautious about actual behavior.
In practice you usually want to use regular methods, except on class methods that create instances (which are often called factory methods). If you really don't need an instance, maybe a standalone function would do the job just as well? Python does not require functions to live only on classes (like Java).
As far as terminology goes, don’t sweat it besides exam considerations. Method vs function are not that different except that one has an instance (or the class in a @classmethod) as first argument.
Typically, if it's indented under a class XXX: declaration, I'd call a method. If it was standalone, I'd call it a function.
OK, read further only if you don't mind getting confused a bit...
Besides the distinction between all these things is rather fluid in practice. classmethods can be called from instances, functions can be dynamically added to classes...
Here's some amusing stuff. Most of it is curiosity, except for the create_me classmethod which is the main reason to use classmethods as factories, but it shows the boundaries are fluid-ish.
This is not stuff you'd typically want to do, but it does walk through some of the finer distinctions on how methods/functions on classes can behave.
class Anc:
@classmethod
def format(cls, msg):
"I dont need an instance to work"
print(f"\n{cls}.format({msg.upper()})")
def __init__(self, keep=1) -> None:
self.keep = keep
def format_instance(self, msg):
"I do and it allows me to use configuration stored in it"
print(f"\n{self}.format_instance({msg[:self.keep]=})")
@classmethod
def create_me(cls, *args, **kwargs):
""" a factory is the main reason for class methods """
return cls(*args, **kwargs)
class Child1(Anc):
@classmethod
def format(cls, msg):
print(f"\n{cls}.format({msg.lower()})")
@staticmethod
def format_static(msg):
print(f"\nLonely static without cls or instance 😭{msg}😭")
class Child2(Anc):
def format_instance(self, msg):
"replace with stars"
print(f"\n{self}.format_instance({'*' * len(msg)})")
def wannabe_method(self, msg):
"is this a function or a method?"
print(f"\nwannabe_method({self},{msg=}).")
def wont_work_as_method():
"too many arguments when called as method"
print(f"\nwont_work_as_method()")
Anc.format("calling format as a classmethod. No instance needed!")
anc = Anc(keep=2)
anc.format("calling format through an instance. It'll automatically get the class as first argument!")
Child1.format("calling Child1's format, which does lower")
Child2.format("calling Child2's format, which will up on Anc.format")
Child1.format_static("this is a static")
child1 = Child1(keep=3)
child1.format_instance("this message will get truncated...")
child2 = Child2()
try:
child2.added_method("hey just got added!")
except (AttributeError,) as e:
print(f"\n❌as expected this fails {e} cuz missing method")
Child2.added_method = wannabe_method
child2.added_method("hey just got added! self gets the instance magically")
try:
wannabe_method("nope not gonna work")
except (TypeError,) as e:
print(f"\n❌as expected this fails {e} because it only got 1 argument")
wannabe_method("FAKE INSTANCE!", "hackish work")
Child2.wont_work = wont_work_as_method
try:
child2.wont_work()
except (TypeError,) as e:
print(f"\n❌ oh no! no place for self {e}")
child2_through_factory = Child2.create_me()
child2_through_factory.format_instance("My God, it's full of stars")
child2_through_factory.format("to uppercase")
child1_through_factory = Child1.create_me()
child1_through_factory.format("TO LOWERCASE")
output:
<class '__main__.Anc'>.format(CALLING FORMAT AS A CLASSMETHOD. NO INSTANCE NEEDED!)
<class '__main__.Anc'>.format(CALLING FORMAT THROUGH AN INSTANCE. IT'LL AUTOMATICALLY GET THE CLASS AS FIRST ARGUMENT!)
<class '__main__.Child1'>.format(calling child1's format, which does lower)
<class '__main__.Child2'>.format(CALLING CHILD2'S FORMAT, WHICH WILL UP ON ANC.FORMAT)
Lonely static without cls or instance 😭this is a static😭
<__main__.Child1 object at 0x10a824460>.format_instance(msg[:self.keep]='thi')
❌as expected this fails 'Child2' object has no attribute 'added_method' cuz missing method
wannabe_method(<__main__.Child2 object at 0x10a824280>,msg='hey just got added! self gets the instance magically').
❌as expected this fails wannabe_method() missing 1 required positional argument: 'msg' because it only got 1 argument
wannabe_method(FAKE INSTANCE!,msg='hackish work').
❌ oh no! no place for self wont_work_as_method() takes 0 positional arguments but 1 was given
<__main__.Child2 object at 0x10a824220>.format_instance(**************************)
<class '__main__.Child2'>.format(TO UPPERCASE)
<class '__main__.Child1'>.format(to lowercase)