Skip to content

Commit d4cbb7a

Browse files
committed
awesome commit
1 parent 5402024 commit d4cbb7a

File tree

20 files changed

+515
-49
lines changed

20 files changed

+515
-49
lines changed

Closures/closure_1.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# what is closure
2+
3+
4+
def outer_func(msg):
5+
message = msg
6+
7+
def inner_func():
8+
print(message)
9+
10+
return inner_func
11+
12+
13+
# my_func is a closure function, even outer_func is executed, my_func still has access to the message variable
14+
hello_func = outer_func("hello")
15+
hi_func = outer_func("hi")
16+
print(hello_func.__name__)
17+
hi_func()
18+
hello_func()

Closures/closure_logger.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import logging
2+
from pathlib import Path
3+
4+
script_location = Path(__file__).absolute().parent
5+
file_location = script_location / "example.log"
6+
7+
8+
logging.basicConfig(filename=file_location, level=logging.INFO, filemode="a")
9+
10+
11+
def logger(func):
12+
13+
# take any number of args
14+
def log_func(*args):
15+
logging.info('Running "{}" with arguments {}'.format(func.__name__, args))
16+
print(func(*args))
17+
18+
return log_func
19+
20+
21+
def add(x, y):
22+
return x + y
23+
24+
25+
def sub(x, y):
26+
return x - y
27+
28+
29+
log_add = logger(add)
30+
log_add(3, 3)
31+
log_add(4, 5)
32+
33+
log_sub = logger(sub)
34+
log_sub(3, 3)

Closures/example.log

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
INFO:root:Running "add" with arguments (3, 3)
22
INFO:root:Running "add" with arguments (4, 5)
3-
INFO:root:Running "sub" with arguments (10, 5)
4-
INFO:root:Running "sub" with arguments (20, 10)
3+
INFO:root:Running "sub" with arguments (3, 3)
4+
INFO:root:Running "add" with arguments (3, 3)
5+
INFO:root:Running "add" with arguments (4, 5)
6+
INFO:root:Running "sub" with arguments (3, 3)

Decorators/decorator_function.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# what is decorator?
2+
# A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
3+
# Decorators are very powerful and useful tool in Python since it allows programmers to modify the behavior of function or class. Decorators allow us to wrap another function in order to extend the behavior of the wrapped function, without permanently modifying it.
4+
# In Decorators, functions are taken as the argument into another function and then called inside the wrapper function.
5+
6+
7+
def my_decorator(original_func):
8+
def wrap_func():
9+
print("********")
10+
original_func()
11+
print("********")
12+
13+
return wrap_func
14+
15+
16+
def my_decorator_2(original_func):
17+
18+
def wrap_func(*args, **kwargs):
19+
print("********")
20+
original_func(*args, **kwargs)
21+
print("********")
22+
23+
return wrap_func
24+
25+
26+
def hello():
27+
print("hello")
28+
29+
30+
# @my_decorator is just a syntactic sugar, shorthand for hello = my_decorator(hello)
31+
decorated = my_decorator(hello)
32+
decorated()
33+
34+
35+
@my_decorator
36+
def hi():
37+
print("hi")
38+
39+
40+
hi()
41+
42+
43+
@my_decorator_2
44+
# @my_decorator takes 0 positional arguments but 2 were given
45+
def show_info(name, age):
46+
print(f"Name: {name}, Age: {age}")
47+
48+
49+
show_info("Jerry", 30)

Decorators/decorator_logger.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from pathlib import Path
2+
import time
3+
import logging
4+
from functools import wraps
5+
6+
7+
def my_logger(orig_func):
8+
9+
script_location = Path(__file__).absolute().parent
10+
11+
file_location = script_location / f"{orig_func.__name__}.log"
12+
13+
logging.basicConfig(filename=file_location, level=logging.INFO)
14+
15+
@wraps(orig_func)
16+
def wrapper(*args, **kwargs):
17+
logging.info("Ran with args: {}, and kwargs: {}".format(args, kwargs))
18+
return orig_func(*args, **kwargs)
19+
20+
return wrapper
21+
22+
23+
def my_timer(orig_func):
24+
25+
@wraps(orig_func)
26+
def wrapper(*args, **kwargs):
27+
t1 = time.time()
28+
result = orig_func(*args, **kwargs)
29+
t2 = time.time() - t1
30+
print(f"{orig_func.__name__} ran in: {t2} sec")
31+
return result
32+
33+
return wrapper
34+
35+
36+
@my_logger
37+
@my_timer
38+
def display_info(name, age):
39+
time.sleep(1)
40+
print(f"display_info ran with arguments ({name}, {age})")
41+
42+
43+
display_info("Tom", 22)
44+
display_info("Jerry", 23)
45+
46+
47+
# this is equivalent to the following:
48+
# @my_logger
49+
# @my_timer
50+
# display_info = my_logger(my_timer(display_info))
51+
52+
# display_info = my_timer(display_info)
53+
# print(display_info.__name__)
54+
# # this will print wrapper

Decorators/decorator_method.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# what is decorator?
2+
# A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
3+
# Decorators are very powerful and useful tool in Python since it allows programmers to modify the behavior of function or class. Decorators allow us to wrap another function in order to extend the behavior of the wrapped function, without permanently modifying it.
4+
# In Decorators, functions are taken as the argument into another function and then called inside the wrapper function.
5+
6+
# methods and functions are really the same. The only difference is that methods expect that their first argument is a reference to the current object (self).
7+
# https://stackoverflow.com/questions/739654/how-do-i-make-function-decorators-and-chain-them-together
8+
9+
10+
def method_friendly_decorator(method_to_decorate):
11+
def wrapper(self, lie):
12+
lie = lie - 3 # very friendly, decrease age even more :-)
13+
return method_to_decorate(self, lie)
14+
15+
return wrapper
16+
17+
18+
class Lucy(object):
19+
20+
def __init__(self):
21+
self.age = 32
22+
23+
@method_friendly_decorator
24+
def sayYourAge(self, lie):
25+
print("I am {0}, what did you think?".format(self.age + lie))
26+
27+
28+
l = Lucy()
29+
l.sayYourAge(-3)
30+
31+
32+
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
33+
# The wrapper accepts any arguments
34+
def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
35+
print("Do I have args?:")
36+
print(args)
37+
print(kwargs)
38+
# Then you unpack the arguments, here *args, **kwargs
39+
# If you are not familiar with unpacking, check:
40+
# http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
41+
function_to_decorate(*args, **kwargs)
42+
43+
return a_wrapper_accepting_arbitrary_arguments
44+
45+
46+
@a_decorator_passing_arbitrary_arguments
47+
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
48+
print("Do {0}, {1} and {2} like platypus? {3}".format(a, b, c, platypus))
49+
50+
51+
function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
52+
# outputs
53+
# Do I have args ? :
54+
# ('Bill', 'Linus', 'Steve')
55+
# {'platypus': 'Indeed!'}
56+
# Do Bill, Linus and Steve like platypus? Indeed!
57+
58+
59+
class Mary(object):
60+
61+
def __init__(self):
62+
self.age = 31
63+
64+
@a_decorator_passing_arbitrary_arguments
65+
def sayYourAge(self, lie=-3): # You can now add a default value
66+
print("I am {0}, what did you think?".format(self.age + lie))
67+
68+
69+
m = Mary()
70+
m.sayYourAge()

Decorators/display_info.log

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
INFO:root:Ran with args: ('Tom', 22), and kwargs: {}
3+
INFO:root:Ran with args: ('Jerry', 23), and kwargs: {}

Decorators/wrapper.log

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
INFO:root:Ran with args: ('Tom', 22), and kwargs: {}
2+
INFO:root:Ran with args: ('Jerry', 23), and kwargs: {}

FC_Functions/first_class_function.py

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# what is first class function?
2+
# 1. A function is an instance of the Object type.
3+
# 2. You can store the function in a variable.
4+
# 3. You can pass the function as a parameter to another function.
5+
# 4. You can return the function from a function.
6+
# 5. You can store them in data structures such as hash tables, lists, …
7+
8+
9+
# Example 1: Functions are objects
10+
def square(x):
11+
return x * x
12+
13+
14+
def cube(x):
15+
return x * x * x
16+
17+
18+
fn = square
19+
20+
print(fn(6))
21+
22+
f = square(5)
23+
print(square)
24+
print(f)
25+
26+
27+
def my_map(func, arg_list):
28+
result = []
29+
for i in arg_list:
30+
result.append(func(i))
31+
return result
32+
33+
34+
r = my_map(square, [1, 2, 3, 4, 5])
35+
print(r)
36+
37+
38+
# Example 2: Functions can be passed as arguments to other functions
39+
# what's the point, why is this useful?
40+
def logger(msg):
41+
# This is a closure function
42+
def log_message():
43+
print("Log:", msg)
44+
45+
return log_message
46+
47+
48+
# log_hi is a function
49+
log_hi = logger("Hi!")
50+
log_hi()
51+
52+
53+
# this is why it's useful
54+
def html_tag(tag):
55+
def wrap_text(msg):
56+
print(f"<{tag}>{msg}</{tag}>")
57+
58+
return wrap_text
59+
60+
61+
# this example is a closure function and a first class function
62+
print_h1 = html_tag("h1")
63+
print_h1("Test Headline!")
64+
print_h1("Another Headline!")
65+
66+
print_p = html_tag("p")
67+
print_p("Test Paragraph!")
68+
print_p("Another Paragraph!")

Generators/people.py

+10-17
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,32 @@
1-
import mem_profile
21
import random
32
import time
43

54
names = ["John", "Corey", "Adam", "Steve", "Rick", "Thomas"]
65
majors = ["Math", "Engineering", "CompSci", "Arts", "Business"]
76

8-
# print 'Memory (Before): {}Mb'.format(mem_profile.memory_usage_psutil())
9-
10-
print(mem_profile.memory_usage_psutil())
11-
127

138
def people_list(num_people):
149
result = []
15-
for i in xrange(num_people):
10+
for i in range(num_people):
1611
person = {"id": i, "name": random.choice(names), "major": random.choice(majors)}
1712
result.append(person)
1813
return result
1914

2015

2116
def people_generator(num_people):
22-
for i in xrange(num_people):
17+
for i in range(num_people):
2318
person = {"id": i, "name": random.choice(names), "major": random.choice(majors)}
2419
yield person
2520

2621

27-
# t1 = time.clock()
28-
# people = people_list(1000000)
29-
# t2 = time.clock()
30-
31-
t1 = time.clock()
32-
people = people_generator(1000000)
33-
t2 = time.clock()
22+
t1 = time.time()
23+
people = people_list(10_000_000)
24+
t2 = time.time()
3425

35-
# print 'Memory (After) : {}Mb'.format(mem_profile.memory_usage_psutil())
26+
print("list ook {} Seconds".format(t2 - t1))
3627

37-
print(mem_profile.memory_usage_psutil())
28+
t1 = time.time()
29+
people = people_generator(10_000_000)
30+
t2 = time.time()
3831

39-
# print 'Took {} Seconds'.format(t2-t1)
32+
print("generator took {} Seconds".format(t2 - t1))

Generators/square_nums_1.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def square_numbers_generator(nums):
2727
for num in my_nums: # it start from 4, not 1
2828
print(num)
2929

30-
for num in my_nums:
30+
for num in my_nums: # it won't print anything, because it's already exhausted
3131
print(num)
3232

3333
print("*" * 20)

0 commit comments

Comments
 (0)