Entities
- class: blueprint for an instance
- instance: a constructed object of the class
- type: indicates the class/instance belongs to
- attribute: value of any object
- method: a callable attribute defined in the class
Instance Methods
- instance methods are variables define in the class
- when call through the instance, the instance is automatically passed as first argument (named self) to the method, e.g. below:
- because of this automatic passing of the instance, instance method are known as “bound” methods, i.e. bound to the instance upon which it is called
1 2 3 4 5 6 7 |
class Car(object): def break(): # note not passing 'self' here pass car = Car() car.break() # TypeError: break() takes no arguments(1 given) |
Encapsulation
- breaking encapsulation: setting the object value without using the setter method
1 2 3 4 5 6 7 |
class Integer(object): def set_value(self, val): try: val = int(val) except ValueError: return self.val = val |
- enforce encapsulation:
1 2 3 4 5 6 7 |
class Integer(object): def __init__(self, val): try: val = int(val) except ValueError: val = 0 self.val = val |
Python Class Variable v.s. Instance Attribute
Set class variable and instance attribute the same name to show the difference and look up order:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class SomeClass(object): val = 'this is a class variable' instance = SomeClass() print(instance.val) # this is a class variable instance.val = 'this is a instance variable' print(instance.val) # this is a instance variable del instance.val print(instance.val) # this is a class variable<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span> |
The attribute look up order: when request an attribute to an instance, it looks for that attribute in the instance first then in the class.
Access class variable in instance:
1 2 3 4 |
class SomeClass(object): val = 0 def __init__(self): SomeClass.val += 1 |
Polymorphism
- Different classes has same interface (i.e., method name), we can say that this group of related classes implement the same action
- Being able to work with different types to call methods without checking it’s possible and handle errors using exception handling is called DOCK TYPING
- Checking the type ahead of time is generally considered un-pythonic
An example from stackoverflow:
1 2 3 4 5 6 7 8 9 10 11 |
class Person(object): def pay_bill(): raise NotImplementedError class Millionare(Person): def pay_bill(): print "Here you go! Keep the change!" class GradStudent(Person): def pay_bill(): print "Can I owe you ten bucks or do the dishes?" |
or an built in len method as an interface implemented by list and set object:
1 2 3 4 5 6 7 |
l = [1, 2, 3] s = (1, 2, 3) l.__len__() /*3*/ s.__len__() /*3*/ |
Inheriting the Constructor
- __init__ can be inherited
- if a class doesn’t have an __init__ constructor, Python will check its parent class, as soon as it finds one, Python calls it and stops looking
- we can use the super() function to call method in the parents class, or we may want to initialize in the parent class
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Animal(object): def __init__(self, name): self.name = name class Dog(Animal): def __init__(self, name): super(Dog, self).__init__(name) ''' if the name of Animal class change in the future or rearrange the class hierarchy that Dog class inherite from another class and we want to call that constructor instead ''' |
Depth-First Order
Multiple inheritance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A(object): def run(self): print('run in A') class B(A): pass class C(object): def run(self): print('run in C') class D(B, C): pass D.mro() ''' the order is : [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>] ''' |
After Python 2.3, add a role for diamond shape ambiguous inheritance
(Make C inherit A in this example)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class A(object): def run(self): print('run in A') class B(A): pass class C(A): def run(self): print('run in C') class D(B, C): pass D.mro() ''' if the same class appears in a method resolution order, the earlier occurrences are removed from [__main__.D, __main__.B, __main__.A, __main__.C, __main__.A, object] to [__main__.D, __main__.B, __main__.C, __main__.A, object] ''' |
Abstract Class
- An abstract class is not designed to and can’t not construct instances, but subclassed by regular classes
- An interface / methods that defined in abstract class must be implement by its subclass
- The python abc module enables the creation of abstract class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import abc class GetterSetter(object): __metaclass__ = abc.ABCMeta @m.abstractmethod def set_val(self, input): return @m.abstractmethod def get_val(self): return class MyGetterSetter(GetterSetter): def set_val(self, input): self.val = input def get_val(self): return self.val<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span> |
Composition
- Inheritance could be brittle (a change may require changes elsewhere)
- Decoupled code could work independently or interactively
- classes interactions will work with an interface
- Not checking types: polymorphic and Pythonic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import StringIO class FileWriter(object): def __init__(self, file): self.file = file def write(self, text): # no type checking here as long as the interface stay the same self.file.write(text) f = open('text.txt', 'w') w1 = FileWriter(f) w1.write('writing into a file object...') w1.close sio = String.StringIO() w2 = FileWriter(sio) w2.write('writing into a string io object...') |
Here is an example for inheritance and composition working together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from myappengine import webfunc, run_app # inheritance: create class inherit from specific class class MainPage(webfunc.RequestHandler): def get(self): self.response.out.write('Here is the MainPage') class FAQPage(webfunc.RequestHandler): def get(self): self.response.out.write('Here is the FAQ Page') # composition: pass class to specific app engine function as an interface app = webfunc.WSGIApplication( [('/', MainPage), ('/faq', FAQPage)] ) def main(): run_app(app) |
Magic Functions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
In [1]: class Dog(object): def __init__(self, color, age, name): if not isinstance(age, int): raise ValueError self.color = color self.age = age self.name = name def __eq__(self, other_dog): return self.name == other_dog.name and self.color == other_dog.color def __lt__(self, other_dog): return self.age < other_dog.age def __gt__(self, other_dog): return self.age > other_dog.age mike = Dog('black', 1, 'Mike') jack = Dog('white', 4, 'Jack') michael = Dog('black', 10, 'Mike') In [2]: mike < jack Out[1]: True In [3]: mike == michael Out[2]: True |
Attribute Encapsulation
- @property should not encapsulate expensive operations, because attribute setting looks cheap
- @property only controls attributes that are expected
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Me(object): def __init__(self, var): self._var = var # encapsulate _var attribute to var @property def var(self) return self._var @var.setter def var(self, var): self._var = var @var.deleter def var(self) self._var = None In [1]: me = Me(1) In [2]: me.var Out[2]: 1 In [3]: me.var = 2 In [4]: del me.var |
“with” in Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class MyClass(object): def __enter__(self): print('We have entered "with"') return self # Use "with" to capture errors def __exit__(self, type value, traceback): print('We have leaved "with"') print('error type: {0}'.format(type)) def do(self): print('Do something...') In [1]: with MyClass() as c: c.do() Out[1]: We have entered "with" Do something... We have leaved "with" In [2]: with MyClass() as c: c.do() 5/0 Out[2]: error type: <type 'exceptions.ZeroDivisionError'> |
Custom Exception
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class MyError(Exception): def __init__(self, **kwargs): self.msg = kwargs.get('msg') def __str__(self): if self.msg: return 'Here\'s MyError Message: %s' % self.msg else: return 'Here\'s MyError' In [1]: raise MyError(msg='Huston, we have a problem.') Out[1]: --------------------------------------------------------------------------- MyError Traceback (most recent call last) <ipython-input-6-de465e9d3c17> in <module>() 9 return 'Here\'s MyError' 10 ---> 11 raise MyError(msg='Huston, we have a problem.') MyError: Here's MyError Message: Huston, we have a problem. |