Більш елегантний спосіб декларування декількох змінних одночасно


140

Щоб оголосити кілька змінних одночасно ", я би зробив:

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

Або чого я пропускаю?


Використовувати список для зберігання цих значень? Словник? (Названий) кортеж?
Джефф Меркадо

@Chris: Я туди потрапляв. :)
Джефф Меркадо

@JeffM: можливо, але я не знаю, як це зробити, схоже, що їх потрібно визначити, щоб належати до списку (я, мабуть, помиляюся)
Trufa

3
@Trufa: Якщо ви збираєтеся оголосити, що багато змінних для зберігання значень, це вже ознака, вам слід розглянути інші альтернативи для зберігання IMHO.
Джефф Меркадо

1
@ user470379 - Я начебто припускав, що імена були лише для прикладу коду, і що Trufa не використовує ці імена у своєму реальному коді.
Кріс Лутц

Відповіді:


52

Як вважають інші, навряд чи використання 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. Код, наведений вище, підірває результат вираження, що слідує **за набором аргументів ключових слів, які передаються викликаній функції. Це, безумовно, надійний спосіб створення словників, і люди, здається, принаймні сприймають цю ідіому, але я підозрюю, що деякі можуть вважати це непітонічним. </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.

Я особисто не зробив би цього, оскільки він не читається інтуїтивно: коли я бачу рядок, я очікую, що він буде використаний як єдине значення, а не як список. Тож я дивлюся на перший рядок і думаю "Гаразд, значить, є Правда і Неправдивий прапор". Тож хоча це і є можливість, я не думаю, що це шлях. Наверху, це може допомогти більш чітко пояснити поняття ітерабелів та ітераторів.


Визначення функції 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. Дуже дякую, я повернусь :)
Труфа

5
@intuited Я вражений вашою відповіддю: ви визначаєте іншу проблему, ніж проблему ОП, і радієте довго відповісти на цю іншу проблему. Він не хоче асоціювати рядки та значення у словнику, він хоче створити об'єкти з ідентифікатором і значенням для кожного.
оченка

5
@eyquem: довгі відповіді погані? Лікар, вилікуйте себе!
Джон Махін

5
Це не дає відповіді на питання, і це опікується. Дивіться відповідь нижче
kodu

225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False

21
Нотатка з комою @Imray (d,)створює однорядний кортеж, який є типом послідовності. Типи послідовностей підтримують додавання та множення серед інших операцій.
дуозмо

36
Елегантний трюк, але будьте в курсі використання цього змінного елемента, такого як списки. Наприклад , a, b, c = ([],)*3не створюють 3 список примірників , але робить a, bі cвказуючи на один екземпляр.
Зак

5
Мені цікаво, скільки часу я витрачаю / витрачаю, намагаючись зробити свій пітон код uber pythonic?
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)).
Новачок C

Це приклад того, чому я люблю python, тільки використовую його недавно .. Я б хотів розпочатись раніше .. Але веб-розробка відрізняється залежно від проекту.
Сердитий 84

52

Скористайтеся списком / словником або визначте власний клас, щоб інкапсулювати речі, які ви визначаєте, але якщо вам потрібні всі ці змінні, ви можете:

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

1
vrs не буде встановлено лише на значення True & False.
N 1.1,

1
@ N1.1: Я не міг зрозуміти, що ти мав на увазі під цим.
Труфа

@Trufa Якщо вони встановлені лише на True / False, запропонований спосіб ідеальний, але що робити, якщо змінні встановлені на різні речі?
N 1.1,

@ N1.1: О, розумію, дякую за пояснення! У цьому випадку всі вони є загальними, але це добре знати. Спасибі
Труфа

5
Пояснення, чому це небезпечна закономірність (хоч і робочий приклад), читайте на 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))

EDIT: Охайний трюк, щоб призначити їх усі як істинні, крім елемента 5 (змінної f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))

Я не знав, що останній можливий, я обов'язково спробую, це дійсно акуратний трюк, і це може стати в нагоді!
Труфа

5

Коли люди пропонують "використовувати список, кортеж чи іншу структуру даних", вони говорять про те, що, коли у вас є багато різних значень, які вас цікавлять, називати їх усі окремо як локальні змінні не може бути найкращим способом робити речі.

Натомість ви можете зібрати їх разом у більшу структуру даних, яка може зберігатися в одній локальній змінній.

Інтуїтивно показав, як ви можете використовувати словник для цього, а Кріс Лутц показав, як використовувати кортеж для тимчасового зберігання, перш ніж розпаковувати в окремі змінні, але інший варіант, який слід розглянути, - це використовувати 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" та букви алфавіту, звичайно.


Дякую за вашу відповідь, я буду перевіряти це як варіант, річ у тому, що ( для цього конкретного випадку ) це може бути не корисно мати та непорушну структуру. Пізніше прокоментую, як це вийшло, ще раз дякую!
Труфа

Можна зробити те ж саме і зі звичайним класом - визначення DataHolderпросто набуває трохи більше багатослівного.
ncoghlan

4

У чому проблема, насправді?

Якщо ви дійсно потребуєте або хочете 10 a , b , c , d , e , f , g , h , i , j , іншої можливості не буде, за той чи інший час, написати a і написати b і write 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' з цим об'єктом у поточному просторі імен

  • або повторно призначити ім'я 'a' у просторі імен об’єкту 10 (тому що 'a' попередньо було прив’язано до іншого об'єкта)

Я це кажу, оскільки не бачу утиліти для визначення 10 ідентифікаторів a, b, c ..., що вказують на False або True. Якщо ці значення не змінюються під час виконання, чому 10 ідентифікаторів? І якщо вони змінюються, навіщо спочатку визначати ідентифікатори ?, вони будуть створені за потреби, якщо вони не були визначені раніше

Ваше запитання мені здається дивним


2

Здається, що ви ставитесь до своєї проблеми неправильно до мене.

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


Дякую за те, що ви можете сказати, що навіть не проглядаючи код :) Я розумію, що це не ідеально, і мені, можливо, доведеться переформатувати, але ваша відповідь не вирішує питання. Я все-таки розумію вашу думку.
Труфа

1
Я можу сказати, що це звучить так, так. Це як звучить. Рефакторинг, ймовірно, вирішить вашу проблему. Ваша проблема - це питання стилю, а не функціональність, тому важко запропонувати що-небудь, крім цілком метадавців.
Річо

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

1

У вашому випадку я б використовував YAML.

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

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

Але Google це простіше, оскільки це стандарт, про нього є сотні інформації, ви можете знайти те, що найкраще відповідає вашому розумінню. ;)

З найкращими побажаннями.


Привіт, Енріке, ласкаво просимо до ДУ, дякую за відповідь! Будь ласка, переконайтеся, що читайте про написання відповіді, перш ніж відповісти на наступне запитання!
Діггі.

0

Як і JavaScript, ви також можете використовувати кілька операторів на одному рядку в pythona = 1; b = "Hello World"; c += 3

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.