class ClassName(SuperClass):
'''docstring here'''
class_var = "someValue" # it's class level
def __init__(self):
pass
startUp() # called once when imported
# outside class
- Class should have docstrings too.
__init__()
is NOT the constructor, but it is the first method called AFTER an instance of class is created.- The first argument of every class method is
self
, referring to the current instance of the class. It’s likethis
in Java, but it’s not a reserved word, just a STRONG convention. - Member variables are not defined directly (since Python variables cannot be declared without assigning a value to it). Instead,
self.var
defines a memeber variablevar
. - Variables defined in class level (e.g.
class_var
) belongs to the class. Instance of a class can override this value, but it won’t affect other instances. However, by modifying it on class level withinst.__class__.class_var
, all instances with unmodifiedclass_var
will be affected.
Instantiating Classes
Call the class by name as if it were a function, and pass parameters required by its __init__()
function. The instantiation operation (“calling” a class object) creates an empty object. When a class defines an __init__()
method, class instantiation automatically invokes __init__()
for the newly-created class instance.
Every class instance has a built-in attribute, __class__
, which is the object’s class. __doc__
is the docstring.
Alternative Constructors
To define a class with more than one constructor, you should use a class method.
import time
class Date:
# Primary constructor
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
# Alternate constructor
@classmethod
def today(cls):
t = time.localtime()
return cls(t.tm_year, t.tm_mon, t.tm_mday)
Date.today() # invocation of the alternative constructor
To use the alternate constructor, you simply call it as a function.
Instantiate an Uninitialized Object
By calling Date.__new__(Date)
on the Date
class, you can create an uninitialized object of class Date
, as __init__()
is not invoked. Thus, it is now your responsibility to set the appropriate instance variables.
Methods and Attributes
Because Python is OOP language, methods and attributes are objects as well. They’re stored in a dict. Therefore, it is possible for an attribute to override method attributes with the same name.
Managed Attributes
Python attributes are normally defined as self.x
fields in methods. However, there are ways to define attributes that are generated from a method.
class Person:
def __init__(self, first_name):
self.first_name = first_name
# Getter function
@property
def first_name(self):
return self._first_name
# Setter function
@first_name.setter
def first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._first_name = value
# Deleter function (optional)
@first_name.deleter
def first_name(self):
raise AttributeError("Can't delete attribute")
The @property
annotation defines a managed attribute. Use managed attributes for the following use cases:
- Getter performs extra computation, or the attribute is derived from other attributes.
- Setter performs extra validation.
Note that managed attributes are accessed as attributes, i.e. without parentheses. Note that extending managed attributes in subclasses are not the same as redefining them. Read more before implementing them.
Access Control
Although Python provides no language-level access control, there are two conventions to define non-public attributes.
- A single leading underscore (
_
) indicates the attribute is “internal”, and should not be used outside the scope. Python does not prevent access to them, but it is considered a bad practice to use internal attributes. - Two leading underscores (
__
) indicates the attribute is private and should not be overridden in inheritance. A__x
attribute will be renamed to_<class_name>__x
so that it cannot be overridden.
For most code, you should probably just make your nonpublic names start with a single underscore. If, however, you know that your code will involve subclassing, and there are internal attributes that should be hidden from subclasses, use the double underscore instead.
Methods, Class Methods, and Static Methods
When defining methods in a class, there are two decorators to be applied to mark a method to be @classmethod
or @staticmethod
. The difference can be illustrated from the following example:
class A(object):
def foo(self, x):
print "executing foo(%s,%s)"%(self, x)
@classmethod
def class_foo(cls, x):
print "executing class_foo(%s,%s)"%(cls, x)
@staticmethod
def static_foo(x):
print "executing static_foo(%s)"%x
a = A()
Below is the usual way an object instance calls a method. The object instance, a
, is implicitly passed as the first argument.
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)
With class methods, the class of the object instance is implicitly passed as the first argument instead of self
. You can also call class_foo
using the class. In fact, if you define something to be a class method, it is probably because you intend to call it from the class rather than from a class instance. A.foo(1)
would have raised a TypeError
, but A.class_foo(1)
works just fine:
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
With static methods, neither self
(the object instance) nor cls
(the class) is implicitly passed as the first argument. They behave like plain functions except that you can call them from an instance or the class:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
Static methods are used to group functions which have some logical connection with a class to the class.
To summarize, foo
in class A
is just a function, but when you call a.foo
you don’t just get the function foo
, you get a “partially applied” version of the function with the object instance a
bound as the first argument to the function. foo
expects 2 arguments, while a.foo
only expects 1 argument.
a
is bound to foo
. That is what is meant by the term “bound” below:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
With a.class_foo
, a
is not bound to class_foo
, rather the class A
is bound to class_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Here, with a static method, even though it is a method, a.static_foo
just returns a good old function with no arguments bound. static_foo
expects 1 argument, and a.static_foo
and A.static_foo
expects 1 argument too.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
Special Methods and Attributes
Compact Memory Footprint
Python objects consists of dictionary. You can often greatly reduce the memory footprint of instances by adding the __slots__
attribute to the class definition. When you define __slots__
, Python uses a much more compact internal representation for instances. Instead of each instance consisting of a dictionary, instances are built around a small fixed-sized array, much like a tuple or list. Attribute names listed in the __slots__
specifier are internally mapped to specific indices within this array. A side effect of using slots is that it is no longer possible to add new attributes to instances — you are restricted to only those attribute names listed in the __slots__
specifier.
A common misperception of __slots__
is that it is an encapsulation tool that prevents users from adding new attributes to instances. Although this is a side effect of using slots, this was never the original purpose.
String Representation
__repr__()
: used byrepr(x)
, returns a printable representation of an objectx
. It should be a valid Python expression. When formatting the string, the special!r
formatting code can be used to indicate that the output of__repr__()
should be used instead of__str__()
, the default. For example,'Pair({0.x!r}, {0.y!r})'.format(self)
.__str__()
: used bystr(x)
, returns a nicely, string version of an objectx
. It is also called when youprint(x)
. If__str__()
is not given, it falls back torepr(x)
.
x.__bytes__()
: similar, but returns a bytes
object.
String Formatting
Define the __format__(self, format_spec)
method. The __format__()
method provides a hook into Python’s string formatting functionality, invoked by format(obj, format_spec)
. The default format_spec
is an empty string which usually gives the same effect as calling str(value)
.
Context Manager (with
statement)
To define a context manager, define two special methods in a class: __enter__()
and __exit__()
.
__enter__(self)
is called when entering a context (at the beginning of the with
statement). The return value of __enter__()
(if any) is placed into the variable indicated with the as
qualifier.
__exit__(self, *args)
is called when exiting the context (at the end of the with statement).
Descriptor
A descriptor is a class that implements the three core attribute access operations (get, set, and delete) in the form of __get__()
, __set__()
, and __delete__()
special methods. These methods work by receiving an instance as input. The underlying dictionary of the instance (__dict__
) is then manipulated as appropriate. Here is an example:
# Descriptor attribute for an integer type-checked attribute
class Integer:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
if instance is None:
return self
else:
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('Expected an int')
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
To use a descriptor, instances of the descriptor are placed into a class definition as class variables. For example:
class Point:
x = Integer('x')
y = Integer('y')
def __init__(self, x, y):
self.x = x
self.y = y
When you do this, all access to the descriptor attributes (e.g., x or y) is captured by the __get__()
, __set__()
, and __delete__()
methods.
Delegation
Delegation is a programming pattern where the responsibility for implementing a particular operation is handed off (i.e., delegated) to a different object. To implement such a way, we can implement __getattr__()
, __setattr()__
, or __delattr__()
methods, which acts like catch-all for attribute lookup, modification, and deletion.
class Proxy:
def __init__(self, obj):
self._obj = obj
# Delegate attribute lookup to internal obj
def __getattr__(self, name):
print('getattr:', name)
return getattr(self._obj, name)
# Delegate attribute assignment
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
print('setattr:', name, value)
setattr(self._obj, name, value)
# Delegate attribute deletion
def __delattr__(self, name):
if name.startswith('_'):
super().__delattr__(name)
else:
print('delattr:', name)
delattr(self._obj, name)
When using delegation to implement proxies, there are a few additional details to note.
- The
__getattr__()
method is actually a fallback method that only gets called when an attribute is not found. Thus, when attributes of the proxy instance itself are accessed (e.g., the_obj
attribute), this method would not be triggered. - It is also important to emphasize that the
__getattr__()
method usually does not apply to most special methods that start and end with double underscores. - The
__setattr__()
and__delattr__()
methods need a bit of extra logic added to separate attributes from the proxy instance intelf and attributes on the internal object_obj
. A common convention is for proxies to only delegate to attributes that don’t start with a leading underscore (i.e., proxies only expose the “public” attributes of the held instance).
Comparable
To make objects of a class comparable using normal comparison operators, the class can support comparison by implementing a special method for each comparison operator. For example, to support the >= operator, you define a __ge__()
method in the classes. Although defining a single method is usually no problem, it quickly gets tedious to create implementations of every possible comparison operator.
The functools.total_ordering
class decorator can be used to simplify this process. To use it, you decorate a class with it, and define __eq__()
and one other comparison method (__lt__
, __le__
, __gt__
, or __ge__
). The decorator then fills in the other comparison methods for you.
Support Basic Operators
A class can implement special methods to support basic operators. For example, a class implements __add__(self, other)
supports +
operator.
Inheritance
To inherit from a super class, enclose the super class in parenthesis between the class name and the colon. Python supports multiple inheritance, and super classes are separated with comma:
class C(A,B):
pass
To call a method in a superclass, use the super()
function:
class A:
def spam(self):
print('A.spam')
class B(A):
def spam(self):
print('B.spam')
super().spam() # Call parent spam()
Though in B
it can call A.spam(self)
, in diamond inheritance the method on the common super class will be invoked multiple times, while it will not using super()
.
Python’s Implementation of Inheritance
For every class that you define, Python computes what’s known as a method resolution order (MRO) list. The MRO list is simply a linear ordering of all the base classes. The __mro__
attribute for every class is the MRO list. To implement inheritance, Python starts with the leftmost class and works its way left- to-right through classes on the MRO list until it finds the first attribute match.
The actual determination of the MRO list itself is made using a technique known as C3 Linearization. It is actually a merge sort of the MROs from the parent classes subject to three constraints:
- Child classes get checked before parents
- Multiple parents get checked in the order listed.
- If there are two valid choices for the next class, pick the one from the first parent.
When you use the super()
function, Python continues its search starting with the next class on the MRO. As long as every redefined method consistently uses super()
and only calls it once, control will ultimately work its way through the entire MRO list and each method will only be called once. Thus, in multiple inheritance, if two unrelated superclasses define the same method, both of them will be invoked in a single super()
method. Rule of thumb:
- First, make sure that all methods with the same name in an inheritance hierarchy have a compatible calling signature (i.e., same number of arguments, argument names). This ensures that
super()
won’t get tripped up if it tries to invoke a method on a class that’s not a direct parent. - Second, it’s usually a good idea to make sure that the topmost class provides an implementation of the method so that the chain of lookups that occur along the MRO get terminated by an actual method of some sort.
Interface or Abstract Base Class
The usage of Interface or Abstract Base Class is to define a class from which you can perform type checking and ensure that certain methods are implemented in subclasses. To define an abstract base class, use the abc module. For example:
from abc import ABCMeta, abstractmethod
class IStream(metaclass=ABCMeta):
@abstractmethod
def read(self, maxbytes=-1):
pass
@abstractmethod
def write(self, data):
pass
@abstractmethod
must appear immediately before the function definition, if other decorators are used as well.
ABCs allow other classes to be registered as implementing the required interface, without actually inheriting from ABC. For example, you can do this:
import io
# Register the built-in I/O classes as supporting our interface
IStream.register(io.IOBase)
Mixin
Mixin classes appear in various places in the standard library, mostly as a means for extending the functionality of other classes. They are also one of the main uses of multiple inheritance. For instance, if you are writing network code, you can often use the ThreadingMixIn
from the socketserver
module to add thread support to other network-related classes. For example, here is a multithreaded XML-RPC server:
from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn
class ThreadedXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
pass
Mixin classes are never meant to be instantiated directly. They have to be mixed with another class that implements the required functionality. Similarly, the ThreadingMixIn
from the socketserver
library has to be mixed with an appropriate server class - it can’t be used all by itself.
Mixin classes typically have no state of their own. This means there is no __init__()
method and no instance variables. The specification of __slots__ = ()
is meant to serve as a strong hint that the mixin classes do not have their own instance data.
Below are examples of mixins for dict:
class LoggedMappingMixin:
'''
Add logging to get/set/delete operations for debugging.
'''
__slots__ = ()
def __getitem__(self, key):
print('Getting ' + str(key))
return super().__getitem__(key)
def __setitem__(self, key, value):
print('Setting {} = {!r}'.format(key, value))
return super().__setitem__(key, value)
def __delitem__(self, key):
print('Deleting ' + str(key))
return super().__delitem__(key)
class SetOnceMappingMixin:
'''
Only allow a key to be set once.
'''
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(str(key) + ' already set')
return super().__setitem__(key, value)
class StringKeysMappingMixin:
'''
Restrict keys to strings only
'''
__slots__ = ()
def __setitem__(self, key, value):
if not isinstance(key, str):
raise TypeError('keys must be strings')
return super().__setitem__(key, value)
When using mixins, they’re listed before the actual base class during inheritance.