Skip to content

Instantly share code, notes, and snippets.

@bourdeau
Last active April 21, 2023 05:58
Show Gist options
  • Save bourdeau/031b0d0f2699ff1df049a9161c1fe345 to your computer and use it in GitHub Desktop.
Save bourdeau/031b0d0f2699ff1df049a9161c1fe345 to your computer and use it in GitHub Desktop.
Python Stuff

Inheritance & DI

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:

Cool Stuff in Python

Lambda

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)

Metaprogramming

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

Decorators

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

Descriptors: "Owing the dot operator"

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)

Idoms

There must be a better way

It’s Easier to Ask Forgiveness than Permission

Use properties and not setters/getters

Stuff I need to learn about

functools.partial inspect.signature (useful to get signature of *args and **kwargs)

Performance method

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


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment