Una forma más elegante de declarar múltiples variables al mismo tiempo

140

Para declarar múltiples variables al "mismo tiempo" haría:

a, b = True, False

Pero si tuviera que declarar muchas más variables, se vuelve cada vez menos elegante:

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True ,True , True, True

¿Hay una manera mejor / elegante / conveniente de hacer esto?

Esto debe ser muy básico, pero si utilizo una lista o una tupla para almacenar las variables, ¿cómo debería enfocarme para ser útil ya que:

aList = [a,b]

No es válido, tendría que hacer:

a, b = True, True

¿O qué me estoy perdiendo?

Trufa
fuente
¿Usar una lista para almacenar esos valores? ¿Un diccionario? ¿Una tupla (con nombre)?
Jeff Mercado
@ Chris: Estaba llegando allí. :)
Jeff Mercado
@JeffM: tal vez, pero no sé cómo hacerlo, parece que deben definirse para pertenecer a una lista (por supuesto, puedo estar equivocado)
Trufa
3
@Trufa: Si va a declarar que hay muchas variables para almacenar valores, eso ya es una señal de que debería considerar otras alternativas de almacenamiento en mi humilde opinión.
Jeff Mercado
1
@ user470379 - Asumí que los nombres eran solo para el código de ejemplo, y que Trufa no está usando esos nombres en su código real.
Chris Lutz

Respuestas:

52

Como otros han sugerido, es poco probable que usar 10 variables locales diferentes con valores booleanos sea la mejor manera de escribir su rutina (especialmente si realmente tienen nombres de una letra :)

Dependiendo de lo que esté haciendo, puede tener sentido usar un diccionario en su lugar. Por ejemplo, si desea configurar valores preestablecidos booleanos para un conjunto de indicadores de una letra, puede hacer esto:

>>> flags = dict.fromkeys(["a", "b", "c"], True)
>>> flags.update(dict.fromkeys(["d", "e"], False))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Si lo prefiere, también puede hacerlo con una sola declaración de asignación:

>>> flags = dict(dict.fromkeys(["a", "b", "c"], True),
...              **dict.fromkeys(["d", "e"], False))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

El segundo parámetro dictno está completamente diseñado para esto: en realidad está destinado a permitirle anular elementos individuales del diccionario utilizando argumentos de palabras clave como d=False. El código anterior explota el resultado de la siguiente expresión **en un conjunto de argumentos de palabras clave que se pasan a la función llamada. Esta es ciertamente una forma confiable de crear diccionarios, y las personas parecen estar al menos aceptando este idioma, pero sospecho que algunos pueden considerarlo poco propicio. </disclaimer>


Otro enfoque, que probablemente sea el más intuitivo si va a utilizar este patrón con frecuencia, es definir sus datos como una lista de valores de bandera ( True, False) asignados a nombres de bandera (cadenas de un solo carácter). Luego transforma esta definición de datos en un diccionario invertido que asigna nombres de banderas a valores de banderas. Esto se puede hacer de manera bastante sucinta con una comprensión de lista anidada, pero aquí hay una implementación muy legible:

>>> def invert_dict(inverted_dict):
...     elements = inverted_dict.iteritems()
...     for flag_value, flag_names in elements:
...         for flag_name in flag_names:
...             yield flag_name, flag_value
... 
>>> flags = {True: ["a", "b", "c"], False: ["d", "e"]}
>>> flags = dict(invert_dict(flags))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

La función invert_dictes una función generadora . Se genera , o rendimientos - lo que significa que devuelve repetidamente valores de - pares de valores clave. Esos pares clave-valor son el inverso del contenido de los dos elementos del flagsdiccionario inicial . Se alimentan al dictconstructor. En este caso, el dictconstructor funciona de manera diferente al anterior porque se está alimentando de un iterador en lugar de un diccionario como argumento.


Basándose en el comentario de @Chris Lutz: si realmente va a usar esto para valores de un solo carácter, en realidad puede hacer

>>> flags = {True: 'abc', False: 'de'}
>>> flags = dict(invert_dict(flags))
>>> print flags
{'a': True, 'c': True, 'b': True, 'e': False, 'd': False}

Esto funciona porque las cadenas de Python son iterables , lo que significa que se pueden mover valor por valor. En el caso de una cadena, los valores son los caracteres individuales de la cadena. Entonces, cuando se interpretan como iterables, como en este caso donde se usan en un bucle for, ['a', 'b', 'c']y 'abc'son efectivamente equivalentes. Otro ejemplo sería cuando se pasan a una función que toma un iterable, comotuple .

Yo personalmente no haría esto porque no se lee intuitivamente: cuando veo una cadena, espero que se use como un valor único en lugar de como una lista. Entonces miro la primera línea y pienso "Bien, entonces hay una bandera verdadera y una bandera falsa". Entonces, aunque es una posibilidad, no creo que sea el camino a seguir. Por el lado positivo, puede ser útil explicar los conceptos de iterables e iteradores más claramente.


Definiendo la función invert_dict modo que realmente devuelva un diccionario tampoco es una mala idea; La mayoría de las veces no hice eso porque realmente no ayuda a explicar cómo funciona la rutina.


Aparentemente, Python 2.7 tiene comprensión de diccionario, lo que sería una forma extremadamente concisa de implementar esa función. Esto se deja como un ejercicio para el lector, ya que no tengo Python 2.7 instalado :)

También puede combinar algunas funciones del módulo itertools siempre versátil . Como dicen, hay más de una forma de hacerlo . Espera, la gente de Python no dice eso. Bueno, es cierto de todos modos en algunos casos. Supongo que Guido nos ha dado una comprensión del diccionario para que haya una forma obvia de hacerlo.

intuido
fuente
1
Tenga en cuenta que ['a', 'b', 'c']se puede acortar a list('abc'), lo que inspira def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dutilizado comovalues = truth_values("abc", "de")
Chris Lutz
Muchas gracias, esto parece una respuesta muy completa, lo examinaré detenidamente y experimentaré con lo que usted prosa, lo que está diciendo, aunque probablemente sea cierto, no es natural que pueda hacerlo, así que tendré que lee un poco y juega hasta que entienda completamente lo que quieres decir, especialmente porque los diccionarios son una de mis debilidades en Python todavía. Muchas gracias, volveré :)
Trufa
55
@intuited Estoy asombrado por su respuesta: usted define otro problema que no es el del OP y le encanta hacer una respuesta larga a este otro problema. No quiere asociar cadenas y valores en un diccionario, quiere crear objetos con un identificador y un valor para cada uno.
eyquem
55
@eyquem: ¿Las respuestas largas son malas? ¡Médico, cúrate a ti mismo!
John Machin
55
Esto no responde la pregunta, y es condescendiente. Vea la respuesta a continuación
kodu
225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
Shariq
fuente
21
@La notación de coma posterior de Imray (d,)crea una tupla de un elemento, que es un tipo de secuencia. Los tipos de secuencia admiten sumas y multiplicaciones, entre otras operaciones.
duozmo
36
Truco elegante, pero tenga en cuenta el uso de esto en elementos mutables como listas. Por ejemplo a, b, c = ([],)*3no crea 3 instancias de lista, pero hace a, by cque apunta a la misma instancia.
Zac
55
Me pregunto cuánto tiempo pierdo / gasto tratando de hacer que mi código de Python sea súper pitónico.
SARose
3
@Zac para hacerlo correctamente utilizar a, b, c = ([] for i in range(3)). fuente . Para mantener la coherencia, también se puede utilizar una variante de que para esta respuesta, es decir, a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)).
Novato C
Este es un ejemplo de por qué me encanta Python, solo lo uso recientemente ... Desearía que comenzara antes ... Pero el desarrollo web varía según el proyecto.
Enojado 84
52

Use una lista / diccionario o defina su propia clase para encapsular las cosas que está definiendo, pero si necesita todas esas variables, puede hacer:

a = b = c = d = e = g = h = i = j = True
f = False
Yan
fuente
1
los vars no se establecerán solo en Verdadero y Falso.
N 1.1
1
@ N1.1: No pude entender lo que quisiste decir con eso.
Trufa
@Trufa Si están configurados en Verdadero / Falso solamente, la forma sugerida es perfecta, pero ¿qué pasa si las variables están configuradas en cosas diferentes?
N 1.1
@ N1.1: Ohh entiendo lo que quieres decir, ¡gracias por la aclaración! En este caso, todos son bools, pero es bueno saberlo. Gracias
Trufa
55
Para una explicación de por qué este es un patrón peligroso (aunque sea un ejemplo de trabajo), lea Notorious BIG
duozmo
10

Esta es una elaboración de @ Jeff M 'sy mis comentarios.

Cuando haces esto:

a, b = c, d

Funciona con tuplas de embalaje y desembalaje. Puede separar los pasos de empaque y desempaque:

_ = c, d
a, b = _

La primera línea crea una tupla llamada _que tiene dos elementos, el primero con el valor de cy el segundo con el valor de d. La segunda línea desempaqueta la _tupla en las variables ay b. Esto desglosa tu gran línea:

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True, True, True, True

En dos líneas más pequeñas:

_ = True, True, True, True, True, False, True, True, True, True
a, b, c, d, e, f, g, h, i, j = _

Le dará exactamente el mismo resultado que la primera línea (incluida la misma excepción si agrega valores o variables a una parte pero olvida actualizar la otra). Sin embargo, en este caso específico, la respuesta de yan es quizás la mejor.

Si tiene una lista de valores, aún puede descomprimirlos. Solo tienes que convertirlo a una tupla primero. Por ejemplo, lo siguiente asignará un valor entre 0 y 9 a cada uno de los apasos j, respectivamente:

a, b, c, d, e, f, g, h, i, j = tuple(range(10))

EDITAR: truco ordenado para asignarlos a todos como verdadero excepto el elemento 5 (variable f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
Chris Lutz
fuente
No sabía que el último era posible, definitivamente lo intentaré, ¡de hecho es un buen truco y puede ser útil!
Trufa
5

Cuando las personas sugieren "usar una lista o tupla u otra estructura de datos", lo que dicen es que, cuando tienes muchos valores diferentes que te interesan, nombrarlos a todos por separado como variables locales puede no ser la mejor manera para hacer cosas.

En cambio, es posible que desee reunirlos en una estructura de datos más grande que se pueda almacenar en una sola variable local.

Intuited mostró cómo podría usar un diccionario para esto, y Chris Lutz mostró cómo usar una tupla para el almacenamiento temporal antes de desempaquetar en variables separadas, pero otra opción a considerar es usar collections.namedtuple para agrupar los valores de forma más permanente.

Entonces podrías hacer algo como:

# Define the attributes of our named tuple
from collections import namedtuple
DataHolder = namedtuple("DataHolder", "a b c d e f g")

# Store our data
data = DataHolder(True, True, True, True, True, False, True)

# Retrieve our data
print(data)
print(data.a, data.f)

Por supuesto, el código real usaría nombres más significativos que "DataHolder" y las letras del alfabeto, por supuesto.

ncoghlan
fuente
Gracias por su respuesta, lo comprobaré como una opción, la cuestión es que ( para este caso particular ) puede no ser útil tener una estructura inmutable. Más adelante comentaré cómo resultó, nuevamente muchas gracias.
Trufa
Puede hacer lo mismo con una clase ordinaria: la definición de DataHoldersimplemente se vuelve un poco más detallada.
ncoghlan
4

¿Cuál es el problema, de hecho?

Si realmente necesita o desea 10 una , b , c , d , e , f , g , h , i , j , no habrá otra posibilidad, en un momento u otro, a escribir una y escritura b y escribir c . ....

Si los valores son todos diferentes, tendrá que escribir por ejemplo

a = 12
b= 'sun'
c = A() #(where A is a class)
d = range(1,102,5)
e = (line in filehandler if line.rstrip())
f = 0,12358
g = True
h = random.choice
i = re.compile('^(!=  ab).+?<span>')
j = [78,89,90,0]

es decir, definir las "variables" individualmente.

O, usando otra escritura, no es necesario usar _:

a,b,c,d,e,f,g,h,i,j =\
12,'sun',A(),range(1,102,5),\
(line for line in filehandler if line.rstrip()),\
0.12358,True,random.choice,\
re.compile('^(!=  ab).+?<span>'),[78,89,90,0]

o

a,b,c,d,e,f,g,h,i,j =\
(12,'sun',A(),range(1,102,5),
 (line for line in filehandler if line.rstrip()),
 0.12358,True,random.choice,
 re.compile('^(!=  ab).+?<span>'),[78,89,90,0])

.

Si algunos de ellos deben tener el mismo valor, es el problema que es demasiado largo para escribir

a, b, c, d, e, f, g, h, i, j = True, True, True, True, True, False, True ,True , True, True 

?

Entonces puedes escribir:

a=b=c=d=e=g=h=i=k=j=True
f = False

.

No entiendo cuál es exactamente tu problema. Si desea escribir un código, está obligado a utilizar los caracteres requeridos para escribir las instrucciones y definiciones. Qué más ?

Me pregunto si tu pregunta no es la señal de que malinterpretas algo.

Cuando se escribe a = 10, no se crea una variable en el sentido de "fragmento de memoria cuyo valor puede cambiar". Esta instrucción:

  • desencadena la creación de un objeto de tipo integery valor 10 y el enlace de un nombre 'a' con este objeto en el espacio de nombres actual

  • o reasigne el nombre 'a' en el espacio de nombres al objeto 10 (porque 'a' estaba previamente vinculado a otro objeto)

Digo eso porque no veo la utilidad para definir 10 identificadores a, b, c ... apuntando a Falso o Verdadero. Si estos valores no cambian durante la ejecución, ¿por qué 10 identificadores? Y si cambian, ¿por qué definir primero los identificadores ?, se crearán cuando sea necesario si no se definieron previamente

Tu pregunta me parece extraña

eyquem
fuente
2

Parece que estás abordando tu problema de la manera incorrecta para mí.

Vuelva a escribir su código para usar una tupla o escriba una clase para almacenar todos los datos.

richo
fuente
Gracias por usted, ¿puede decir eso sin siquiera echar un vistazo al código :) Entiendo que esto no es ideal, y es posible que tenga que volver a formatear, pero su respuesta realmente no resuelve la pregunta. Aunque sí entiendo tu punto.
Trufa
1
Puedo decir que suena así, sí. Es lo que parece. Refactorizar probablemente resolverá su problema. Su problema es un problema de estilo, no funcional, por lo que es difícil ofrecer otra cosa que no sea un metadvice.
richo
1

Me gusta la respuesta más votada; sin embargo, tiene problemas con la lista como se muestra.

  >> a, b = ([0]*5,)*2
  >> print b
  [0, 0, 0, 0, 0]
  >> a[0] = 1
  >> print b
  [1, 0, 0, 0, 0]

Esto se discute en grandes detalles (aquí) , pero la esencia es eso ayb son el mismo objeto con a is bretorno True(lo mismo para id(a) == id(b)). Por lo tanto, si cambia un índice, está cambiando el índice de ambos ay b, dado que están vinculados. Para resolver esto puedes hacer (fuente)

>> a, b = ([0]*5 for i in range(2))
>> print b
[0, 0, 0, 0, 0]
>> a[0] = 1
>> print b
[0, 0, 0, 0, 0]

Esto se puede usar como una variante de la respuesta principal, que tiene los resultados intuitivos "deseados"

>> a, b, c, d, e, g, h, i = (True for i in range(9))
>> f = (False for i in range(1)) #to be pedantic
Novato C
fuente
1

En su caso, usaría YAML.

Ese es un estándar elegante y profesional para tratar con múltiples parámetros. Los valores se cargan desde un archivo separado. Puedes ver información en este enlace:

https://keleshev.com/yaml-quick-introduction

Pero es más fácil buscarlo en Google, ya que es un estándar, hay cientos de información al respecto, puede encontrar lo que mejor se adapte a su comprensión. ;)

Atentamente.

Henrique LR
fuente
Hola Henrique, bienvenido a SO, ¡gracias por tu respuesta! ¡Asegúrate de leer sobre cómo escribir una respuesta antes de responder tu próxima pregunta!
Diggy.
0

Al igual que JavaScript , también puede usar varias declaraciones en una línea en Pythona = 1; b = "Hello World"; c += 3

Isaac Frost
fuente