Более элегантный способ объявления нескольких переменных одновременно

141

Чтобы объявить несколько переменных одновременно, я бы сделал:

a, b = True, False

Но если бы мне пришлось объявить гораздо больше переменных, это становилось все менее и менее элегантным:

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

Есть ли лучший / элегантный / удобный способ сделать это?

Это должно быть очень просто, но если я использую список или кортеж для хранения переменных, как мне подойти, чтобы быть полезным, поскольку:

aList = [a,b]

Недействительно, мне нужно будет сделать:

a, b = True, True

Или что мне не хватает?

Труфа
источник
Использовать список для хранения этих значений? Словарь? (Именованный) кортеж?
Джефф Меркадо
@ Крис: Я был там. :)
Джефф Меркадо
@JeffM: возможно, но я не знаю, как это сделать, кажется, что они должны быть определены, чтобы принадлежать списку (я, конечно, могу ошибаться)
Trufa
3
@Trufa: Если вы собираетесь объявить, что много переменных для хранения значений, это уже знак, что вы должны рассмотреть другие альтернативы хранения IMHO.
Джефф Меркадо
1
@ user470379 - Я как бы предположил, что имена были только для примера кода, и что Trufa не использует эти имена в своем реальном коде.
Крис Лутц

Ответы:

53

Как предполагали другие, маловероятно, что использование 10 разных локальных переменных с логическими значениями - лучший способ написать вашу процедуру (особенно если у них действительно однобуквенные имена :)

В зависимости от того, что вы делаете, может иметь смысл вместо этого использовать словарь. Например, если вы хотите установить логические предустановленные значения для набора однобуквенных флагов, вы можете сделать это:

>>> 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}

Если хотите, вы также можете сделать это с помощью одного оператора присваивания:

>>> 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}

Второй параметр dictне полностью предназначен для этого: он действительно предназначен для того, чтобы позволить вам переопределить отдельные элементы словаря, используя аргументы ключевого слова, такие как d=False. Приведенный выше код **превращает результат следующего выражения в набор аргументов ключевого слова, которые передаются вызываемой функции. Это, безусловно, надежный способ создания словарей, и люди, кажется, по крайней мере принимают эту идиому, но я подозреваю, что некоторые могут посчитать ее Unpythonic. </disclaimer>


Еще один подход, который, вероятно, будет наиболее интуитивно понятным, если вы будете часто использовать этот шаблон, состоит в том, чтобы определить ваши данные как список значений флагов ( True, False), сопоставленных с именами флагов (односимвольные строки). Затем вы преобразовываете это определение данных в перевернутый словарь, который сопоставляет имена флагов со значениями флагов. Это можно сделать довольно лаконично с пониманием вложенного списка, но вот очень удобочитаемая реализация:

>>> 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}

Функция invert_dictявляется функцией генератора . Это порождает или выходы - это означает , что она многократно возвращает значения - пар ключ-значение. Эти пары «ключ-значение» являются инверсией содержимого двух элементов исходного flagsсловаря. Они подаются в dictконструктор. В этом случае dictконструктор работает иначе, чем указано выше, потому что в качестве аргумента ему передается итератор, а не словарь.


Опираясь на комментарий @Chris Lutz: если вы действительно будете использовать это для односимвольных значений, вы действительно можете сделать

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

Это работает, потому что строки Python являются итеративными , то есть их можно перемещать по значению по значению. В случае строки значения - это отдельные символы в строке. Итак, когда они интерпретируются как итерируемые объекты, как в этом случае, когда они используются в цикле for, ['a', 'b', 'c']и 'abc'фактически эквивалентны. Другой пример: они передаются в функцию, которая принимает итерацию, например tuple.

Я лично не стал бы этого делать, потому что он не читается интуитивно: когда я вижу строку, я ожидаю, что она будет использоваться как отдельное значение, а не как список. Итак, я смотрю на первую строку и думаю: «Хорошо, есть флаг True и флаг False». Так что, хотя это возможно, я не думаю, что это правильный путь. С другой стороны, это может помочь более четко объяснить концепции итераторов и итераторов.


Определение функции так invert_dict, чтобы она фактически возвращала словарь, тоже неплохая идея; Я просто этого не делал, потому что это не помогает объяснить, как работает распорядок.


По-видимому, в Python 2.7 есть словарные интерпретации, которые позволили бы очень кратко реализовать эту функцию. Это оставлено читателю в качестве упражнения, поскольку у меня не установлен Python 2.7 :)

Вы также можете комбинировать некоторые функции универсального модуля itertools . Как говорится, есть несколько способов сделать это . Погодите, люди Python этого не говорят. Что ж, в некоторых случаях это правда. Я предполагаю, что Гвидо дал нам толкование словаря, чтобы был один очевидный способ сделать это.

интуитивный
источник
1
Обратите внимание, что ['a', 'b', 'c']можно сократить до list('abc'), что вдохновляет def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dиспользовать какvalues = truth_values("abc", "de")
Крис Лутц
Большое спасибо, это кажется очень исчерпывающим ответом, я внимательно его просмотрю и поэкспериментирую с тем, что вы говорите, что вы говорите, хотя это, вероятно, правда, это не естественно, поэтому мне придется почитайте немного и поиграйте, пока я полностью не пойму, что вы имеете в виду, особенно потому, что словари - это одна из моих слабых сторон в python. Большое спасибо, я вернусь :)
Trufa
5
@intuited Я ошеломлен вашим ответом: вы определяете проблему, отличную от проблемы OP, и вам приятно дать длинный ответ на эту другую проблему. Он не хочет связывать строки и значения в словаре, он хочет создавать объекты с идентификатором и значением для каждого из них.
eyquem
5
@eyquem: Длинные ответы - это плохо? Врач, исцели себя!
Джон Мачин
5
Это не отвечает на вопрос, и это снисходительно. Смотрите ответ ниже
kodu
226
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
Шарик
источник
21
@Imray Запятая в конце (d,)создает кортеж из одного элемента, который является типом последовательности. Типы последовательностей , помимо других операций, поддерживают сложение и умножение.
duozmo
36
Элегантный трюк, но не забывайте использовать его с изменяемыми элементами, такими как списки. Например , a, b, c = ([],)*3не создают 3 список экземпляров , но делает a, bи cуказывая на один экземпляр.
Zac
5
Интересно, сколько времени я трачу / трачу на попытки сделать свой код Python убер-питоническим?
SARose
3
@Zac, чтобы правильно это сделать, используйте a, b, c = ([] for i in range(3)). источник . Для единообразия вы также можете использовать вариант этого ответа, то есть a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)).
Novice C
Это пример того, почему я люблю питон. Я использую его недавно ... Я бы хотел начать раньше .. Но веб-разработка зависит от проекта.
Angry 84
52

Используйте список / словарь или определите свой собственный класс для инкапсуляции того, что вы определяете, но если вам нужны все эти переменные, вы можете сделать:

a = b = c = d = e = g = h = i = j = True
f = False
Ян
источник
1
vars не будет иметь только значение True и False.
N 1.1
1
@ N1.1: Я не мог понять, что вы имели в виду.
Trufa
@Trufa Если они установлены только на True / False, предложенный способ идеален, но что, если переменные установлены на разные вещи?
N 1.1
@ N1.1: О, я понял, что вы имеете в виду, спасибо за разъяснения! В данном случае все они логические, но это полезно знать. Спасибо
Trufa
6
Для объяснения того, почему это опасный образец (хотя и рабочий пример), прочтите Notorious BIG
duozmo
10

Это разработка @ Jeff M и моих комментариев.

Когда вы это сделаете:

a, b = c, d

Работает с упаковкой и распаковкой кортежей. Вы можете разделить этапы упаковки и распаковки:

_ = c, d
a, b = _

Первая строка создает кортеж с именем _, состоящий из двух элементов, первый со значением, cа второй со значением d. Вторая строка распаковывает _кортеж в переменные aи b. Это разбивает вашу одну огромную строку:

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

На две более мелкие строки:

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

Это даст вам тот же результат, что и первая строка (включая то же исключение, если вы добавляете значения или переменные в одну часть, но забываете обновить другую). Однако в этом конкретном случае ответ Яна, пожалуй, лучший.

Если у вас есть список значений, вы все равно можете их распаковать. Вам просто нужно сначала преобразовать его в кортеж. Например, следующее будет присвоить значение от 0 до 9 каждому из aдо j, соответственно:

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

РЕДАКТИРОВАТЬ: изящный трюк, чтобы назначить все из них как истинные, кроме элемента 5 (переменная f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
Крис Лутц
источник
Я не знал, что последний вариант возможен, я обязательно попробую, это действительно изящный трюк, и он может пригодиться!
Trufa
5

Когда люди предлагают «использовать список, кортеж или другую структуру данных», они говорят, что, когда у вас много разных значений, которые вас волнуют, называть их все отдельно, поскольку локальные переменные могут быть не лучшим способом делать вещи.

Вместо этого вы можете собрать их вместе в более крупную структуру данных, которая может храниться в одной локальной переменной.

intuited показал, как для этого можно использовать словарь, а Крис Лутц показал, как использовать кортеж для временного хранения перед распаковкой в ​​отдельные переменные, но еще один вариант, который следует рассмотреть, - это использовать collections.namedtupleдля более постоянного связывания значений.

Итак, вы можете сделать что-то вроде:

# 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)

Мы надеемся, что в реальном коде будут использоваться более значимые имена, чем «DataHolder» и, конечно, буквы алфавита.

Ncoghlan
источник
Спасибо за ваш ответ, я проверю это как вариант, дело в том, что ( для этого конкретного случая ) может быть бесполезно иметь неизменную структуру. Я прокомментирую позже, как это получилось, еще раз большое спасибо!
Trufa
Вы можете сделать то же самое с обычным классом - DataHolderпросто определение становится более подробным.
ncoghlan
4

В чем собственно проблема?

Если вам действительно нужно или вы хотите 10 a , b , c , d , e , f , g , h , i , j , не будет другой возможности, в то или иное время, написать a, написать b и написать c . ....

Если значения все разные, вы должны будете написать, например,

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]

то есть определение «переменных» индивидуально.

Или, используя другое письмо, не нужно использовать _:

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]

или

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])

.

Если некоторые из них должны иметь одинаковое значение, проблема в том, что писать слишком долго

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

?

Тогда вы можете написать:

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

.

Я не понимаю, в чем именно твоя проблема. Если вы хотите написать код, вы обязаны использовать символы, необходимые для написания инструкций и определений. Что еще ?

Интересно, не является ли ваш вопрос признаком того, что вы что-то неправильно поняли.

Когда кто-то пишет a = 10, он не создает переменную в смысле «фрагмента памяти, значение которого может изменяться». Эта инструкция:

  • либо запускает создание объекта типа integerи значения 10 и привязку имени 'a' к этому объекту в текущем пространстве имен

  • или повторно присвоить объекту 10 имя 'a' в пространстве имен (потому что 'a' был ранее привязан к другому объекту)

Я говорю это, потому что не вижу утилиты для определения 10 идентификаторов a, b, c ... указывающих на False или True. Если эти значения не меняются во время выполнения, почему 10 идентификаторов? И если они изменятся, зачем сначала определять идентификаторы? Они будут созданы при необходимости, если не определены заранее

Ваш вопрос кажется мне странным

Eyquem
источник
2

Похоже, ты неправильно подходишь к своей проблеме.

Перепишите свой код, чтобы использовать кортеж, или напишите класс для хранения всех данных.

рико
источник
Спасибо за то, что вы можете сказать это, даже не взглянув на код :) Я понимаю, что это не идеально, и мне, возможно, придется переформатировать, но ваш ответ на самом деле не решает вопрос. Хотя я понимаю вашу точку зрения.
Trufa
1
Могу сказать, что это так звучит, да. Это то, на что это похоже. Рефакторинг, скорее всего, решит вашу проблему. Ваша проблема связана со стилем, а не с функциональностью, поэтому трудно предложить что-либо, кроме довольно метасоветов.
richo
1

Мне нравится ответ, получивший наибольшее количество голосов; однако у него есть проблемы со списком, как показано.

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

Это подробно обсуждается (здесь) , но суть в том, что aи bэто один и тот же объект с a is bвозвратом True(то же самое для id(a) == id(b)). Поэтому, если вы меняете индекс, вы меняете индекс обоих aи b, поскольку они связаны. Чтобы решить эту проблему, вы можете сделать (источник)

>> 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]

Затем это можно использовать как вариант основного ответа, который дает "желаемые" интуитивно понятные результаты.

>> a, b, c, d, e, g, h, i = (True for i in range(9))
>> f = (False for i in range(1)) #to be pedantic
Новичок C
источник
1

В вашем случае я бы использовал YAML.

Это элегантный и профессиональный стандарт для работы с несколькими параметрами. Значения загружаются из отдельного файла. Вы можете увидеть некоторую информацию по этой ссылке:

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

Но его проще погуглить, так как это стандарт, о нем сотни информации, вы можете найти то, что лучше всего подходит для вашего понимания. ;)

С уважением.

Энрике Л.Р.
источник
Привет, Энрике, добро пожаловать в SO, спасибо за ответ! Пожалуйста, внимательно прочитайте, как написать ответ, прежде чем отвечать на свой следующий вопрос!
Дигги.
0

Как и JavaScript, вы также можете использовать несколько операторов в одной строке в Pythona = 1; b = "Hello World"; c += 3

Исаак Фрост
источник