同时声明多个变量的更优雅的方式


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

还是我想念什么?


使用列表存储这些值?一本字典?一个(命名的)元组?
杰夫·梅卡多

@克里斯:我到那儿了。:)
杰夫·梅卡多

@JeffM:也许但我不知道该怎么做,似乎必须对其进行定义才能属于列表(我当然可能错了)
Trufa

3
@Trufa:如果您要声明许多变量来存储值,那已经是一个信号,您应该考虑其他存储替代方案恕我直言。
杰夫·梅卡多

1
@ user470379-我有点假设名称仅用于示例代码,而Trufa并未在其真实代码中使用这些名称。
克里斯·卢茨

Answers:


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。上面的代码将表达式的结果**分解为一组关键字参数,这些参数传递给调用的函数。这无疑是创建字典的可靠方法,人们似乎至少接受了这种习语,但我怀疑有人可能认为它是非Python的。 </disclaimer>


另一种方法,这是最直观的,如果你会经常使用这种模式,是定义你的数据作为标志值(列表TrueFalse映射到标志名(单字符串))。然后,您可以将此数据定义转换为反向字典,该字典将标志名称映射到标志值。使用嵌套列表理解可以很简洁地完成此操作,但是这是一个非常易读的实现:

>>> 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人士不这么说。好吧,在某些情况下还是如此。我猜想Guido已经给了我们字典理解能力,所以会有一种明显的方式来做到这一点。


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

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

21
@Imray尾随逗号表示法(d,)创建一个单项元组,它是一种序列类型。序列类型支持加法和乘法以及其他运算。
duozmo 2014年

36
绝妙的技巧,但请注意在可变元素(例如列表)上使用此技巧。例如a, b, c = ([],)*3不创建3个列出的实例,但品牌abc指向同一个实例。
扎克

5
我想知道我浪费/花费很多时间试图将我的python代码变成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的示例,仅在最近使用。。
生气

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

5
有关为什么这是一种危险模式的解释(尽管是有效的示例),请阅读臭名昭著的BIG
duozmo,2014年

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 = _

它会为您提供与第一行完全相同的结果(如果将值或变量添加到一个部分而忘记更新另一个部分,则包括相同的例外)。但是,在这种特定情况下,yan的答案也许是最好的。

如果您有值列表,则仍然可以解压它们。您只需要先将其转换为元组即可。例如,以下代码将分别为a到分配一个介于0和9之间的值j

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

编辑:巧妙地把所有元素都分配为true,元素5(变量f)除外:

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

我不知道最后一个是可能的,我一定会尝试的,这确实是一个巧妙的技巧,它可能派上用场!
Trufa

5

当人们建议“使用列表,元组或其他数据结构”时,他们的意思是,当您关心许多不同的值时,将它们分别命名为局部变量可能不是最好的方法做事情。

取而代之的是,您可能希望将它们收集到一个更大的数据结构中,该结构可以存储在单个局部变量中。

直观的演示了如何使用字典来实现此目的,克里斯·卢茨(Chris Lutz)演示了在拆分成单独的变量之前如何使用元组进行临时存储,但是要考虑的另一种选择是使用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”和字母更大的有意义的名称。


谢谢您的回答,我将作为一个选项进行检查,问题是(对于这种特定情况)具有不可变的结构可能没有用。稍后,我将对此进行评论,再次感谢!
Trufa

您可以使用普通的类做同样的事情-的定义DataHolder变得更加冗长。
ncoghlan 2011年

4

实际上是什么问题?

如果您确实需要或想要10 abcdefghij,则一次或一次别无其他可能写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”与此对象在当前名称空间中的绑定

  • 或将名称空间中的名称“ a”重新分配给对象10(因为先前已将“ a”绑定到了另一个对象)

我说这是因为我看不到该实用程序定义10个标识符a,b,c ...指向False或True。如果在执行过程中这些值未更改,为什么要使用10个标识符?如果更改了,为什么要先定义标识符?如果没有事先定义,则会在需要时创建它们

你的问题对我来说很奇怪


2

听起来您正以错误的方式对待我。

重写您的代码以使用元组或编写一个类来存储所有数据。


谢谢您可以说一句话,甚至不看代码:)我确实知道这不是理想的选择,我可能不得不重新格式化,但您的回答并不能真正解决问题。不过,我的意思是正确的。
Trufa

1
我可以说听起来像是的。听起来就是这样。重构可能会解决您的问题。您的问题是样式问题,而不是功能问题,因此很难提供除完全metadvice之外的任何其他功能。
richo 2011年

1

我喜欢投票最多的答案;但是,它存在清单所示的问题。

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

对此进行了详细的讨论(在此),但要点是ab都是相同的,并且带有a is breturn True(与相同id(a) == id(b))。因此,如果您更改索引,你正在改变两者的指数ab,因为它们是联系在一起的。为了解决这个问题,你可以做(源)

>> 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来说,它比较容易,因为它是一种标准,因此有成百上千的信息,您可以找到最适合您的理解的信息。;)

最好的祝福。


您好,Henrique,欢迎来到SO,谢谢您的回答!在回答下一个问题之前,请务必先阅读答案
Diggy。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.