self
to allow classmethods see normal methods when instantiated?def fmt_atts(self, att):
"""A simple formatter"""
if hasattr(self, att): print '\t self.{:15s} = {}'.format(att, getattr(self, att))
else: print "\t self.{:15s} does not exist".format(att)
def _report_vals(func):
"""This just reports the current state of values"""
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
print '> calling func: {} :: {}'.format(func.__name__, func.__doc__)
res = func(*args, **kwargs)
for att in self.all_attrs:
fmt_atts(self, att)
print '> end func: {}'.format(func.__name__)
return res
return wrapper
class Example(object):
all_attrs = ['att_class', 'att_clsFnSet', 'att_initSet']
att_class = 'set_in_class'
@_report_vals
def __init__(self):
"""setting an attribute and calling self.test2"""
self.att_initSet = 'set_in_init'
self.set_atts_in_classmethod()
@classmethod
@_report_vals
def set_atts_in_classmethod(cls):
"""Sets attributes from within a classmethod"""
cls.att_class = 'set_in_classmethod'
cls.att_clsFnSet = 'set_in_classmethod'
@classmethod
@_report_vals
def view_atts_from_classmethod(cls):
"""View attributes from a classmethod"""
pass
@_report_vals
def view_atts_from_method(self):
"""View attributes from a normal method"""
pass
@_report_vals
def set_atts_in_method(self):
"""Sets attributes from within a normal method"""
self.att_class = 'set_in_method'
self.att_clsFnSet = 'set_in_method'
if __name__ == '__main__':
print '__without init'
print '> calling `Example.att_class` directly'
fmt_atts(Example, 'att_class')
print '# comment: notice that `A.att_class` has a persisting value'
Example.set_atts_in_classmethod()
Example.view_atts_from_classmethod()
print '\n__ post init: ex = Example()'
ex = Example()
print '# comment: notice that `self.att_initSet` has been set but not accessible from classmethod'
ex.set_atts_in_classmethod()
ex.view_atts_from_classmethod()
print '# comment: notice that `self.att_initSet` was set in __init__ but not avialable!'
print '# comment: however, `self.att_class` was set in another classmethod but *IS* accessible'
ex.view_atts_from_method()
print '# comment: notice that `self.att_initSet` is accessible from a normal method'
ex.set_atts_in_method()
ex.view_atts_from_classmethod()
print '# comment: It appears that classmethods can only access attributes set by other classmethods'
print '# comment: even when instanciated'
__without init
> calling `Example.att_class` directly
self.att_class = set_in_class
# comment: notice that `A.att_class` has a persisting value
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
__ post init: ex = Example()
> calling func: __init__ :: setting an attribute and calling self.test2
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet = set_in_init
> end func: __init__
# comment: notice that `self.att_initSet` has been set but not accessible from classmethod
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
# comment: notice that `self.att_initSet` was set in __init__ but not avialable!
# comment: however, `self.att_class` was set in another classmethod but *IS* accessible
> calling func: view_atts_from_method :: View attributes from a normal method
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet = set_in_init
> end func: view_atts_from_method
# comment: notice that `self.att_initSet` is accessible from a normal method
> calling func: set_atts_in_method :: Sets attributes from within a normal method
self.att_class = set_in_method
self.att_clsFnSet = set_in_method
self.att_initSet = set_in_init
> end func: set_atts_in_method
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
# comment: It appears that classmethods can only access attributes set by other classmethods
# comment: even when instantiated
Class (or static) methods are not meant to act on instances. They are methods that are common to all the instances of the class. Therefore, it is not logical for a class method to act on one specific instance.
These are generally utilitary functions. For example, a Vector
class could implement a dot_product
as a class method, rather than an instance method. This would make sense, because such an operation involves more than one instance.
Yes, there is, though it does not really make sense.
First off, a class method is able to see instance methods. For example:
class Foo:
def speak(self):
print("Hello")
@classmethod
def make_speak(cls, instance):
instance.speak()
foo = Foo()
Foo.make_speak(foo)
Output:
Hello
Let's go further, and change a little the make_speak
method:
@classmethod
def make_speak(cls):
print(cls.speak)
Foo.make_speak()
Output:
<function Foo.speak at 0x000001E58331EF28>
What this output reveals, is that the speak
method referred to by cls.speak
actually corresponds to a speak
method that has not been attached to any instance. This can be seen even clearer when calling that function:
@classmethod
def make_speak(cls):
cls.speak()
Foo.make_speak()
Output:
TypeError: speak() missing 1 required positional argument: 'self'
An TypeError
is raised, because the speak
method from the Foo
class needs one argument, which is not passed.
Disclaimer: The following is not a good solution.
The speak
method needs an instance as argument to be run. Then, just give it:
@classmethod
def make_speak(cls, instance):
cls.speak(instance)
foo = Foo()
Foo.make_speak(foo)
Output:
Hello
But by doing this, you want the make_speak
method to act on a specific Foo
instance, so as to call its speak
method.
This is exactly tantamount to directly calling foo.speak
.
This example might seem obvious and dull. However, it highlights the fact that if a class method takes exactly one instance as argument (and potentially other things that are not instances of this class), then it is equivalent to an instance method.