Bardziej elegancki sposób deklarowania wielu zmiennych w tym samym czasie

140

Aby zadeklarować wiele zmiennych w „tym samym czasie”, zrobiłbym:

a, b = True, False

Ale gdybym miał zadeklarować znacznie więcej zmiennych, robi się coraz mniej elegancko:

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

Czy jest na to lepszy / elegancki / wygodny sposób?

To musi być bardzo podstawowe, ale jeśli użyję listy lub krotki do przechowywania zmiennych, jak powinienem podejść, aby być pomocnym, skoro:

aList = [a,b]

Jest nieważne, musiałbym zrobić:

a, b = True, True

Albo czego mi brakuje?

Trufa
źródło
Użyć listy do przechowywania tych wartości? Słownik? (Nazwana) krotka?
Jeff Mercado,
@Chris: Właśnie tam zmierzałem. :)
Jeff Mercado
@JeffM: może, ale nie wiem, jak to zrobić, wydaje się, że muszą być zdefiniowane, aby należeć do listy (oczywiście mogę się mylić)
Trufa
3
@Trufa: Jeśli zamierzasz zadeklarować, że wiele zmiennych do przechowywania wartości, jest to już znak, powinieneś rozważyć inne alternatywy przechowywania IMHO.
Jeff Mercado,
1
@ user470379 - W pewnym sensie założyłem, że nazwy są tylko przykładowym kodem i że Trufa nie używa tych nazw w swoim prawdziwym kodzie.
Chris Lutz,

Odpowiedzi:

52

Jak sugerowali inni, jest mało prawdopodobne, że użycie 10 różnych zmiennych lokalnych z wartościami boolowskimi jest najlepszym sposobem na napisanie procedury (zwłaszcza jeśli naprawdę mają one jednoliterowe nazwy :)

W zależności od tego, co robisz, sensowne może być użycie słownika. Na przykład, jeśli chcesz ustawić domyślne wartości logiczne dla zestawu jednoliterowych flag, możesz to zrobić:

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

Jeśli wolisz, możesz to również zrobić za pomocą jednej instrukcji przypisania:

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

Drugi parametr dictto nie jest do tego przeznaczony: tak naprawdę ma na celu nadpisanie poszczególnych elementów słownika za pomocą argumentów słów kluczowych, takich jak d=False. Powyższy kod wysadza wynik następującego wyrażenia **do zestawu argumentów słów kluczowych, które są przekazywane do wywoływanej funkcji. Jest to z pewnością niezawodny sposób tworzenia słowników i wydaje się, że ludzie przynajmniej akceptują ten idiom, ale podejrzewam, że niektórzy mogą uznać go za niepytoniczny. </disclaimer>


Jeszcze innym podejściem, które jest prawdopodobnie najbardziej intuicyjne, jeśli będziesz często używać tego wzorca, jest zdefiniowanie danych jako listy wartości flag ( True, False) odwzorowanych na nazwy flag (ciągi jednoznakowe). Następnie należy przekształcić tę definicję danych w odwrócony słownik, który odwzorowuje nazwy flag na wartości flag. Można to zrobić dość zwięźle za pomocą zagnieżdżonych list składanych, ale oto bardzo czytelna implementacja:

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

Funkcja invert_dictjest funkcją generatora . To generuje , lub plonów - co oznacza, że wielokrotnie zwraca wartości - par klucz-wartość. Te pary klucz-wartość są odwrotnością zawartości dwóch elementów pierwotnego flagssłownika. Są wprowadzane do dictkonstruktora. W tym przypadku dictkonstruktor działa inaczej niż powyżej, ponieważ jako argument jest podawany do iteratora, a nie do słownika.


Opierając się na komentarzu @Chris Lutz: Jeśli naprawdę będziesz używać tego do wartości jednoznakowych, możesz to zrobić

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

To działa, ponieważ łańcuchy Pythona są iterowalne , co oznacza, że ​​można je przesuwać przez wartość według wartości. W przypadku łańcucha wartościami są poszczególne znaki w ciągu. Więc kiedy są interpretowane jako iterowalne, jak w tym przypadku, gdy są używane w pętli for, ['a', 'b', 'c']i 'abc'są w rzeczywistości równoważne. Innym przykładem może być sytuacja, gdy są one przekazywane do funkcji, która przyjmuje iterowalną funkcję, na przykład tuple.

Osobiście nie zrobiłbym tego, ponieważ nie czyta intuicyjnie: kiedy widzę ciąg, spodziewam się, że zostanie on użyty jako pojedyncza wartość, a nie jako lista. Patrzę więc na pierwszą linię i myślę „OK, więc jest flaga Prawda i Flaga fałszu”. Więc chociaż jest taka możliwość, nie sądzę, żeby to była droga. Z drugiej strony może pomóc jaśniej wyjaśnić pojęcia iterowalne i iteratory.


Zdefiniowanie funkcji invert_dicttak, aby faktycznie zwracała słownik, również nie jest złym pomysłem; W większości po prostu tego nie robiłem, ponieważ nie pomaga to wyjaśnić, jak działa rutyna.


Najwyraźniej Python 2.7 ma wyrażenia słownikowe, co stanowiłoby niezwykle zwięzły sposób implementacji tej funkcji. Czytelnikowi pozostawiam to jako ćwiczenie, ponieważ nie mam zainstalowanego Pythona 2.7 :)

Możesz także łączyć niektóre funkcje z zawsze wszechstronnego modułu itertools . Jak mówią, jest więcej niż jeden sposób, aby to zrobić . Czekaj, ludzie Pythona tego nie mówią. Cóż, w niektórych przypadkach to prawda. Sądzę, że Guido podał nam wyrażenia słownikowe, aby można było to zrobić w jeden oczywisty sposób .

intuicyjnie
źródło
1
Zauważ, że ['a', 'b', 'c']można to skrócić do list('abc'), co inspiruje def truth_values(trues, falses): d = dict.from_keys(list(trues), True); d.update(dict.from_keys(list(falses), False)); return dużywane jakovalues = truth_values("abc", "de")
Chris Lutz
Dziękuję bardzo, wydaje się to bardzo wyczerpującą odpowiedzią, przyjrzę się jej dobrze i poeksperymentuję z tym, co prozujesz, co mówisz, chociaż to prawdopodobnie prawda, to nie jest naturalne, więc będę musiał poczytaj trochę i pobaw się, aż w pełni zrozumiem, co masz na myśli, zwłaszcza, że ​​słowniki to jedna z moich słabych stron w Pythonie. Dziękuję bardzo, wrócę :)
Trufa
5
@intuited Twoja odpowiedź zaskoczyła mnie: definiujesz inny problem niż ten z PO i z przyjemnością udzielasz długiej odpowiedzi na ten inny problem. Nie chce kojarzyć łańcuchów i wartości w słowniku, chce tworzyć obiekty z identyfikatorem i wartością dla każdego z nich.
eyquem
5
@eyquem: Długie odpowiedzi są złe? Doktorze, ulecz się!
John Machin
5
To nie odpowiada na pytanie i jest protekcjonalne. Zobacz odpowiedź poniżej
kodu
225
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
Shariq
źródło
21
@Imray Końcowa notacja z przecinkiem (d,)tworzy krotkę z jednym elementem, która jest typem sekwencji. Typy sekwencji obsługują między innymi dodawanie i mnożenie.
duozmo
36
Elegancka sztuczka, ale pamiętaj o używaniu jej na zmiennych elementach, takich jak listy. Na przykład a, b, c = ([],)*3nie tworzy 3 Wykaz wystąpień, ale czyni a, bi cwskazując na tej samej instancji.
Zac
5
Zastanawiam się, ile czasu marnuję / spędzam, próbując uczynić mój kod Pythona uber pythonic?
SARose
3
@Zac, aby poprawnie to zrobić, użyj a, b, c = ([] for i in range(3)). źródło . Aby zachować spójność, możesz również użyć wariantu tego w tej odpowiedzi, tj a,b,c,d,e,g,h,i,j = (True for i in range(9)) f=(False i in range(1)).
Nowicjusz C
To jest przykład, dlaczego kocham Pythona. Używam go dopiero od niedawna .. Chciałbym, żeby zaczęło się wcześniej .. Ale tworzenie stron internetowych różni się w zależności od projektu.
Angry 84
52

Użyj listy / słownika lub zdefiniuj własną klasę, aby hermetyzować rzeczy, które definiujesz, ale jeśli potrzebujesz wszystkich tych zmiennych, możesz zrobić:

a = b = c = d = e = g = h = i = j = True
f = False
yan
źródło
1
vars nie zostaną ustawione tylko na True i False.
N 1.1
1
@ N1.1: Nie mogłem zrozumieć, co masz na myśli.
Trufa
@Trufa Jeśli są ustawione tylko na Prawda / Fałsz, sugerowany sposób jest doskonały, ale co, jeśli zmienne są ustawione na różne rzeczy?
N 1.1
@ N1.1: Och, rozumiem, co masz na myśli, dzięki za wyjaśnienie! W tym przypadku wszystkie są wartościami logicznymi, ale dobrze jest wiedzieć. Dzięki
Trufa
5
Aby uzyskać wyjaśnienie, dlaczego jest to niebezpieczny wzór (choć działający przykład), przeczytaj Notorious BIG
duozmo
10

To jest rozwinięcie @ Jeff M 's i moich komentarzy.

Kiedy to robisz:

a, b = c, d

Działa z pakowaniem i rozpakowywaniem krotek. Możesz oddzielić etapy pakowania i rozpakowywania:

_ = c, d
a, b = _

Pierwsza linia tworzy zwaną krotkę, _która ma dwa elementy, pierwszy o wartości, ca drugi o wartości d. Druga linia rozpakowuje _krotkę do zmiennych ai b. To rozbija twoją jedną ogromną linię:

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

Na dwie mniejsze linie:

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

Daje dokładnie taki sam wynik jak pierwsza linia (w tym ten sam wyjątek, jeśli dodasz wartości lub zmienne do jednej części, ale zapomnisz zaktualizować drugą). Jednak w tym konkretnym przypadku odpowiedź yan jest prawdopodobnie najlepsza.

Jeśli masz listę wartości, nadal możesz je rozpakować. Musisz tylko najpierw przekonwertować go na krotkę. Na przykład, następujące przypisanie wartości od 0 do 9, do każdego z aprzelotowym jodpowiednio:

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

EDYCJA: Sprytna sztuczka, aby przypisać je wszystkie jako prawdziwe z wyjątkiem elementu 5 (zmienne f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
Chris Lutz
źródło
Nie wiedziałem, że ostatnia jest możliwa, na pewno spróbuję, to naprawdę fajna sztuczka i może się przydać!
Trufa
5

Kiedy ludzie sugerują „użyj listy, krotki lub innej struktury danych”, mówią, że kiedy masz wiele różnych wartości, na których Ci zależy, nazwanie ich wszystkich osobno jako zmiennych lokalnych może nie być najlepszym sposobem robić rzeczy.

Zamiast tego możesz chcieć zebrać je razem w większą strukturę danych, którą można przechowywać w jednej zmiennej lokalnej.

intuited pokazał, jak można w tym celu użyć słownika, a Chris Lutz pokazał, jak używać krotki do tymczasowego przechowywania przed rozpakowaniem do osobnych zmiennych, ale inną opcją do rozważenia jest użycie collections.namedtuplebardziej trwałego pakowania wartości.

Więc możesz zrobić coś takiego:

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

Miejmy nadzieję, że prawdziwy kod używałby bardziej znaczących nazw niż „DataHolder” i oczywiście litery alfabetu.

ncoghlan
źródło
Dziękuję za odpowiedź, sprawdzę to jako opcję, chodzi o to, że ( w tym konkretnym przypadku ) może nie być przydatne posiadanie i niezmienną strukturę. Później skomentuję, jak to się skończyło, jeszcze raz wielkie dzięki!
Trufa
Możesz zrobić to samo ze zwykłą klasą - definicja DataHolderstaje się po prostu trochę bardziej szczegółowa.
ncoghlan
4

W czym właściwie jest problem?

Jeśli naprawdę potrzebujesz lub chcesz 10 a , b , c , d , e , f , g , h , i , j , nie będzie innej możliwości, w takim czy innym czasie, aby napisać a i napisać b i napisać c . ....

Jeśli wartości są różne, będziesz musiał napisać na przykład

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]

to znaczy definiowanie „zmiennych” indywidualnie.

Lub, używając innego pisma, nie musisz używać _:

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]

lub

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

.

Jeśli część z nich musi mieć tę samą wartość, to problem polega na tym, że jest za długi, aby go napisać

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

?

Następnie możesz napisać:

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

.

Nie rozumiem, na czym dokładnie polega twój problem. Jeśli chcesz napisać kod, jesteś zobowiązany do używania znaków wymaganych przy pisaniu instrukcji i definicji. Co jeszcze ?

Zastanawiam się, czy twoje pytanie nie oznacza, że ​​coś źle zrozumiałeś.

Kiedy się pisze a = 10, nie tworzy się zmiennej w znaczeniu „kawałka pamięci, którego wartość może się zmienić”. Ta instrukcja:

  • albo powoduje utworzenie obiektu typu integeri wartości 10 oraz powiązanie nazwy „a” z tym obiektem w bieżącej przestrzeni nazw

  • lub ponownie przypisz nazwę „a” w przestrzeni nazw do obiektu 10 (ponieważ „a” został wcześniej powiązany z innym obiektem)

Mówię to, ponieważ nie widzę narzędzia do definiowania 10 identyfikatorów a, b, c ... wskazujących na False lub True. Jeśli te wartości nie zmieniają się podczas wykonywania, dlaczego 10 identyfikatorów? A jeśli się zmienią, po co definiować identyfikatory w pierwszej kolejności? Będą one tworzone w razie potrzeby, jeśli nie zostały wcześniej zdefiniowane

Twoje pytanie wydaje mi się dziwne

eyquem
źródło
2

Wygląda na to, że podchodzisz do swojego problemu w niewłaściwy sposób.

Przepisz swój kod, aby użyć krotki lub napisz klasę do przechowywania wszystkich danych.

richo
źródło
Dziękuję, że możesz to powiedzieć bez choćby spojrzenia na kod :) Rozumiem, że to nie jest idealne i być może będę musiał przeformatować, ale twoja odpowiedź tak naprawdę nie rozwiązuje pytania. Jednak rozumiem twój punkt widzenia.
Trufa
1
Mogę powiedzieć, że tak to brzmi, tak. Tak to brzmi. Refaktoryzacja prawdopodobnie rozwiąże Twój problem. Twoim problemem jest styl, a nie funkcjonalność, więc ciężko jest zaoferować coś innego niż całkiem metadvice.
richo
1

Podoba mi się odpowiedź, na którą głosowano najczęściej; jednak ma problemy z listą, jak pokazano.

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

Jest to omówione bardzo szczegółowo (tutaj) , ale sedno jest to, że ai bto ten sam obiekt z a is bpowrotem True(tak samo dla id(a) == id(b)). Dlatego zmieniając indeks, zmieniasz indeks obu ai b, ponieważ są one połączone. Aby rozwiązać ten problem, możesz to zrobić (źródło)

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

Można to następnie użyć jako wariantu górnej odpowiedzi, która daje „pożądane” intuicyjne wyniki

>> a, b, c, d, e, g, h, i = (True for i in range(9))
>> f = (False for i in range(1)) #to be pedantic
Nowicjusz C
źródło
1

W twoim przypadku użyłbym YAML.

To elegancki i profesjonalny standard postępowania z wieloma parametrami. Wartości są ładowane z oddzielnego pliku. Możesz zobaczyć kilka informacji w tym linku:

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

Ale łatwiej go wygooglować, bo to standard, są setki informacji na jego temat, możesz znaleźć to, co najlepiej pasuje do twojego rozumienia. ;)

Z poważaniem.

Henrique LR
źródło
Cześć Henrique, witamy w SO, dziękuję za odpowiedź! Przeczytaj o pisaniu odpowiedzi, zanim odpowiesz na następne pytanie!
Diggy.
0

Podobnie jak JavaScript , możesz również użyć wielu instrukcji w jednej linii w Pythoniea = 1; b = "Hello World"; c += 3

Isaac Frost
źródło