Use the inspect module as Cameron suggested.
import inspect def analyze_class(clzz): """Analyze a class proof of concept""" assert inspect.isclass(clzz) for k, v in inspect.getmembers(clzz, lambda v: inspect.isfunction(v) or inspect.ismethod(v)): if inspect.ismethod(v): print(f"{v.__qualname__} -> class method ") if inspect.isfunction(v): sig = inspect.signature(v) names = [n for n, _ in sig.parameters.items()] if len(names) > 0 and str(names[0]) == 'self': print(f"{v.__qualname__}-> probably a bound method ") else: print(f"{v.__qualname__}-> probably a static method ") class Demo: @classmethod def us(cls): print(cls.__name__) @staticmethod def double(x): return x + x def triple(self, y): return 3 * y analyze_class(Demo) output: Demo.double-> probably a static method Demo.triple-> probably a bound method Demo.us -> class method From: Python-list <python-list-bounces+gweatherby=uchc....@python.org> on behalf of Ian Pilcher <arequip...@gmail.com> Date: Saturday, November 12, 2022 at 11:36 AM To: python-list@python.org <python-list@python.org> Subject: Re: Dealing with non-callable classmethod objects *** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. *** On 11/11/22 16:47, Cameron Simpson wrote: > On 11Nov2022 15:29, Ian Pilcher <arequip...@gmail.com> wrote: >> * Can I improve the 'if callable(factory):' test above? This treats >> all non-callable objects as classmethods, which is obviously not >> correct. Ideally, I would check specifically for a classmethod, but >> there doesn't seem to be any literal against which I could check the >> factory's type. > > Yeah, it does feel a bit touchy feely. > > You could see if the `inspect` module tells you more precise things > about the `factory`. > > The other suggestion I have is to put the method name in `_attrs`; if > that's a `str` you could special case it as a well known type for the > factory and look it up with `getattr(cls,factory)`. So I've done this. class _HasUnboundClassMethod(object): @classmethod def _classmethod(cls): pass # pragma: no cover _methods = [ _classmethod ] _ClassMethodType = type(_HasUnboundClassMethod._methods[0]) Which allows me to do this: def __init__(self, d): for attr, factory in self._attrs.items(): if callable(factory): value = factory(d[attr]) else: assert type(factory) is self._ClassMethodType value = factory.__func__(type(self), d[attr]) setattr(self, attr, value) It's a bit cleaner, although I'm not thrilled about having a throwaway class, just to define a literal that ought to be provided by the runtime. -- ======================================================================== Google Where SkyNet meets Idiocracy ======================================================================== -- https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!nx6jxVGHt4Gj1WplLAV4uuhaMyS7Ry0qTCGvZm7jLCj9GbK4vto49sfmP12TTgcAT6Akjz5hJWw9JoylO_FrgQ$<https://urldefense.com/v3/__https:/mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!nx6jxVGHt4Gj1WplLAV4uuhaMyS7Ry0qTCGvZm7jLCj9GbK4vto49sfmP12TTgcAT6Akjz5hJWw9JoylO_FrgQ$> -- https://mail.python.org/mailman/listinfo/python-list