Multiple inheritance can lead to multi level diamond diagram. In Python it is solved by C3 linearization.
Python doesn't need DI because it has super()
. It doesn't work like parent()
in other Typed programming languages such as Java or PHP.
In multiple inheritance super()
doesn't call the parents but the ancestors:
class Adam(): pass
class Eve(): pass
# First Family
class Joseph(Adam, Eve): pass
class Marie(Adam, Eve): pass
class Christ(Joseph, Marie): pass
# 2nd Family
class Homer(Adam, Eve): pass
class Marge(Adam, Eve): pass
class Lisa(Homer, Marge): pass
# Birth of Cartman...
class Cartman(Christ, Lisa): pass
help(Cartman)
"""
Help on class Cartman in module __main__:
class Cartman(Christ, Lisa)
| # Birth of Cartman...
|
| Method resolution order:
| Cartman
| Christ
| Joseph
| Adam
| Eve <- calling the Christ ancestors, skipping Lisa ancestors because they are the same
| Marie
| Lisa
| Homer
| Marge
"""
Which means when you call super()
you can't know in advance who it's going to call.
It uses MRO(Method Resolution Order). Example :
class MonsantoFactory:
def get_food(self):
return "OGM FOOD 😞"
class OrganicFactory:
def get_food(self):
return "ORGANIC FOOD ❤️"
class Restaurant(MonsantoFactory):
def make_pizza(self):
# Switching factory only works with self, not with super()
# which makes sense.
print("Making a Pizza with : {}".format(self.get_food()))
# If one switch order of inheritance you will switch Factory...
class NewOrganicRestaurant(OrganicFactory, Restaurant):
pass
if __name__ == '__main__':
Restaurant().make_pizza()
"""
output: Making a Pizza with : OGM FOOD
"""
NewOrganicRestaurant().make_pizza()
"""
output: Making a Pizza with : ORGANIC FOOD ❤️
"""
Sources:
- Raymond Hettinger - Super considered super! - PyCon 2015 (Old MRO)
- Understanding Python MRO - Class search path
Lambda are just anonymous functions.
# With name
something_funny = lambda joke_1, joke_2: "{} :) {} :D".format(joke_1.title(), joke_2.title())
something_funny('I like milk', 'I like you')
# As argument
my_list = ['Robert Carisson', 'John Den Bar', 'Paul Dim Smith']
my_list.sort(key=lambda name: name.split(" ")[-1].lower())
# Return Lambda
def quadratic(a, b, c):
return lambda x: a*x**2 + b*x + c
gnouf = quadratic(2, 3, -5)
gnouf(0)
quadratic(2, 3, -5)(0)
Sources:
Code that manipulates code.
Ex:
- Decorators
- Metaclasses
- Descriptors
Classes are basically dictionaries:
class Spam:
pass
test = type.__prepare__('Spam', (Base,))
All classes are type but you can create your own metaclass:
class MyType(type)
def __new__(cls, clsname, bases, clsdict):
if len(bases) > 1:
raise TypeException('No polymophism here!')
return super().__new__(cls, clsname, bases, clsdict)
class Base(metaclass=MyType):
pass
class Spam(Base):
pass
class Fish(Base):
pass
class SpamFish(Spam, Fish):
"""
raise error : 'No polymophism here!'
"""
pass
class User:
"""
Class variable
Ex: User.name
>>> 'Bourdeau'
"""
name = 'Bourdeau'
def __init__(self, first_name):
"""
Instance variable
Ex: test = User('Pierre-Henri')
test
>>> 'Pierre-Henri'
"""
self.first_name = first_name
@classmethod
def god_mod(cls):
"""
Class method
Ex: User.god_mod()
"""
pass
@staticmethod
def pinguin_mod():
"""
Static method
Ex: User.pinguin_mod()
"""
pass
A function that create a wrapper around a function. It's a function that works exactly like the original function but it adds some extra processing to it.
from functools import wraps
def oauth_login(func):
"""
Handle OAuth auth.
"""
@wraps(func)
def wrap(*args, **kwargs):
if not args[0].authorization_header:
args[0]._login()
return func(*args, **kwargs)
return wrap
@oauth_login
def get_very_secure_stuff(self):
pass
The problems with decorators is that you loose a lot of informations when trying to debug (print(), help(), etc.) but fortuanately wraps
imports metadata. So always use functools.wraps
.
But even better, a decorator can be used on a class:
@debug
class User: pass
class Descriptor:
def __init__(self, name=None):
self.name = name
def __get__(self, instance, cls):
print('Get', self.name)
def __set__(self, instance, value):
print('Set', self.name, value)
def __delete__(self, instance):
print('Delete', self.name)
There must be a better way
It’s Easier to Ask Forgiveness than Permission
Use properties and not setters/getters
functools.partial inspect.signature (useful to get signature of *args and **kwargs)
python -m timeit "dict()"
5000000 loops, best of 5: 42.5 nsec per loop
python -m timeit "{}"
50000000 loops, best of 5: 9.71 nsec per loop