Skip to content
This repository was archived by the owner on Apr 3, 2026. It is now read-only.

Commit 67cec26

Browse files
authored
feat: Exercise #4. Functions and Strings
BREAKING CHANGE: Exercise complete
2 parents f6f3364 + 5aba320 commit 67cec26

7 files changed

Lines changed: 326 additions & 12 deletions

File tree

File renamed without changes.

2-loops/exercise.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ def print_names_length(names):
1111
add__line()
1212

1313
print_names_length(names_list)
14-
# 2) Add an if check inside the loop to only output names longer than 5 characters.
1514

15+
# 2) Add an if check inside the loop to only output names longer than 5 characters.
1616
def print_long_names_length(names):
1717
add__line()
1818
for name in names:
@@ -21,8 +21,8 @@ def print_long_names_length(names):
2121
add__line()
2222

2323
print_long_names_length(names_list)
24-
# 3) Add another if check to see whether a name includes a “n” or “N” character.
2524

25+
# 3) Add another if check to see whether a name includes a “n” or “N” character.
2626
def print_names_with_n(names):
2727
add__line()
2828
for name in names:
@@ -31,8 +31,8 @@ def print_names_with_n(names):
3131
add__line()
3232

3333
print_names_with_n(names_list)
34-
# 4) Use a while loop to empty the list of names (via pop() )
3534

35+
# 4) Use a while loop to empty the list of names (via pop() )
3636
def empty_names_list(names):
3737
add__line()
3838
while len(names) > 0:

3-data-structures/exercise.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,45 @@
1+
def add__line():
2+
print('--------------------')
3+
14
# 1) Create a list of “person” dictionaries with a name, age and list of hobbies for each person. Fill in any data you want.
25
persons_list = [
36
{ 'name': 'Nicolas', 'age': 22, 'hobbies': ['reading', 'gaming'] },
47
{ 'name': 'Florencia', 'age': 19, 'hobbies': ['painting', 'cycling'] },
58
{ 'name': 'Martin', 'age': 25, 'hobbies': ['hiking', 'swimming'] }
69
]
7-
print(persons_list)
10+
11+
def print_persons_list():
12+
add__line()
13+
print(persons_list)
14+
add__line()
15+
16+
print_persons_list()
817

918
# 2) Use a list comprehension to convert this list of persons into a list of names (of the persons).
10-
person_comprehension = ', '.join([person['name'] for person in persons_list])
11-
print('The persons names are:', person_comprehension)
19+
def print_person_names():
20+
add__line()
21+
person_comprehension = ', '.join([person['name'] for person in persons_list])
22+
print('The persons names are:', person_comprehension)
23+
add__line()
24+
25+
print_person_names()
1226

1327
# 3) Use a list comprehension to check whether all persons are older than 20.
14-
all_older_than_20 = all([person['age'] > 20 for person in persons_list])
15-
print('All persons older than 20:', all_older_than_20)
28+
def list_all_older_than_20():
29+
add__line()
30+
all_older_than_20 = ', '.join([person['name'] for person in persons_list if person['age'] > 20])
31+
print('All persons older than 20:', all_older_than_20)
32+
add__line()
33+
34+
list_all_older_than_20()
1635

1736
# 4) Copy the person list such that you can safely edit the name of the first person (without changing the original list).
18-
persons_copy = [person.copy() for person in persons_list]
19-
persons_copy[0]['name'] = 'Matias'
20-
print('Original list:', persons_list)
21-
print('Modified copy:', persons_copy)
37+
def copy_and_edit_persons_list():
38+
add__line()
39+
persons_copy = [person.copy() for person in persons_list]
40+
persons_copy[0]['name'] = 'Matias'
41+
print('Original list:', persons_list)
42+
print('Modified copy:', persons_copy)
43+
add__line()
44+
45+
copy_and_edit_persons_list()
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# A built in module that provides functions from python standard library
2+
import functools
3+
4+
MINING_REWARD = 10
5+
genesis_block = {
6+
'previous_hash': '',
7+
'index': 0,
8+
'transactions': []
9+
}
10+
my_blockchain = [genesis_block]
11+
open_transactions = []
12+
waiting_for_input = True
13+
owner = 'Nicolas'
14+
participants = set([owner])
15+
16+
def add__line():
17+
print('------------------------------')
18+
19+
def generate_options_menu():
20+
add__line()
21+
print('Please choose an option:')
22+
print('1: Add a new transaction')
23+
print('2: Mine a new block')
24+
print('3: Output all blockchain blocks')
25+
print('4: Output all participants')
26+
print('h: Manipulate blockchain')
27+
print('q: Quit')
28+
add__line()
29+
30+
def get_user_input():
31+
user_input = input('Please enter your selection: ')
32+
add__line()
33+
return user_input
34+
35+
def hash_block(block):
36+
""" Hash a block using its strucutre as base """
37+
return str(block[key] for key in block)
38+
39+
def mine_block():
40+
last_block = my_blockchain[-1]
41+
hashed_block = hash_block(last_block)
42+
43+
reward_transaction = {
44+
'sender': 'MINING',
45+
'recipient': owner,
46+
'amount': MINING_REWARD
47+
}
48+
49+
block = {
50+
'previous_hash': hashed_block,
51+
'index': len(my_blockchain),
52+
'transactions': [open_transactions, reward_transaction]
53+
}
54+
55+
my_blockchain.append(block)
56+
print('Block added!')
57+
add__line()
58+
return True
59+
60+
def get_transaction_value():
61+
""" Returns the input of the user (a new transaction amount and its recipient) as a tuple """
62+
tx_recipient_input = input('Please enter the recipient of the transaction: ')
63+
tx_amount_input = float(input('Please enter your transaction input: '))
64+
add__line()
65+
66+
# When you return a tuple, it does not need to be enclosed in parentheses (is optional i guess)
67+
return tx_recipient_input, tx_amount_input
68+
69+
def take_last_blockchain_value():
70+
if len(my_blockchain) < 1:
71+
return None
72+
73+
return my_blockchain[-1]
74+
75+
def verify_transaction(transaction):
76+
sender_balance = get_balance(transaction['sender'])
77+
78+
if sender_balance >= transaction['amount']:
79+
return True
80+
return False
81+
82+
def add_transaction(sender, recipient, amount=1):
83+
"""
84+
Add a new transaction to the list of open transactions (which will be added to the next mined block)
85+
86+
Arguments:
87+
:sender: The sender of the coins.
88+
:recipient: The recipient of the coins.
89+
:amount: The amount of the transaction.
90+
"""
91+
92+
new_transaction = {
93+
'sender': sender,
94+
'recipient': recipient,
95+
'amount': amount
96+
}
97+
98+
if verify_transaction(new_transaction):
99+
open_transactions.append(new_transaction)
100+
participants.add(sender)
101+
participants.add(recipient)
102+
else:
103+
print('Transaction failed! Not enough balance!')
104+
add__line()
105+
106+
def return_all_blocks():
107+
print('---Outputting all blocks---')
108+
109+
for block in my_blockchain:
110+
print(f'Outputting block: {block}')
111+
add__line()
112+
113+
def get_balance(participant):
114+
# final_balance = 0
115+
sent_transactions = [[tx['amount'] for tx in block['transactions'] if tx['sender'] == participant] for block in my_blockchain]
116+
recieved_transactions = [[tx['amount'] for tx in block['transactions'] if tx['recipient'] == participant] for block in my_blockchain]
117+
open_sent_transactions = [tx['amount'] for tx in open_transactions if tx['sender'] == participant]
118+
119+
sent_transactions.append(open_sent_transactions)
120+
# This function is a reducer that will sum up all the values in a list of lists
121+
# The lambda function is an anonymous function that takes two arguments, the first one is the accumulator (the sum of the previous values) and the second one is the current value
122+
sent_amounts = functools.reduce(lambda tx_sum, tx_amt: tx_sum + sum(tx_amt) if len(tx_amt) > 0 else tx_amt + 0, sent_transactions, 0)
123+
# for sent_amount in sent_transactions:
124+
# if len(sent_amount) > 0:
125+
# final_balance -= sent_amount
126+
127+
recieved_amounts = functools.reduce(lambda tx_sum, tx_amt: tx_sum + sum(tx_amt) if len(tx_amt) > 0 else tx_amt + 0, recieved_transactions, 0)
128+
# for recieved_amount in recieved_transactions:
129+
# if len(recieved_amount) > 0:
130+
# final_balance += recieved_amount
131+
132+
return recieved_amounts - sent_amounts
133+
134+
def verify_chain():
135+
""" The function helps to verify the integrity of the blockchain by checking if each block's previous hash matches the hash of the previous block. """
136+
for (index, block) in enumerate(my_blockchain):
137+
if index == 0:
138+
continue
139+
if block['previous_hash'] != hash_block(my_blockchain[index - 1]):
140+
return False
141+
return True
142+
143+
def verify_transactions():
144+
""" The function verifies all open transactions to ensure they are valid. """
145+
return all([tx for tx in open_transactions if not verify_transaction(tx)])
146+
147+
while waiting_for_input:
148+
generate_options_menu()
149+
150+
user_choice = get_user_input()
151+
152+
if user_choice == '1':
153+
tx_input_data = get_transaction_value()
154+
recipient, amount = tx_input_data
155+
add_transaction(owner, recipient, amount)
156+
elif user_choice == '2':
157+
if mine_block():
158+
open_transactions = []
159+
elif user_choice == '3':
160+
return_all_blocks()
161+
elif user_choice == '4':
162+
print(participants)
163+
elif user_choice == 'q':
164+
waiting_for_input = False
165+
elif user_choice == 'h':
166+
if len(my_blockchain) >= 1:
167+
my_blockchain[0] = [2.0]
168+
else:
169+
print('Invalid input, please choose a valid option')
170+
if not verify_chain():
171+
print('Invalid blockchain!')
172+
waiting_for_input = False
173+
print(f"Balance of {owner}: {get_balance(owner)}")
174+
else:
175+
print('User left!')
176+
177+
add__line()
178+
print('Done!')
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
def add__line():
2+
print('--------------------')
3+
4+
# 1) Write a normal function that accepts another function as an argument. Output the result of that other function in your “normal” function.
5+
def normal_function(callback):
6+
return callback()
7+
8+
def callback_function():
9+
return "Hello from callback function!"
10+
11+
print(normal_function(callback_function))
12+
add__line()
13+
14+
# 2) Call your “normal” function by passing a lambda function – which performs any operation of your choice – as an argument.
15+
print(normal_function(lambda: "Hello from lambda function!"))
16+
add__line()
17+
18+
# 3) Tweak your normal function by allowing an infinite amount of arguments on which your lambda function will be executed.
19+
number_list = [1, 2, 3, 4, 5]
20+
def normal_function_with_infinite_args(callback, *args):
21+
return callback(*args)
22+
23+
def callback_infinite_args(*args):
24+
return args
25+
26+
print(normal_function_with_infinite_args(callback_infinite_args, *number_list))
27+
add__line()
28+
29+
# 4) Format the output of your “normal” function such that numbers look nice and are centered in a 20 character column.
30+
def normal_function_centered(callback, number):
31+
return callback(number)
32+
33+
def callback_function_centered(number):
34+
return f"{number:^20}"
35+
36+
print(normal_function_centered(callback_function_centered, 5))
37+
print(normal_function_centered(callback_function_centered, 6))
38+
print(normal_function_centered(callback_function_centered, 7))
39+
print(normal_function_centered(callback_function_centered, 8))
40+
print(normal_function_centered(callback_function_centered, 9))
41+
print(normal_function_centered(callback_function_centered, 10))
42+
add__line()

4-functions-and-strings/theory.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# ---STRING FORMATTING (BEFORE PYTHON 3.6)---
2+
name = "Nicolas"
3+
age = 33
4+
5+
# This version of a concatenated string is the base idea to start working, but it becoames hard to read
6+
string_name="My name is " + name + " and I am " + str(age) + " years old."
7+
print(string_name) # Returns My name is Nicolas and I am 33 years old.
8+
9+
# A better way to format strings is using the format method, which inserts the variables in the string and has no arguments limit
10+
string_name = "My name is {} and I am {} years old.".format(name, age)
11+
print(string_name) # Returns My name is Nicolas and I am 33 years old.
12+
13+
# An alternative way to inject the variables is by using positional arguments
14+
# On this case, it will put first the age and then the name (because arguments are 0 indexed)
15+
string_name = "My name is {1} and I am {0} years old.".format(name, age)
16+
print(string_name) # Returns My name is 33 and I am Nicolas years old.
17+
18+
# Another alternative way to inject the variables is by using named arguments
19+
# By assigning a name to each argument, we can use them in any order
20+
string_name = "My name is {name} and I am {years} years old.".format(name=name, years=age)
21+
print(string_name) # Returns My name is Nicolas and I am 33 years old.
22+
23+
founds = 150.978
24+
25+
founds_statement = "Founds: {}.".format(founds)
26+
print(founds_statement) # Returns Founds: 150.97.
27+
28+
# We can also format numbers, in this case, we can ask to show a float number. By default, it shows 6 decimals
29+
founds_statement = "Founds: {:f}.".format(founds)
30+
print(founds_statement) # Returns Founds: 150.978000.
31+
32+
# If you want to reduce the number of decimals, you can specify it after the point
33+
founds_statement = "Founds: {:.2f}.".format(founds)
34+
print(founds_statement) # Returns Founds: 150.98.
35+
36+
# ---STRING INTERPOLATION (AFTER PYTHON 3.6)---
37+
38+
# After Python 3.6, we can use f-strings, which are more readable and easier to use
39+
# In this case, you can directly inject the variables in the string using {}
40+
string_name = f"My name is {name} and I am {age} years old."
41+
print(string_name) # Returns My name is Nicolas and I am 33 years old.
42+
43+
# And can be extended to formatting numbers as well
44+
string_name = f"My name is {name} and I am {age:.1f} years old."
45+
print(string_name) # Returns My name is Nicolas and I am 33.0 years old.
46+
47+
# ---LIST MAPPING AND LAMBDA FUNCTIONS---
48+
simple_list = [1, 2, 3, 4, 5]
49+
50+
# The map function applies a function to all items in an iterable (like a list) and returns a map object (which is an iterator)
51+
# First arguments is the same as an callback function in javascript, the second argument is the iterable to apply the function to
52+
def multiply_by_two(item):
53+
return item * 2
54+
55+
mapped_list = map(multiply_by_two, simple_list)
56+
print(mapped_list) # Returns <map object at 0x...>
57+
58+
# You can convert the map object to a list or a tuple using the list() or tuple
59+
print(list(mapped_list)) # Returns [2, 4, 6, 8, 10]
60+
# TO HAVE IN MIND: It is recommended to use list comprehensions instead of map function for better readability and performance
61+
62+
# An alternative way to use the map function is by using a lambda function as the first argument
63+
# A lambda function is an anonymous function that can take any number of arguments, but can only have one expression
64+
mapped_list = map(lambda item: item * 2, simple_list)
65+
print(list(mapped_list)) # Returns [2, 4, 6, 8, 10]

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ python3 exercise.py # Or any .py file
6060
- How to check value validity (if is true) with methods such as `all` or `any`.
6161
- How to use `set`.
6262
- Use built-in converter methods like `enumerate`.
63+
- Functions and Strings (`4-functions-and-strings` folder)
64+
- How to use `format` method for strings.
65+
- How to use string interpolation with the `f` method.
66+
- How to map lists with map method and `lambda functions`.
67+
- How to summarize data with `reduce` method.
6368

6469
## Other practice repos
6570
| Node | React | Angular | GraphQL | HTML & CSS | Styling | Typescript | NextJs | Docker |

0 commit comments

Comments
 (0)