Imaginemos Esta Función (Que, Ustedes Dirán, ES Bastante Tonta) Que Agroga Un Elemento A UNA Lista Eniacy, Ambos ArgageS de La Función. Перо Адемас, Queremos que POR DEFECTO , La Lista Enibied Esté Vacía, Así Que Pasarle Sólo Un Элемент
A La Función Implique Devolver Una Lista Con únicape Elemento:
def agregar_a_lista(elemento, inicial=[]): inicial.append(elemento) return inicial
Así que vamos usar nuestra humilde, Перо корректирует función!
>>> una_lista_larga = agregar_a_lista(5, [2, 3]) >>> print(una_lista_larga) [2, 3, 5] >>> una_lista_corta = agregar_a_lista(5) >>> print(una_lista_corta) [5] >>> otra_lista_corta = agregar_a_lista(7)
Todo Parece Andar Bien, y otra_lista_corta
Debería Dar [7]
Отказ Sin Embargo …
>>> print(otra_lista_corta) [5, 7]
Qué!? ¿ Qué Pasó? ¿ SE “Acuerda” de que Antes Le Había Agregado ООН 5?
ESTA ES La Primera Pregunta que nos va ayudar entender lo que está suciciendo. Para Eso, Vamos Usar: 1. Эль Методо __defaults__
que nos правда ver los valores por defecto de una función; Y 2. Эль встроенный ID
que nos devualve una резингения única a cada objeto (es decir, dos objetos tienen el mismo ID
Si Y Sólo Si Si Son El Mismo Objeto). Ahora Comenzamos de Cero Un
def agregar_a_lista(elemento, inicial=[]): inicial.append(elemento) return inicial valores_por_defecto = agregar_a_lista.__defaults__ identidades = [id(valor) for valor in agregar_a_lista.__defaults__] print(f"Valores por defecto: {valores_por_defecto}") print(f"Identidades: {identidades}") print("Ejecutando una_lista = agregar_a_lista(5)") una_lista = agregar_a_lista(5) print(f"Resultado: {una_lista}") valores_por_defecto = agregar_a_lista.__defaults__ identidades = [id(valor) for valor in agregar_a_lista.__defaults__] print(f"Valores por defecto: {valores_por_defecto}") print(f"Identidades: {identidades}") print("Ejecutando otra_lista = agregar_a_lista(7)") otra_lista = agregar_a_lista(7) print(f"Resultado: {otra_lista}")
Cuando Ejecutamos ESE Script, Obtenemos:
Valores por defecto: ([],) Identidades: [140611099623168] Ejecutando una_lista = agregar_a_lista(5) Resultado: [5] Valores por defecto: ([5],) Identidades: [140611099623168] Ejecutando otra_lista = agregar_a_lista(7) Resultado: [5, 7]
Efectivamente, La Función SE acuerda de que legregamos un 5, Перо Порка El objeto es el mismo (fíjense que las addicidades concuerdan). La Primera Vez Que Ejecutamos La Función, Modificamos Su Valor; Обучению, La Segunda Vez Que Lo Usamos, LO Tomamos Con El Valor Modififado. La Primera Endución Sería: No Modifiquemos Los Valores Por Defecto Doctro de Una Función de Python O, Más General Y Más Seguro, NO USEMOS OBJETOS Turable Como Valores POR DEFECTO EN UNA FUNCION .
Буэно, Перо ¿Qué Está Pasando?
COMO PUEDEN VER, EL __defaults__
de la función. ya tiene un valor incluso antes de que la función se llame por primera vez. ESTO SE DEBE que los valores por defecto de la función se calculan cuando la función se define, no cuando se llama. Este Comportamiento SE LLAMA Ранний связывающий , Y SU CONTRAPARTE ( Поздние Обязательные ) Calcula Los Valores Por Defecto En Cada Ejecución.
PodeMos Poner Ezo A The Prueba Bastante Fácillemente: ¿Qué Pasa Si Entre llamada y lllamada volvemos a difrish la función tal como estaba?
def agregar_a_lista(elemento, inicial=[]): inicial.append(elemento) return inicial valores_por_defecto = agregar_a_lista.__defaults__ identidades = [id(valor) for valor in agregar_a_lista.__defaults__] print(f"Valores por defecto: {valores_por_defecto}") print(f"Identidades: {identidades}") print("Ejecutando una_lista = agregar_a_lista(5)") una_lista = agregar_a_lista(5) print(f"Resultado: {una_lista}") def agregar_a_lista(elemento, inicial=[]): inicial.append(elemento) return inicial valores_por_defecto = agregar_a_lista.__defaults__ identidades = [id(valor) for valor in agregar_a_lista.__defaults__] print(f"Valores por defecto: {valores_por_defecto}") print(f"Identidades: {identidades}") print("Ejecutando otra_lista = agregar_a_lista(7)") otra_lista = agregar_a_lista(7) print(f"Resultado: {otra_lista}")
Al Ejecutar Este Script Obtenemos El Butterado Esperado:
Valores por defecto: ([],) Identidades: [140438619595200] Ejecutando una_lista = agregar_a_lista(5) Resultado: [5] Valores por defecto: ([],) Identidades: [140438619595840] Ejecutando otra_lista = agregar_a_lista(7) Resultado: [7]
Нет Sólo Los Valores Por Defecto SE Vuelven Калькулярный, Sino Que Además Fíjense Que Tienen DOS отождествляет Distintas; Es Decir, сын Dos objetos Dantintos Отказ
ОК, Энтенддо. Перо ¿Cómo Lo Resualvo?
La Solución Cuanto No Qeremos Este Comportamiento (Que es es la mayoría de los casos) es utilizar lo que se llama un objeto Centinela , Para lo que hethermentree SE США Нет
:
def agregar_a_lista_bien(elemento, inicial=None): if inicial is None: inicial = [] inicial.append(elemento) return inicial
Fíjense la diferencia cuando usamos esta función, сравнение преданный спереди
una_lista = agregar_a_lista(5) otra_lista = agregar_a_lista(7) print(f"Identidades: {id(una_lista)}, {id(otra_lista)}") una_lista_bien = agregar_a_lista_bien(5) otra_lista_bien = agregar_a_lista_bien(7) print(f"Identidades: {id(una_lista_bien)}, {id(otra_lista_bien)}")
Identidades: 139893270457152, 139893270457152 Identidades: 139893271721280, 139893270011200
RU agregar_a_lista
Ambas Listas Son El Mismo Objeto (Sus Initedides Son Iguales), MientArs Que en agregar_a_lista_bien
SE CREAN DOS LISTAS DINTINTAS: [5]
y [7]
. La Diferencia фундаментальный ES QUE EL USO DEL VALOR CENTINELA Нет
nos permitió diforiar Исконированный
Денго de la función y, así, es искренний = []
подавать CADA VEZ QUE LLAMAMOS LA FUNCION y Нет Sólo Al Commitio Como Antes.
Algunas Aclaraciones Extra (Quizás un Poco Más Avanzadas)
Sobre Los Centinelas
Нет quiero Ahondar Mucho en Este Tema, Перо сена Veces que, en la función que usamos, эль аргумент por defecto podría ser Нет
Отказ EN ESE CASO, NUESTRA TÉCNICA DEL CENTINELA Interfiere Con Ese Valor, Privque Lo Meader Simplemente El RindaDor de “Acá va una lista vacía”. ¿La Solución? Usar Centinelas únicos. La Exprachación detallada Quedará Para Más Adelante, Перо La Idea Es Algo Así:
_CENTINELA = object() def agregar_a_lista_bien_unico(elemento, inicial=_CENTINELA): if inicial is _CENTINELA: inicial = [] inicial.append(elemento) return inicial
Собре Эль есть
Ла ключевое слово это
EN Python ES PARA CHEQUEAR INDIEDDADES, без валов. Es Decir, А это Б.
VA DAR Истинный
si y sólo si ID (A) (B)
; ES DECIR, SI Y SOLO SI A
y B
Сын Эль Миссо Объето. ¿Por Qué Compasmos против это
y Нет con ==
? Porterque en python EL SHARTADOR ( ==
) SE PUEDE DEASEIR PARA CADA CLASE Través del Método __eq__
; Обучению, Si Crowsos Una Clase Que Parece Una Lista, Перо Devualve Siempre Правда
RU SPARACIONES:
class MiLista(list): def __eq__(self, other): return True
подавать:
>>> lista_prueba = MiLista([1, 2, 3, 4]) >>> lista_prueba == None True >>> lista_prueba is None False
Así que nunca, Nunca , Nunca Сравнить валов Centinela Con ==.
. Lo que me lleva al tercer y último punto:
Сравнительные каравали
Python Tiene Conversions Dimícitas de Valores Booleanos ( True
/ Ложь
, Ба). POR EJEMPLO, LAS Listas Vacías, Нет
y los números iguales a 0 se evalúan como Ложь
Отказ Así que, como Нет
SE EVALúA A Ложь
Uno Podría Tentarse Escribir:
... if not inicial: inicial = []
en vez del chekeoo Ишенский это не
Отказ
ESTO, Además del Flancea de Fromar Con ==
SE Le Suma Que Quizás Sí Кермеос Pasar Una lista Vacía Y Modififarla (Con un Добавить
), Перо ESTE Código Nos Fuerza Crage Una Lista Nueva Исконированный
Дендо дель Область de la función.
Entudiar Cómo Funciona El Lenguaje Nos Puede Ayudar a que comportamientos que Antes Nos Parecían Raros SE Vuelvan Más Intuitivos. EN ESTE CASO EN в частности:
- Нет упретиков Сообщения COMO Valores POR DEFECTO EN PYTHON.
Нет
Casi Siempre Es un Buen Valor Centinela- CuAndo No Sirve Como Centinela, Hay que arrremargarse un poco; Перо Ла-Логика Е.С. Л.А. МИМСА.
- Los Centinelas Siempre SE Chquea Con
это
, нет кон==.
.
Оригинал: “https://dev.to/ninjaia/argumentos-opcionales-en-python-por-que-no-hay-que-usar-listas-3hok”