super() printable representation - python

sup = super(B, self) and sup2 = super(B, B) have indistinguishable representations, they both look like this:
<super: <class 'B'>, <B object>>
while super(B, self).spam gives a bound method, super(B, B) only works with class methods, so super(B, B).cls_spam(), like demonstrated below. You can't use it for regular methods, you get a normal function:
class A:
#classmethod
def cls_spam(cls):
print('A.cls_spam: cls =', cls)
def spam(self):
print('A.spam: self =', self)
class B(A):
def call_spam(self):
sup = super(B, self)
print('B.call_spam: sup =', sup)
print('B.call_spam: sup.spam =', sup.spam)
print('B.call_spam: sup.cls_spam =', sup.cls_spam)
sup.spam()
sup.cls_spam()
sup2 = super(B, B)
print('B.call_spam: sup2 =', sup2)
print('B.call_spam: sup2.css_spam =', sup2.cls_spam)
# can't call sup2.spam(), not without giving it self explicitly
sup2.cls_spam()
The following interactive session illustrates:
>>> b = B()
>>> b.call_spam3()
B.call_spam: sup = <super: <class 'B'>, <B object>>
B.call_spam: sup.spam = <bound method A.spam of <__main__.B object at 0x108830b50>>
B.call_spam: sup.cls_spam = <bound method A.cls_spam of <class '__main__.B'>>
A.spam: self = <__main__.B object at 0x108830b50>
A.cls_spam: cls = <class '__main__.B'>
B.call_spam: sup2 = <super: <class 'B'>, <B object>>
B.call_spam: sup2.css_spam = <bound method A.cls_spam of <class '__main__.B'>>
A.cls_spam: cls = <class '__main__.B'>
super() is a complicated subject, if the demonstrated above behavior is documented understanding it would help me a lot.
Using Python 3.5.3,Debian GNU/Linux 9.11 (stretch)

super() is meant to be useful both in class methods and in regular methods. In a classmethod there is no instance, you only have access to the class, so the second argument to super() accepts either an instance or a class. That at least is covered in the documentation for super():
If the second argument is an object, isinstance(obj, type) must be true. If the second argument is a type, issubclass(type2, type) must be true (this is useful for classmethods).
Bold emphasis mine.
Now, the primary task super() has to perform is to search for attributes along the Method Resolution Order (MRO) list of classes, given a starting point. The starting point is the first argument, but the MRO has to be taken from the second argument; if you use super() in class Foo, you can't know at that time if Foo might have been subclassed, which can alter the MRO of the second argument. So, for this purpose, super() tracks two pieces of information:
The class you want to use as a starting point for the search (the first argument)
The class from which the MRO is taken (the type of the second argument if it is an instance, or just the second argument, if it is a class).
There is also a 3rd piece of information, the instance or class to which attributes are bound, when you do an attribute lookup. That's simply the second argument itself, so either an instance or class. The repr() output reflects just the first two values, in large part because those are 'hidden' in that super(), without arguments, gets its arguments from the context and so you can't, as easily see what the starting point is, or what the MRO source is, but you can much more easily see the first argument to the method (so self or cls).
If you want to distinguish between your two super() instances, you can instead look at the __self__ attribute, which represents that 3rd piece of information:
>>> sup = super(B, b)
>>> sup.__self__
<__main__.B object at 0x108b4c520>
>>> sup2 = super(B, B)
>>> sup2.__self__
<class '__main__.B'>
The other two pieces of information you do see in the repr() output are the __thisclass__ and __self_class__ attributes, respectively:
>>> sup.__thisclass__, sup.__self_class__
(<class '__main__.B'>, <class '__main__.B'>)
This is easier to spot when you use a different class as the first argument:
>>> sup_a = super(A, b)
>>> sup_a
<super: <class 'A'>, <B object>>
>>> sup_a.__thisclass__, sup_a.__self_class__
(<class '__main__.A'>, <class '__main__.B'>)

Related

See all inherited classes in python

So I've done a bit of research online and it seems like the __subclasses__ method returns all the inherited classes for a python object (relevant stack overflow question)
On python3.8 I then tried the following:
class A:
a = 1
class B:
b = 2
class C(A, B):
c = 3
obj = C()
print('a: ', obj.a)
print('subclasses: ', C.__subclasses__())
and I get out
a: 1
subclasses: []
this shows that class C successfully inherits A and B, however they don't show up with the subclasses method? So is there something I'm missing in the __subclasses__ method, or has the method changed for python 3.8?
just combining the answerts from above :
my_class.__subclasses__ will return the classes, which subclass from my_class
C.__mro__ shows the inheritence hierarchy in your case :
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
object
/ \
A B
\ /
C
In short, __subclasses__ goes down the object hierarchy ladder and the __mro__ goes up. Good luck :)

Class inheritance via super with two arguments

In the below code, i replaced args with numbers to demonstrate what classes are inherited.
class Animal:
def __init__(self, animalName):
print(animalName, 'is an animal.');
class Mammal(Animal):
def __init__(self, mammalName):
print(mammalName, 'is a mammal.')
super().__init__(mammalName)
class CannotFly(Mammal):
def __init__(self, mammalThatCantFly):
print('2', "cannot fly.")
super().__init__('2')
class CannotSwim(Mammal):
def __init__(self, mammalThatCantSwim):
print('1', "cannot swim.")
super().__init__('1')
# Cat inherits CannotSwim and CannotFly
class Cat(CannotSwim, CannotFly):
def __init__(self):
print('I am a cat.');
super().__init__('Cat')
cat = Cat()
returns
I am a cat.
1 cannot swim.
2 cannot fly.
2 is a mammal.
2 is an animal.
Why is it not the below?
I am a cat.
1 cannot swim.
1 is a mammal.
1 is an animal.
2 cannot fly.
2 is a mammal.
2 is an animal.
There are effectively two call streams, no?
You can see the method resolution order (MRO) for Cat:
>>> Cat.mro()
[<class '__main__.Cat'>, <class '__main__.CannotSwim'>, <class '__main__.CannotFly'>, <class '__main__.Mammal'>, <class '__main__.Animal'>, <class 'object'>]
Each class appears once in the MRO, due to the C3 linearization algorithm. Very briefly, this constructs the MRO from the inheritance graph using a few simple rules:
Each class in the graph appears once.
Each class precedes any of its parent classes.
When a class has two parents, the left-to-right order of the parents is preserved.
("Linearization", because it produces a linear ordering of the nodes in the inheritance graph.)
super() is misnamed; a better name would have been something lie nextclass, because it does not use the current class's list of parents, but the MRO of the self argument. When you call Cat, you are seeing the following calls.
Cat.__init__
Cat.__init__ uses super to call CannotSwim.__init__
CannotSwim.__init__ uses super to call CannotFly.__init__
CannotFly.__init__ uses super to call Mammal.__init__
Mammal.__init__ uses super to call Animal.__init__
Animal.__init__ uses super to call object.__init__
object.__init__ does not use super (it "owns" __init__), so the chain ends there.
In particular, note #3: CannotSwim causes its "sibling" in the inheritance graph to be used, not its own parent.
Take a look at this post What does 'super' do in Python? - difference between super().__init__() and explicit superclass __init__()
Now what it says is that __init__ is called for every class in the instance's mro.
you can print that by doing print(Cat.__mro__) this will print out
(<class '__main__.Cat'>, <class '__main__.CannotSwim'>, <class '__main__.CannotFly'>, <class '__main__.Mammal'>, <class '__main__.Animal'>, <class 'object'>)
As you can see there is the order of the calls. Now, why '2' is used and not '1', see the answer in the comments by hussic

What is the difference between a method object and a function object in python? [duplicate]

I'm asking this question because of a discussion on the comment thread of this answer. I'm 90% of the way to getting my head round it.
In [1]: class A(object): # class named 'A'
...: def f1(self): pass
...:
In [2]: a = A() # an instance
f1 exists in three different forms:
In [3]: a.f1 # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1 # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1'] # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1'] # a function
Out[6]: <function __main__.f1>
What is the difference between the bound method, unbound method and function objects, all of which are described by f1? How does one call these three objects? How can they be transformed into each other? The documentation on this stuff is quite hard to understand.
A function is created by the def statement, or by lambda. Under Python 2, when a function appears within the body of a class statement (or is passed to a type class construction call), it is transformed into an unbound method. (Python 3 doesn't have unbound methods; see below.) When a function is accessed on a class instance, it is transformed into a bound method, that automatically supplies the instance to the method as the first self parameter.
def f1(self):
pass
Here f1 is a function.
class C(object):
f1 = f1
Now C.f1 is an unbound method.
>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True
We can also use the type class constructor:
>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>
We can convert f1 to an unbound method manually:
>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>
Unbound methods are bound by access on a class instance:
>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>
Access is translated into calling through the descriptor protocol:
>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
Combining these:
>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>
Or directly:
>>> types.MethodType(f1, C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
The main difference between a function and an unbound method is that the latter knows which class it is bound to; calling or binding an unbound method requires an instance of its class type:
>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>
Since the difference between a function and an unbound method is pretty minimal, Python 3 gets rid of the distinction; under Python 3 accessing a function on a class instance just gives you the function itself:
>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True
In both Python 2 and Python 3, then, these three are equivalent:
f1(C())
C.f1(C())
C().f1()
Binding a function to an instance has the effect of fixing its first parameter (conventionally called self) to the instance. Thus the bound method C().f1 is equivalent to either of:
(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())
is quite hard to understand
Well, it is quite a hard topic, and it has to do with descriptors.
Lets start with function. Everything is clear here - you just call it, all supplied arguments are passed while executing it:
>>> f = A.__dict__['f1']
>>> f(1)
1
Regular TypeError is raised in case of any problem with number of parameters:
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)
Now, methods. Methods are functions with a bit of spices. Descriptors come in game here. As described in Data Model, A.f1 and A().f1 are translated into A.__dict__['f1'].__get__(None, A) and type(a).__dict__['f1'].__get__(a, type(a)) respectively. And results of these __get__'s differ from the raw f1 function. These objects are wrappers around the original f1 and contain some additional logic.
In case of unbound method this logic includes a check whether first argument is an instance of A:
>>> f = A.f1
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead)
If this check succeeds, it executes original f1 with that instance as first argument:
>>> f(A())
<__main__.A object at 0x800f238d0>
Note, that im_self attribute is None:
>>> f.im_self is None
True
In case of bound method this logic immediately supplies original f1 with an instance of A it was created of (this instance is actually stored in im_self attribute):
>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>
So, bound mean that underlying function is bound to some instance. unbound mean that it is still bound, but only to a class.
A function object is a callable object created by a function definition. Both bound and unbound methods are callable objects created by a Descriptor called by the dot binary operator.
Bound and unbound method objects have 3 main properties: im_func is the function object defined in the class, im_class is the class, and im_self is the class instance. For unbound methods, im_self is None.
When a bound method is called, it calls im_func with im_self as the first parameter followed by its calling parameters. unbound methods call the underlying function with just its calling parameters.
Starting with Python 3, there are no unbound methods. Class.method returns a direct reference to the method.
Please refer to the Python 2 and Python 3 documentation for more details.
My interpretation is the following.
Class Function snippets:
Python 3:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return types.MethodType(self, obj)
Python 2:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj, objtype)
If a function is called without class or instance, it is a plain function.
If a function is called from a class or an instance, its __get__ is called to retrieve wrapped function:
a. B.x is same as B.__dict__['x'].__get__(None, B).
In Python 3, this returns plain function.
In Python 2, this returns an unbound function.
b. b.x is same as type(b).__dict__['x'].__get__(b, type(b). This will return a bound method in both Python 2 and Python 3, which means self will be implicitly passed as first argument.
What is the difference between a function, an unbound method and a bound method?
From the ground breaking what is a function perspective there is no difference.
Python object oriented features are built upon a function based environment.
Being bound is equal to:
Will the function take the class (cls) or the object instance (self) as the first parameter or no?
Here is the example:
class C:
#instance method
def m1(self, x):
print(f"Excellent m1 self {self} {x}")
#classmethod
def m2(cls, x):
print(f"Excellent m2 cls {cls} {x}")
#staticmethod
def m3(x):
print(f"Excellent m3 static {x}")
ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)
print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)
Outputs:
Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
The output shows the static function m3 will be never called bound.
C.m2 is bound to the C class because we sent the cls parameter which is the class pointer.
ci.m1 and ci.m2 are both bound; ci.m1 because we sent self which is a pointer to the instance, and ci.m2 because the instance knows that the class is bound ;).
To conclude you can bound method to a class or to a class object, based on the first parameter the method takes. If method is not bound it can be called unbound.
Note that method may not be originally part of the class. Check this answer from Alex Martelli for more details.
One interesting thing I saw today is that, when I assign a function to a class member, it becomes an unbound method. Such as:
class Test(object):
#classmethod
def initialize_class(cls):
def print_string(self, str):
print(str)
# Here if I do print(print_string), I see a function
cls.print_proc = print_string
# Here if I do print(cls.print_proc), I see an unbound method; so if I
# get a Test object o, I can call o.print_proc("Hello")

What is the default `__reduce__` in Python?

import pickle
class A:
pass
pickle.dumps(B().__reduce__())
yields
(<function _reconstructor at 0x1010143b0>, (<class '__main__.B'>, <class 'object'>, None))
What is this function "_reconstructor". It's neither B, B.__init__, nor B.__new__ as I expected.
I have to make 2 changes to get that result:
Change the name of your class from A to B.
Remove the outer pickle.dumps() call.
In any case, pickle is free to do anything it likes to reconstruct the object ;-) In this case, you can find the _reconstructor() function in Lib/copyreg.py.

What is the difference between a function, an unbound method and a bound method?

I'm asking this question because of a discussion on the comment thread of this answer. I'm 90% of the way to getting my head round it.
In [1]: class A(object): # class named 'A'
...: def f1(self): pass
...:
In [2]: a = A() # an instance
f1 exists in three different forms:
In [3]: a.f1 # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1 # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1'] # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1'] # a function
Out[6]: <function __main__.f1>
What is the difference between the bound method, unbound method and function objects, all of which are described by f1? How does one call these three objects? How can they be transformed into each other? The documentation on this stuff is quite hard to understand.
A function is created by the def statement, or by lambda. Under Python 2, when a function appears within the body of a class statement (or is passed to a type class construction call), it is transformed into an unbound method. (Python 3 doesn't have unbound methods; see below.) When a function is accessed on a class instance, it is transformed into a bound method, that automatically supplies the instance to the method as the first self parameter.
def f1(self):
pass
Here f1 is a function.
class C(object):
f1 = f1
Now C.f1 is an unbound method.
>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True
We can also use the type class constructor:
>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>
We can convert f1 to an unbound method manually:
>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>
Unbound methods are bound by access on a class instance:
>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>
Access is translated into calling through the descriptor protocol:
>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
Combining these:
>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>
Or directly:
>>> types.MethodType(f1, C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
The main difference between a function and an unbound method is that the latter knows which class it is bound to; calling or binding an unbound method requires an instance of its class type:
>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>
Since the difference between a function and an unbound method is pretty minimal, Python 3 gets rid of the distinction; under Python 3 accessing a function on a class instance just gives you the function itself:
>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True
In both Python 2 and Python 3, then, these three are equivalent:
f1(C())
C.f1(C())
C().f1()
Binding a function to an instance has the effect of fixing its first parameter (conventionally called self) to the instance. Thus the bound method C().f1 is equivalent to either of:
(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())
is quite hard to understand
Well, it is quite a hard topic, and it has to do with descriptors.
Lets start with function. Everything is clear here - you just call it, all supplied arguments are passed while executing it:
>>> f = A.__dict__['f1']
>>> f(1)
1
Regular TypeError is raised in case of any problem with number of parameters:
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)
Now, methods. Methods are functions with a bit of spices. Descriptors come in game here. As described in Data Model, A.f1 and A().f1 are translated into A.__dict__['f1'].__get__(None, A) and type(a).__dict__['f1'].__get__(a, type(a)) respectively. And results of these __get__'s differ from the raw f1 function. These objects are wrappers around the original f1 and contain some additional logic.
In case of unbound method this logic includes a check whether first argument is an instance of A:
>>> f = A.f1
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead)
If this check succeeds, it executes original f1 with that instance as first argument:
>>> f(A())
<__main__.A object at 0x800f238d0>
Note, that im_self attribute is None:
>>> f.im_self is None
True
In case of bound method this logic immediately supplies original f1 with an instance of A it was created of (this instance is actually stored in im_self attribute):
>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>
So, bound mean that underlying function is bound to some instance. unbound mean that it is still bound, but only to a class.
A function object is a callable object created by a function definition. Both bound and unbound methods are callable objects created by a Descriptor called by the dot binary operator.
Bound and unbound method objects have 3 main properties: im_func is the function object defined in the class, im_class is the class, and im_self is the class instance. For unbound methods, im_self is None.
When a bound method is called, it calls im_func with im_self as the first parameter followed by its calling parameters. unbound methods call the underlying function with just its calling parameters.
Starting with Python 3, there are no unbound methods. Class.method returns a direct reference to the method.
Please refer to the Python 2 and Python 3 documentation for more details.
My interpretation is the following.
Class Function snippets:
Python 3:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return types.MethodType(self, obj)
Python 2:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj, objtype)
If a function is called without class or instance, it is a plain function.
If a function is called from a class or an instance, its __get__ is called to retrieve wrapped function:
a. B.x is same as B.__dict__['x'].__get__(None, B).
In Python 3, this returns plain function.
In Python 2, this returns an unbound function.
b. b.x is same as type(b).__dict__['x'].__get__(b, type(b). This will return a bound method in both Python 2 and Python 3, which means self will be implicitly passed as first argument.
What is the difference between a function, an unbound method and a bound method?
From the ground breaking what is a function perspective there is no difference.
Python object oriented features are built upon a function based environment.
Being bound is equal to:
Will the function take the class (cls) or the object instance (self) as the first parameter or no?
Here is the example:
class C:
#instance method
def m1(self, x):
print(f"Excellent m1 self {self} {x}")
#classmethod
def m2(cls, x):
print(f"Excellent m2 cls {cls} {x}")
#staticmethod
def m3(x):
print(f"Excellent m3 static {x}")
ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)
print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)
Outputs:
Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
The output shows the static function m3 will be never called bound.
C.m2 is bound to the C class because we sent the cls parameter which is the class pointer.
ci.m1 and ci.m2 are both bound; ci.m1 because we sent self which is a pointer to the instance, and ci.m2 because the instance knows that the class is bound ;).
To conclude you can bound method to a class or to a class object, based on the first parameter the method takes. If method is not bound it can be called unbound.
Note that method may not be originally part of the class. Check this answer from Alex Martelli for more details.
One interesting thing I saw today is that, when I assign a function to a class member, it becomes an unbound method. Such as:
class Test(object):
#classmethod
def initialize_class(cls):
def print_string(self, str):
print(str)
# Here if I do print(print_string), I see a function
cls.print_proc = print_string
# Here if I do print(cls.print_proc), I see an unbound method; so if I
# get a Test object o, I can call o.print_proc("Hello")

Categories

Resources