<jupyter_start><jupyter_text>Practical Python Design Patterns  What is a class in Python?<jupyter_code>class Test:
    pass
a = Test()
a
type(a)
type(Test)
Test<jupyter_output><empty_output><jupyter_text>What is type in Python?<jupyter_code>type('TestWithType', (object,), {})
type?
test_with_type = type('TestWithType', (object,), {})
a1 = test_with_type()
a1
dir(a1)
type('TestWithType', (object,), {})()<jupyter_output><empty_output><jupyter_text>Life Cycle involved in a class<jupyter_code>class TestClass:

    def __new__(cls, *args, **kwargs):
        print('new method called')
        instance = super(TestClass, cls).__new__(cls, *args, **kwargs)
        return instance

    def __call__(self, a, b, c):
        print('call method called')
        return a * b * c

    def __init__(self):
        super(TestClass, self).__init__()
        print('init method called')
a = TestClass()
a(1,2,3)<jupyter_output>call method called<jupyter_text>Decorators<jupyter_code>def func1(func2):
    def wrapper():
        return func2().lower()
    return wrapper
@func1
def get_value():
    return 'aBcDeFgH'
get_value()
func1(get_value)()
class MyDecorator:    
    def __init__(self, case):
        self.is_lower_case = False if case == 'upper' else True
        
    def __call__(self, func2):
        def wrapper():
            return func2().lower() if self.is_lower_case else func2().upper()
        return wrapper
user_input = 'upper'
@MyDecorator(user_input)
def get_value():
    return 'aBcDeFgH'
get_value()
from flask import Flask
app = Flask(__name__)

@app.route('/hw')
def hello_world():
    return 'Hello World!'

app.run()<jupyter_output>* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [17/Mar/2018 11:44:46] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [17/Mar/2018 11:44:46] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [17/Mar/2018 11:44:51] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [17/Mar/2018 11:44:51] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [17/Mar/2018 11:44:54] "GET //hw HTTP/1.1" 404 -
127.0.0.1 - - [17/Mar/2018 11:44:59] "GET //hw HTTP/1.1" 404 -
127.0.0.1 - - [17/Mar/2018 11:45:03] "GET /hw HTTP/1.1" 200 -<jupyter_text>Back to Meta classes  What is type? 'type' defines how a class behaves in Python.  Got it. Well then - Can I change 'how' a class behaves in Python? - MetaClasses<jupyter_code>class MySingletonMeta(type):
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(MySingletonMeta, cls).__call__(*args)
        return cls._instances[cls]
class MySingletonClass(metaclass=MySingletonMeta):
    def __init__(self):
        self.i = 1
a = MySingletonClass()
b = MySingletonClass()
a is b
type(a), id(a) , type(b), id(b)
from abc import ABCMeta, ABC, abstractmethod
ABCMeta?
class MyAbstractClass(metaclass=ABCMeta):
    def __init__(self):
        pass

    @abstractmethod
    def my_abstract_method(self):
        pass
MyAbstractClass()
class MyChildClass(MyAbstractClass):
    
    def __init__(self):
        pass
    
    def my_abstract_method(self):
        pass
    
    @staticmethod
    def my_static_method():
        print('I am a static method')
        
    @classmethod
    def my_class_method(cls):
        print('Class method called')

mcc = MyChildClass()
mcc.my_static_method()
MyChildClass.my_class_method()<jupyter_output>Class method called<jupyter_text>Combine two meta classes<jupyter_code>class MySingletonABCMeta(ABCMeta):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(MySingletonABCMeta, cls).__call__(*args)
        return cls._instances[cls]
class MyAbstractSingletonClass(metaclass=MySingletonABCMeta):
    def __init__(self):
        pass

    @abstractmethod
    def my_abstract_method(self):
        pass
MyAbstractSingletonClass()
class MyAbstractSingletonChild(MyAbstractSingletonClass):
    def __init__(self):
        pass
    
    def my_abstract_method(self):
        pass
a1 = MyAbstractSingletonChild()
b1 = MyAbstractSingletonChild()
type(a1), id(a1), type(b1), id(b1)<jupyter_output><empty_output><jupyter_text>Hashable Objects in Python  What items are hashable in Python?<jupyter_code>a = [1,2]
{a:1}
print(a.__hash__)
hash((1,2))
from collections import Hashable
class HashableObject(Hashable):
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    def __eq__(self, other):
        return True if isinstance(other, self.__class__) and self.get_key() == other.get_key() else False

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        return hash(self.get_key())

    @abstractmethod
    def get_key(self):
        return self.__dict__.keys()
class MyHashableClass(HashableObject):
    def __init__(self, a,b,c):
        self.a = a
        self.b = b
        self.c = c
        
    def get_key(self):
        return self.a, self.b
        
a1 = MyHashableClass(1, 2, 3)
a2 = MyHashableClass(4, 5, 6)
a3 = MyHashableClass(4, 5, 8)
a1 == a2
a2 == a3<jupyter_output><empty_output><jupyter_text>Comparable Objects in Python<jupyter_code>class ComparableObject:
    def __init__(self):
        pass

    def __eq__(self, other):
        return True if isinstance(other, self.__class__) and self.__dict__ == other.__dict__ else False

    def __ne__(self, other):
        return not self == other
class MyComparableObject(ComparableObject):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
c1 = MyComparableObject(1, 2, 3)
c2 = MyComparableObject(4, 5, 6)
c3 = MyComparableObject(1, 2, 3)
c1 == c2
c1 == c3<jupyter_output><empty_output><jupyter_text>Enum vs Polled Objects<jupyter_code>from enum import Enum
class MyEnumeration(Enum):
    ONE = 'one'
    TWO = 'two'
    
    @staticmethod
    def get_enum(inp):
        for e in MyEnumeration:
            if e.value == inp:
                return e
            
a = MyEnumeration.get_enum('one')
a<jupyter_output><empty_output><jupyter_text>Where Enums do not work and Solution<jupyter_code>class MyBeanMeta(type):
    _instances = {}

    def __call__(cls, *args):
        print(args)
        key = tuple((cls, args))
        if key not in cls._instances:
            cls._instances[key] = super(MyBeanMeta, cls).__call__(*args)
        return cls._instances[key]
class MyBeanClass(metaclass=MyBeanMeta):
    def __init__(self, a ):
        self.a = a

bn1 = MyBeanClass(1)
bn2 = MyBeanClass(2)
bn3 = MyBeanClass(3)
bn4 = MyBeanClass(1)
id(bn1), id(bn2), id(bn3), id(bn4)<jupyter_output><empty_output><jupyter_text>Use Generator wherever possible<jupyter_code>def with_generator():
    result = 0
    for x in get_data():
        result += x

def get_data():
    i = 0
    while i < 10000:
        yield i
        i += 1
def without_generator():
    # print("Without Generator")
    result = 0
    for x in get_data_wo_gen():
        result += x

def get_data_wo_gen():
    ret_val = []
    for i in range(0, 10000):
        ret_val.append(i)

    return ret_val<jupyter_output><empty_output><jupyter_text>Use native list comprehensions wherever possible<jupyter_code>a = []
for i in range(1000):
    a.append(i)
a = [i for i in range(1000)]
sum([i for i in range(1000)])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1250340506673931, 'fraction_numerical': 0.03650231544538273, 'mean_word_length': 3.3067448680351905, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 8, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '448121', 'license': 'MIT', 'n_tokens_mistral': 2766, 'n_tokens_neox': 2618, 'n_words': 623, 'path': '2018/2018-03-17/Practical Python Design Patterns/PythonDesignPatterns.ipynb', 'repo_name': 'GoelJatin/HydPy-meetups', 'size': '7342'}