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!'
"""
passclass 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()
"""
passA 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):
passThe 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: passclass 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