|
| 1 | +#5 { Retos para Progarmadores } VALOR Y REFERENCIA |
| 2 | +log = print # Shortening print function to log |
| 3 | + |
| 4 | +# Variables by Value |
| 5 | + |
| 6 | +# In Python, immutable data types (such as int, float, str, and tuple) are assigned by value. |
| 7 | +# This means that when you assign a primitive value to a variable, a copy of that value is made. |
| 8 | + |
| 9 | +a = 44 # a is an integer (immutable type) |
| 10 | +b = a # b is assigned the value of a |
| 11 | + |
| 12 | +log(a) # 44 |
| 13 | +log(b) # 44 |
| 14 | + |
| 15 | +b += 20 # Changing b does not affect a |
| 16 | +log(a) # 44 |
| 17 | +log(b) # 64 |
| 18 | + |
| 19 | +# Variables by Reference |
| 20 | + |
| 21 | +# In Python, mutable data types (such as list, dict, and set) are assigned by reference. |
| 22 | +# This means that when you assign a mutable object to a variable, you are actually assigning a reference to that object, not a copy of it. |
| 23 | + |
| 24 | +obj1 = {"name": "Julia"} # obj1 is a dictionary (mutable type) |
| 25 | +obj2 = obj1 # obj2 is assigned the reference of obj1 |
| 26 | + |
| 27 | +log(obj1["name"]) # Julia |
| 28 | +log(obj2["name"]) # Julia |
| 29 | + |
| 30 | +obj2["name"] = "Karla" # Changing obj2 affects obj1 because they reference the same object |
| 31 | +log(obj1["name"]) # Karla |
| 32 | +log(obj2["name"]) # Karla |
| 33 | + |
| 34 | +obj2["age"] = 32 |
| 35 | +log(obj2["age"]) # 32 |
| 36 | +log(obj1["age"]) # 32 |
| 37 | + |
| 38 | +# Lists (Mutable Type) in Python are also reference types. |
| 39 | + |
| 40 | +arr1 = [7, 4, 90] # arr1 is a list (mutable type) |
| 41 | +arr2 = arr1 # arr2 is assigned the reference of arr1 |
| 42 | + |
| 43 | +log(arr1) # [7, 4, 90] |
| 44 | +log(arr2) # [7, 4, 90] |
| 45 | + |
| 46 | +arr2.append(42) # Modifying arr2 affects arr1 |
| 47 | +log(arr1) # [7, 4, 90, 42] |
| 48 | +log(arr2) # [7, 4, 90, 42] |
| 49 | + |
| 50 | +# By Value: Immutable types (e.g., int, str) are copied when assigned to a new variable. |
| 51 | +# By Reference: Mutable types (e.g., dict, list) are assigned by reference, meaning changes to one variable affect the other if they reference the same object. |
| 52 | + |
| 53 | +# In Python, functions are also first-class objects, which means they can be assigned to variables, passed as arguments, and returned from other functions. |
| 54 | +# When you assign a function to a variable, you are assigning a reference to that function, not a copy of it. |
| 55 | + |
| 56 | +# Functions Assigned by Reference |
| 57 | + |
| 58 | +# When you assign a function to a new variable, both variables point to the same function in memory. |
| 59 | + |
| 60 | +def greet(): |
| 61 | + log("Hello everybody! It's time for coding...") |
| 62 | + |
| 63 | +greet_copy = greet # greet_copy is assigned the reference of greet |
| 64 | + |
| 65 | +greet() # Hello everybody! It's time for coding... |
| 66 | +greet_copy() # Hello everybody! It's time for coding... |
| 67 | + |
| 68 | +# Changing the function reference |
| 69 | +greet_copy = lambda: log("This world is turning into a weird scenario with all those AIs, M2M (technology), and there are cyber attacks that can explode beepers, cell phones, and big media that shows only a tiny fragment of reality.") |
| 70 | + |
| 71 | +greet_copy() # This world is turning into a weird scenario with all those AIs, M2M (technology), and there are cyber attacks that can explode beepers, cell phones, and big media that shows only a tiny fragment of reality. |
| 72 | + |
| 73 | +# Passing Functions as Arguments |
| 74 | + |
| 75 | +# You can pass functions as arguments to other functions. This demonstrates that functions are treated as reference types. |
| 76 | + |
| 77 | +def call_funct(fn): |
| 78 | + fn() |
| 79 | + |
| 80 | +def say_bye_lady(): |
| 81 | + log("See you later, lady!") |
| 82 | + |
| 83 | +call_funct(say_bye_lady) # See you later, lady! |
| 84 | + |
| 85 | +# Returning Functions from Functions |
| 86 | + |
| 87 | +# You can also return functions from other functions, which shows that functions can be created dynamically and assigned by reference. |
| 88 | + |
| 89 | +# Function that returns another function (closure) |
| 90 | +def say_something_to(greeting): |
| 91 | + return lambda name: log(f"{greeting}, {name}!") |
| 92 | + |
| 93 | +say = say_something_to("Welcome to coding lab") |
| 94 | +say2 = say_something_to("It's a long way home") |
| 95 | + |
| 96 | +say("Nicky") # Welcome to coding lab, Nicky |
| 97 | +say2("Jhon") # It's a long way home, Jhon |
| 98 | + |
| 99 | +# Modifying Functions |
| 100 | + |
| 101 | +# Since functions are reference types, if you modify a function that is referenced by multiple variables, it will affect all references. |
| 102 | + |
| 103 | +def hi_miss(): |
| 104 | + log("Hello, miss. Have a beautiful day") |
| 105 | + |
| 106 | +func_ref = hi_miss # func_ref points to hi_miss |
| 107 | + |
| 108 | +func_ref() # Hello, miss. Have a beautiful day |
| 109 | + |
| 110 | +# Modifying the original function |
| 111 | +hi_miss = lambda: log("Sometimes I feel jealous of the wind that caresses your hair, sometimes the rain....") |
| 112 | + |
| 113 | +func_ref() # Hello, miss. Have a beautiful day (still points to the original function) |
| 114 | + |
| 115 | +# But if we do, for example... |
| 116 | +func_ref = lambda: hi_miss() |
| 117 | + |
| 118 | +func_ref() # Sometimes I feel jealous of the wind that caresses your hair, sometimes the rain.... (now every change in hi_miss will be reflected) |
| 119 | + |
| 120 | +# In Python, when you pass arguments to a function, the behavior depends on whether the argument is a primitive type (passed by value) or a reference type (passed by reference). |
| 121 | + |
| 122 | +# Function that takes a primitive type (passed by value) |
| 123 | +def do_something(value): |
| 124 | + value = 20 # This change does not affect the original variable |
| 125 | + log("Value Inside do_something:", value) # Value Inside do_something: 20 |
| 126 | + |
| 127 | +# Function that takes an object (passed by reference) |
| 128 | +def set_user_name(obj, name): |
| 129 | + obj['name'] = name # This change affects the original object |
| 130 | + log("Value Inside set_user_name:", obj['name']) |
| 131 | + |
| 132 | +# Testing the functions |
| 133 | +num = 'something' |
| 134 | +log("Value Before do_something:", num) # Value Before do_something: something |
| 135 | +do_something(num) |
| 136 | +log("After do_something:", num) # After do_something: something (remains unchanged) |
| 137 | + |
| 138 | +user = {'name': "Luna", 'age': 32} |
| 139 | +log("Before set_user_name:", user['name']) # Before set_user_name: Luna |
| 140 | +set_user_name(user, 'Kia') # Value Inside set_user_name: Kia |
| 141 | +log("After set_user_name:", user['name']) # After set_user_name: Kia (changed) |
| 142 | + |
| 143 | +# Note: the same applies to lists that are reference values. |
| 144 | + |
| 145 | +# Simulating a window load event (not applicable in standard Python, but for demonstration) |
| 146 | +def on_load(): |
| 147 | + body_style = { |
| 148 | + 'background': '#000', |
| 149 | + 'text-align': 'center' |
| 150 | + } |
| 151 | + |
| 152 | + title = { |
| 153 | + 'text': 'Retosparaprogramadores #5.', |
| 154 | + 'font-size': '3.5vmax', |
| 155 | + 'color': '#fff', |
| 156 | + 'line-height': '100vh' |
| 157 | + } |
| 158 | + |
| 159 | + log("Title:", title['text']) |
| 160 | + |
| 161 | + # Simulating a delay (not applicable in standard Python, but for demonstration) |
| 162 | + import time |
| 163 | + time.sleep(2) |
| 164 | + log('Retosparaprogramadores #5') # this will be logged at the end |
| 165 | + |
| 166 | +# Call the simulated load function |
| 167 | +on_load() |
| 168 | + |
| 169 | +# EXTRA DIFFICULTY (optional): |
| 170 | + |
| 171 | +user1 = { |
| 172 | + 'name': 'Kasperle', |
| 173 | + 'age': 12 |
| 174 | +} |
| 175 | + |
| 176 | +user2 = { |
| 177 | + 'name': 'Snoopy', |
| 178 | + 'age': None |
| 179 | +} |
| 180 | + |
| 181 | +log('user1:', user1) # user1: {'name': 'Kasperle', 'age': 12} |
| 182 | +log('user2:', user2) # user2: {'name': 'Snoopy', 'age': None} |
| 183 | + |
| 184 | +def change_user(obj1, obj2): |
| 185 | + provisional = obj1.copy() # Create a shallow copy |
| 186 | + obj1.update(obj2) # Update obj1 with obj2 |
| 187 | + obj2.update(provisional) # Update obj2 with the original obj1 |
| 188 | + |
| 189 | +change_user(user1, user2) |
| 190 | + |
| 191 | +log('user1:', user1) # user1: {'name': 'Snoopy', 'age': None} |
| 192 | +log('user2:', user2) # user2: {'name': 'Kasperle', 'age': 12} |
| 193 | + |
| 194 | +def swap_values(a, b): |
| 195 | + return b, a # Return the swapped values as a tuple |
| 196 | + |
| 197 | +# You can call the function and use tuple unpacking to assign the returned values to new variables: |
| 198 | + |
| 199 | +x = 85 |
| 200 | +y = 17 |
| 201 | + |
| 202 | +log("Before swap:", x, y) # Before swap: 85 17 |
| 203 | + |
| 204 | +# Call the function and unpack the returned tuple |
| 205 | +d, e = swap_values(x, y) |
| 206 | + |
| 207 | +log("After swap:", x, y) # After swap: 85 17 |
| 208 | +log('New variables with values swapped:', d, e) # New variables with values swapped: 17 85 |
| 209 | + |
| 210 | +# Note: tuple unpacking allows us to achieve the effect of reference values with primitive values. |
| 211 | +# By using tuple unpacking, we can achieve both effects: conserving the original values or swapping the values. |
| 212 | +log("Before swap:", x, y) # Before swap: 85 17 |
| 213 | +x, y = swap_values(x, y) |
| 214 | + |
| 215 | +log("After swap:", x, y) # After swap: 17 85 |
| 216 | + |
| 217 | +# Note: using tuple unpacking allows us to achieve both effects: conserving the original values or swapping the values. |
| 218 | + |
| 219 | +# Note2: Keep in mind that when you use copying methods like copy(), it creates a shallow copy, |
| 220 | +# which means that if there are nested objects inside, the nested ones won't be copied. |
| 221 | + |
| 222 | + |
0 commit comments