class Car:
def
__init__(self):
print("Car object created.")
def drive():
print("driving a car..")
c = Car()
c.drive() ## this is equivalent to Car.drive(c) but
this will work
Car.drive()
TypeError: drive() takes 0 positional arguments but 1 was given
Solution is @staticmethod. With staticmethod we don't have to initialize object. staticmethod doesn't need any object we can call method directly from class and staticmethod cannot access class attributes.
Car.drive()
Car.drive()
class Car:
def
__init__(self):
print("Car object created.")
@staticmethod
def drive():
print("driving a car..")
c = Car()
c.drive()
output:
Car object created.
driving a car..
======
class Car:
def
__init__(self):
print("Car object created.")
@staticmethod
def drive():
print("driving a car..")
#@classmethod
def sell(c):
print("c =", c)
c = Car()
c.drive()
c.sell()
Car object created.
driving a car..
c = <__main__ .car="" 0x7fda7b572b00="" at="" object="">
to make it work, use classmethod
class Car:
def
__init__(self):
print("Car object created.")
@staticmethod ## we don’t pass anything
def drive():
print("driving a car..")
@classmethod
## always take class as first argument
def sell(c):
print("c =", c)
c = Car()
c.drive()
c.sell() or Car.sell()
output:
Car object created.
driving a car..
c =
staticmethod pass nothing whether u
call from class or instance. It will call function as it is. Staticmethod cannot
access class or instance attribute.
classmethod: whether you call for
class perspective or instance perspective always it will call class as first
argument
decorator is a pattern which allows
one function or object to decorate another object
def greet():
return "Hello world"
def convert_to_upper(fn):
def wrapper():
return fn().upper()
return wrapper
print("greet =", greet)
print(greet())
greet = convert_to_upper(greet)
print("greet =", greet)
print(greet())
output:
greet =
Hello world
greet = .wrapper at 0x7f790d200e18>
HELLO WORLD
Actual code should be:
def convert_to_upper(fn):
def wrapper():
return fn().upper()
return wrapper
@convert_to_upper
def greet():
return "Hello world"
print("greet =", greet)
print(greet())
##output
greet = .wrapper at 0x7f692a474e18>
HELLO WORLD
def outer_function():
message = 'Hi'
def inner_function():
print(message)
return inner_function()
outer_function()
def outer_function():
message = 'Hi'
def inner_function():
print(message)
return inner_function
my_func = outer_function()
my_func()
my_func()
my_func()
def outer_function(msg):
message = msg
def inner_function():
print(message)
return inner_function
hi_func = outer_function('Hi')
bye_func = outer_function('Bye')
hi_func()
bye_func()
def outer_function(msg):
def inner_function():
print(msg)
return inner_function
hi_func = outer_function('Hi')
bye_func = outer_function('Bye')
hi_func()
bye_func()
## all of above code is on closure. Now let's talk about decorator. It is similar to closure.
## a decorator is a function that takes another function as argument, adds some kind of functionality and return another function all of this without altering the source code of original function passed in
## let's replace outer function with decorator and inner function with wrapper. Instead of message we are going to accept function as argument
def decorator_function(original_function):
def wrapper_function():
return original_function()
return wrapper_function
def display():
print('display function ran')
decorated_display = decorator_function(display)
decorated_display()
## here decorated_display is equal to wrapper function
## decorator function allows us to easily add functionality to our existing function by adding that functionality inside our wrapper function
def decorator_function(original_function):
def wrapper_function():
print('wrapper executed this before {}'.format(original_function.__name__))
return original_function()
return wrapper_function
def display():
print('display function ran')
decorated_display = decorator_function(display)
decorated_display()
## or
def decorator_function(original_function):
def wrapper_function():
print('wrapper executed this before {}'.format(original_function.__name__))
return original_function()
return wrapper_function
@decorator_function
def display():
print('display function ran')
display()
#@decorator_function is equal to display = decorator_function(display)
## Now let's decorate two functions with same decorator
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print('wrapper executed this before {}'.format(original_function.__name__))
return original_function(*args, **kwargs)
return wrapper_function
@decorator_function
def display():
print('display function ran')
@decorator_function
def display_info(name,age):
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Nawraj', 32)
display()
## We can also use class as decorator instead of function
class decorator_class(object):
def __init__(self, original_function):
self.original_function = original_function ## tie our function with the instance of this class
## instead of wrpper function we need to use call method here
def __call__(self, *args, **kwargs):
print('call method executed this before {}'. format(self.original_function.__name__))
return self.original_function(*args, **kwargs)
@decorator_class
def display():
print('display function ran')
@decorator_class
def display_info(name,age):
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Nawraj', 32)
display()
## some practical examples
def my_logger(orig_func):
import logging
logging.basicConfig(filename='{}.log'.format(orig_func.__name__), level=logging.INFO)
def wrapper(*args, **kwargs):
logging.info(
'Ran with args: {}, and kwargs: {}'.format(args, kwargs))
return orig_func(*args, **kwargs)
return wrapper
def my_timer(orig_func):
import time
def wrapper(*args, **kwargs):
t1 = time.time()
result = orig_func(*args, **kwargs)
t2 = time.time() - t1
print('{} ran in: {}'.format(orig_func.__name__, t2))
return result
return wrapper
#@decorator_function
#def display():
# print('display function ran')
@my_logger
def display_info(name,age):
print('display_info ran with arguments ({}, {})'.format(name, age))
#display_info('Nawraj', 32)
## this will create a display_info.log file
## we can add this logging functionaltiy to any new function using this decorator. We don't have to add logging functionality to all new functions
## Now lets use timer decorator
import time
@my_timer
def display_info(name,age):
time.sleep(1)
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Nawraj', 32)
## we can also use two decorator to decorate same function
def my_logger(orig_func):
import logging
logging.basicConfig(filename='{}.log'.format(orig_func.__name__), level=logging.INFO)
def wrapper(*args, **kwargs):
logging.info(
'Ran with args: {}, and kwargs: {}'.format(args, kwargs))
return orig_func(*args, **kwargs)
return wrapper
def my_timer(orig_func):
import time
def wrapper(*args, **kwargs):
t1 = time.time()
result = orig_func(*args, **kwargs)
t2 = time.time() - t1
print('{} ran in: {}'.format(orig_func.__name__, t2))
return result
return wrapper
import time
@my_logger
@my_timer
def display_info(name,age):
time.sleep(1)
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Nawraj', 32)
# note:
@my_logger
@my_timer
is equivalent to display_info = my_logger(my_timer(display_info))
first my_timer will execute and then my_logger
## to preserve the identity of original function in case of nested decorator we use wraps to decorate all our wrapper
from functools import wraps
def my_logger(orig_func):
import logging
logging.basicConfig(filename='{}.log'.format(orig_func.__name__), level=logging.INFO)
@wraps(orig_func)
def wrapper(*args, **kwargs):
logging.info(
'Ran with args: {}, and kwargs: {}'.format(args, kwargs))
return orig_func(*args, **kwargs)
return wrapper
def my_timer(orig_func):
import time
@wraps(orig_func)
def wrapper(*args, **kwargs):
t1 = time.time()
result = orig_func(*args, **kwargs)
t2 = time.time() - t1
print('{} ran in: {}'.format(orig_func.__name__, t2))
return result
return wrapper
import time
@my_logger
@my_timer
def display_info(name,age):
time.sleep(1)
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Nawraj', 32)
No comments:
Post a Comment