A Python Class Miscellany¶
In this lesson we will discuss some of the available features of Class programming in Python.
We’ll cover the super
function (py3
) and how to use it.
We’ll discuss classmethods and how they can be used.
And we’ll discover static methods and discuss why they are rare in Python.
Super¶
The super
function is used in conjunction with subclasses.
The function takes as its two arguments a class object and an instance of that class.
The return value is a proxy for “the correct” parent class.
instead of:
class A(B):
def __init__(self, *args, **kwargs)
B.__init__(self, *args, **kwargs)
...
You can do:
class A(B):
def __init__(self, *args, **kwargs)
super(A, self).__init__(*args, **kwargs)
...
Be careful, though.
There are some subtle differences between these two usages.
One difference is in syntax.
It’s a bit hard to understand all of the stuff in a call to super
:
super(A, self).__init__(*args, **kwargs)
In english, we can read this like “create a super
object for the superclass of A.”
“Then, call the __init__ method on that object, using this instance as self
.”
It’s important to realize that the value returned by the call to super
is not the superclass object itself.
However, it is a proxy that ensures that the method you call will be invoked on the superclass object.
super().__init__(*args, **kwargs)
Note that in Python 3, it is possible (and encouraged) to call super
with no arguments.
It just does the right thing.
This syntax is not compatible with Python 2.x. If you have to maintain compatibility, you must provide the class name and self.
super() issues...¶
There are a few issues around using super that you must keep track of.
The method that is being called by super
needs to exist.
And every occurence of that method in superclasses must also use super
.
All the way back up to the first class that implements that method.
The long-and-short of it is that if you use it, be consistent in using it. When you do, it is part of the public interface for your class, like it or not. So you should document the fact that you use it.
When using super
, the call signature is important.
Both the method in which super is invoked, and the method invoked by it must have the same parameter list.
You should never call super with anything but the exact arguments that came in to the method you use it inside.
If you need to add one or more optional arguments, then you should always accept *args
and **kwargs
.
If you do this, then you should invoke super
like this:
super(MyClass, self).method(<args_declared>, *args, **kwargs)
Static and Class Methods¶
You’ve seen how methods of a class are bound to an instance when it is created.
And you’ve seen how the argument self
is then automatically passed to
the method when it is called.
And you’ve seen how you can call unbound methods on a class object so long as you pass an instance of that class as the first argument.
But what if you don’t want or need an instance?
Static Methods¶
A static method is a method that doesn’t get self:
In [36]: class StaticAdder(object):
....: def add(a, b):
....: return a + b
....: add = staticmethod(add)
....:
In [37]: StaticAdder.add(3, 6)
Out[37]: 9
Static methods can be written declaratively using the staticmethod
built-in as a decorator:
class StaticAdder(object):
@staticmethod
def add(a, b):
return a + b
Where are static methods useful?
Usually they aren’t
99% of the time, it’s better just to write a module-level function
An example from the Standard Library (tarfile.py):
class TarInfo(object):
# ...
@staticmethod
def _create_payload(payload):
"""Return the string payload filled with zero bytes
up to the next 512 byte border.
"""
blocks, remainder = divmod(len(payload), BLOCKSIZE)
if remainder > 0:
payload += (BLOCKSIZE - remainder) * NUL
return payload
Class Methods¶
A class method gets the class object, rather than an instance, as the first argument
In [41]: class Classy(object):
....: x = 2
....: def a_class_method(cls, y):
....: print(u"in a class method: ", cls)
....: return y ** cls.x
....: a_class_method = classmethod(a_class_method)
....:
In [42]: Classy.a_class_method(4)
in a class method: <class '__main__.Classy'>
Out[42]: 16
Once again, the classmethod
built-in can be used as a decorator for a
more declarative style of programming:
class Classy(object):
x = 2
@classmethod
def a_class_method(cls, y):
print(u"in a class method: ", cls)
return y ** cls.x
Unlike static methods, class methods are quite common.
They have the advantage of being friendly to subclassing.
Consider this:
In [44]: class SubClassy(Classy):
....: x = 3
....:
In [45]: SubClassy.a_class_method(4)
in a class method: <class '__main__.SubClassy'>
Out[45]: 64
Because of this friendliness to subclassing, class methods are often used to build alternate constructors.
Consider the case of wanting to build a dictionary with a given iterable of keys:
In [57]: d = dict([1,2,3])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-57-50c56a77d95f> in <module>()
----> 1 d = dict([1,2,3])
TypeError: cannot convert dictionary update sequence element #0 to a sequence
The stock constructor for a dictionary won’t work this way. So the dict object implements an alternate constructor that can.
@classmethod
def fromkeys(cls, iterable, value=None):
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
If not specified, the value defaults to None.
'''
self = cls()
for key in iterable:
self[key] = value
return self
(this is actually from the OrderedDict implementation in collections.py
)
See also datetime.datetime.now(), etc....