combomethod does not create a method object upon access, but a specially wrapped function. Like methods, each access creates a new object, in this case a new function object.
class A: def __init__(self): self.data = 'instance' @combomethod def foo(param): if isinstance(param, A): print("This is an " + param.data + " method.") elif param is A: print("This is a class method.") >>> a = A() >>> A.foo <function foo at 0x00CFE810> >>> a.foo <function foo at 0x00CFE858> >>> A.foo() This is a class method. >>> a.foo() This is an instance method.
This is new for every access:
>>> A.foo is A.foo False >>> a.foo is a.foo False
foo really _wrapper hiding:
>>> A.foo.__code__.co_name '_wrapper'
When called from a class, the closure has obj == None (note that here "self" refers to a combo method that has a link to the original function object in self.method ):
>>> print(*zip(A.foo.__code__.co_freevars, A.foo.__closure__), sep='\n') ('obj', <cell at 0x011983F0: NoneType object at 0x1E1DF8F4>) ('self', <cell at 0x01198530: combomethod object at 0x00D29630>) ('objtype', <cell at 0x00D29D10: type object at 0x01196858>)
When called as an instance attribute, obj is an instance:
>>> print(*zip(a.foo.__code__.co_freevars, a.foo.__closure__), sep='\n') ('obj', <cell at 0x01198570: A object at 0x00D29FD0>) ('self', <cell at 0x01198530: combomethod object at 0x00D29630>) ('objtype', <cell at 0x00D29D10: type object at 0x01196858>)
Here is the original function stored in the combo:
>>> A.foo.__closure__[1].cell_contents.method <function foo at 0x00D1CB70> >>> A.foo.__closure__[1].cell_contents.method.__code__.co_name 'foo'
_wrapper executes self.method with either the class or the instance as the first argument given by obj:
if obj is not None: return self.method(obj, *args, **kwargs) else: return self.method(objtype, *args, **kwargs)