Python Language OOP
External
Internal
TODO
- How to call a method from inside the constructor. If I try, the compiler says "Unresolved reference"
- PyOOP "Introducing object-oriented"
- PyOOP "Objects and classes"
- PyOOP "Specifying attributes and behaviors"
- PyOOP "Data describes objects"
- PyOOP "Behaviors are actions"
- PyOOP "Hiding details and creating the public interface"
- PyOOP "Composition"
- PyOOP "Inheritance"
- PyOOP "Inheritance provides abstraction"
- PyOOP "Multiple inheritance", "Case study"
- PyOOP "Objects in Python"
- PyOOP "Creating Python classes"
- PyOOP "Adding attributes", "Making it do something", "Talking to yourself", "More arguments", "Initializing the object"
- PyOOP "Explaining yourself"
- PyOOP "Who can access my data?"
- PyOOP "When Objects are Alike"
- PyOOP "Basic inheritance"
- PyOOP "Extending built-ins"
- PyOOP "Overriding and super"
- PyOOP "Multiple inheritance" + "The diamond problem" + "Different sets of arguments"
- PyOOP "Polymorphism"
- PyOOP "Abstract base classes" + Using an abstract base class" + "Creating an abstract base class" + "Demystifying the magic" + "Case study"
- PyOOP "When to Use Object-Oriented Programming" + "Treat objects as objects"
- PyOOP "Manager objects"
- PyOOP "Removing duplicate code" + "In practice" + "Case study"
- PyOOP "Empty objects"
- PyOOP "Using functions as attributes"
- PyOOP "Callable objects" + "Case study"
- PyOOP "Serializing objects" + "Customizing pickles" + "Serializing web objects" + "Case study"
Overview
Everything in Python is an object, from numbers to modules. The language offers possibility of declaring custom types, and creating object instances for those types. Unlike modules, there can be multiple instances of the same type. Creation of new types is achieved by using the class
syntax. The custom types such declared are no different, intrinsically, from the types already existing in the runtime. The existing types can be extended.
An object contains data, in form of variables called attributes and behavior, in form of functions called methods.
Class
A class is the definition of an object. A new type (class) is defined with the class
keyword. "Class" and "type" mean pretty much the same thing.
class MyClass:
def __init__(self):
pass
The class may be declared with parentheses, but the IDE static checks find those as "redundant":
class MyClass():
...
Classes are typically defined at the module level, but they can also be defined inside a function or a method. A class created inside the scope of a function cannot be accesses from anywhere outside that function.
Class Names
Attributes and Properties
Methods
An object contains behavior, in form of functions called methods. Methods are callable attributes.
Unlike in other languages, all methods are public.
Calling class methods from inside class methods:
class MyClass:
def __init__(self):
self.internal()
def internal(self):
pass
Special Methods
__init__()
The object instance initialization method, which is used to do anything that's needed to distinguish an object instance from other object instances of the same class. The self
argument specifies that it refers to the individual object itself. self
is not a reserved word in Python, but it is used conventionally, and it's a good idea to stick with the convention.
class MyClass:
def __init__(self, name, color):
self.name = name
self.color = color
An __init__()
method is not required.
It is good practice to define all attributes in a contiguous block in __init__()
, even if we have to assign them to None
, and possibly document them. If we call other "private" methods from __init__()
that initialize attributes, an attempt to initialize an attribute not declared in __init__()
will be signaled as a warning: "Instance attribute key defined outside __init__".
Can there be more than one __init__() methods, each with a different set of arguments, like overloaded constructors?
__str__()
__str__()
is used by print()
, str()
and the string formatters to produce a string representation of an instance.
class MyClass:
...
def __str__(self):
return self.name + ', ' + self.color
__eq__()
__hash__()
Getters and Setters
Static Methods
class SomeClass:
@staticmethod
def some_static_method():
...
Class Methods
class SomeClass:
@classmethod
def some_class_method(cls):
...
Static Initialization at Class Level
To perform an initialization once, upon creation of the type in the class' namespace, place the code directly under the code definition: All methods placed into the class are also "executed" - like when defining a function in the global namespace - but they are not calling.
class MyClass:
# executed only once upon creation of the type
pattern = re.compile(r'^(\w+):(\w+)-(\w+)$')
def __init__(self):
...
def my_method(self):
...
The static variable can be accessed inside the class (and outside) by prefixing it with the class name, or, within an instance method, by prefixing it with self
:
class MyClass:
some_var = 'something'
def some_method(self):
print('some_var value', MyClass.some_var)
print('some_var value', self.some_var)
Class Introspection
Access to Class Instance from the Class Code
Use the variable self.__class__
.
__class__
also resolves, but in case of a class hierarchy, it won't behave polymorphically, it will resolve to the superclass, not the actual class.
Access to Class Name from the Class Code
Use the variable self.__class__.__name__
.
Type Checking
The type of a class instance can be displayed with type()
.
class MyClass:
...
i = MyClass()
print(type(c))
will display:
<class '__main__.MyClass'>
Whether the instance if of specific type can be verified with:
assert type(i) is MyClass
Alternatively, isinstance()
built-in is available:
assert isinstance(o, MyClass)
Checking whether an Instance is a Class
class A:
pass
assert type(A) is type
Checking whether a Class is Subclass of Another Class
Checked with the issubclass()
built-in:
class A:
pass
class B(A):
pass
assert issubclass(B, A)
Storing Classes in Files
A Class per File (Module)
This pattern mimics Java's approach of storing one public class per file.
.
└─ src
└─ some_package
└─ some_subpackage_1
└─ some_subpackage_2
└─ SomeClass.py
Class declaration:
class SomeClass():
pass
Class consumption:
from some_package.some_subpackage_1.some_subpackage_2.SomeClass import SomeClass
sc = SomeClass()
Multiple Classes per File(Module)
Objects
In Python, everything is an object. To create an instance of a class, use the class name followed by left parenthesis, followed by constructor arguments, followed by the right parenthesis.
o2 = SomeOtherClass('something')
If only the class name without parentheses is used, the code will work, but the variable will contain a reference to the class, not the instance of the class.
Identity of an Object Instance
The identity of an object instance is an integer which is guaranteed to be unique and constant for the duration of the object's lifetime. The identity of any object can be obtained with the built-in function id()
. Two objects with non-overlapping lifetimes may have the same id()
value. As an implementation detail, the identity is the address of the object in memory.
Object Instantiation
Via Class Name
class SomeClass:
def __init__(self, arg):
...
sc = SomeClass('blue')
Via Class Instance
Given access to the instance of the class defined above, an object of that class can be instantiated by using the same constructor syntax on the class instance instead of the class name:
# this is the SomeClass class
cls = ...
sc = cls('blue')
Initialization
Inheritance
Overriding
If a method is defined in the base class but not defined in the subclass, the base class method is available to the subclass instances.
class SomeClass:
def some_method(self):
print('this is SomeClass.some_method()')
class SomeSubClass(SomeClass):
pass
SomeSubClass().some_method()
# will print:
# this is SomeClass.some_method()
Polymorphism
Duck Typing
.