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

Posted

技术标签:

【中文标题】同时声明多个变量的更优雅的方式【英文标题】:More elegant way of declaring multiple variables at the same time 【发布时间】:2011-07-26 14:30:01 【问题描述】:

要同时声明多个变量,我会这样做:

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:如果您要声明这么多变量来存储值,这已经表明您应该考虑其他存储替代方案恕我直言。 @user470379 - 我有点假设这些名称只是用于示例代码,而 Trufa 并没有在他的真实代码中使用这些名称。 【参考方案1】:
a, b, c, d, e, g, h, i, j = (True,)*9
f = False

【讨论】:

@Imray 尾随逗号符号(d,) 创建一个单项元组,它是一个序列类型。 Sequence types 支持加法和乘法等运算。 优雅的技巧,但要注意在列表等可变元素上使用它。例如 a, b, c = ([],)*3 不会创建 3 个列表实例,而是使 abc 指向同一个实例。 我想知道我浪费/花费了多少时间试图让我的python代码超级pythonic? @Zac 正确使用a, b, c = ([] for i in range(3))。 source。为了保持一致性,您还可以为此答案使用它的变体,即a,b,c,d,e,g,h,i,j = (True for i in range(9))f=(False i in range(1)) 这是我为什么喜欢python的一个例子,最近才使用它..我希望早点开始..但是web开发确实因项目而异。【参考方案2】:

正如其他人所建议的那样,使用 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 之类的关键字参数覆盖字典的各个元素。上面的代码将** 后面的表达式的结果分解为一组keyword arguments,这些keyword arguments 将传递给被调用的函数。这无疑是一种创建字典的可靠方法,人们似乎至少接受了这个成语,但我怀疑有些人可能认为它是 Unpythonic。 </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 是generator function。它生成,或yields——意味着它重复地返回——键值对的值。这些键值对与初始 flags 字典的两个元素的内容相反。它们被输入dict 构造函数。在这种情况下,dict 构造函数的工作方式与上面不同,因为它的参数是 iterator 而不是字典。


借鉴@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 字符串是 iterable,这意味着它们可以逐个值移动。在字符串的情况下,值是字符串中的单个字符。因此,当它们被解释为可迭代对象时,例如在 for 循环中使用它们时,['a', 'b', 'c']'abc' 实际上是等效的。另一个例子是当它们被传递给一个接受迭代的函数时,比如tuple

我个人不会这样做,因为它不直观地阅读:当我看到一个字符串时,我希望它被用作单个值而不是列表。所以我看着第一行并想“好的,所以有一个 True 标志和一个 False 标志。”因此,尽管这是一种可能性,但我认为这不是要走的路。从好的方面来说,它可能有助于更清楚地解释可迭代对象和迭代器的概念。


定义函数invert_dict 使其实际返回字典也不是一个坏主意;我大多只是没有这样做,因为它并不能真正帮助解释例程的工作原理。


显然,Python 2.7 具有字典解析,这将为实现该功能提供一种极其简洁的方式。这留给读者作为练习,因为我没有安装 Python 2.7 :)

您还可以组合功能多样的itertools 模块中的一些功能。正如他们所说,There's More Than One Way To Do It。等等,Python 人不会这么说。好吧,无论如何,在某些情况下确实如此。我猜 Guido 已经给了我们字典理解,所以会有One Obvious Way 来做这件事。

【讨论】:

注意['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 中的弱点之一。非常感谢,我会回来的:) @intuited 你的回答让我大吃一惊:你定义了另一个问题,而不是 OP 的问题,你很高兴对另一个问题做出长篇大论的回答。他不想将字典中的字符串和值关联起来,他想为每个对象创建一个标识符和一个值的对象。 @eyquem:长答案不好?医生,治愈你自己! 这并没有回答问题,这是光顾。请看下面的答案【参考方案3】:

使用列表/字典或定义您自己的类来封装您定义的内容,但如果您需要所有这些变量,您可以这样做:

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

【讨论】:

vars 不会被设置为 True & False only。 @N1.1:我不明白你的意思。 @Trufa 如果仅将它们设置为 True/False,建议的方法是完美的,但是如果将变量设置为不同的东西呢? @N1.1:哦,我明白你的意思了,谢谢你的澄清!在这种情况下,它们都是布尔值,但很高兴知道。谢谢 要了解为什么这是一种危险的模式(尽管是可行的示例),请阅读Notorious B.I.G.【参考方案4】:

这是对@Jeff M 和我的cmets 的阐述。

当你这样做时:

a, b = c, d

它适用于元组打包和解包。您可以将打包和拆包步骤分开:

_ = c, d
a, b = _

第一行创建了一个名为_ 的元组,它有两个元素,第一个元素的值为c,第二个元素的值为d。第二行将_ 元组解包到变量ab 中。这打破了你的一大行:

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's answer 可能是最好的。

如果你有一个值列表,你仍然可以解压它们。您只需先将其转换为元组。例如,以下将分别为aj 中的每一个分配一个介于 0 和 9 之间的值:

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

编辑:将除元素 5(变量 f)之外的所有这些都分配为 true 的巧妙技巧:

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

【讨论】:

我不知道最后一个可能,我一定会试一试,这确实是一个巧妙的技巧,它可能会派上用场!【参考方案5】:

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

相反,您可能希望将它们聚集到一个更大的数据结构中,该结构可以存储在单个局部变量中。

intuited 展示了如何使用字典来实现此目的,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”和字母表中的字母更有意义的名称。

【讨论】:

感谢您的回答,我将把它作为一个选项进行检查,问题是(对于这种特殊情况)它可能没有用且不可变结构体。我稍后会评论结果如何,再次非常感谢! 你可以用一个普通的类做同样的事情 - DataHolder 的定义变得更冗长了。【参考方案6】:

实际上有什么问题?

如果你真的需要或想要 10 个abcde fghij、没有其他可能,一次或一次,写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 个标识符?如果它们发生变化,为什么要先定义标识符?如果没有事先定义,它们将在需要时创建

我觉得你的问题很奇怪

【讨论】:

【参考方案7】:

在我看来,你以错误的方式解决问题。

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

【讨论】:

谢谢你,你能说你连代码都看不到 :) 我明白这并不理想,我可能需要重新格式化,但你的回答并不能真正解决问题.不过我明白你的意思。 我可以说听起来是这样,是的。听起来就是这样。重构可能会解决您的问题。您的问题是样式问题,而不是功能问题,因此除了非常元的建议之外很难提供任何其他内容。【参考方案8】:

我喜欢票数最高的答案;但是,它的列表有问题,如图所示。

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

这在(here) 进行了详细讨论,但要点是ab 是同一个对象,a is b 返回Trueid(a) == id(b) 相同)。因此,如果您更改索引,您将同时更改ab 的索引,因为它们是链接的。要解决这个问题,你可以这样做(source)

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

【讨论】:

【参考方案9】:

在你的情况下,我会使用 YAML 。

这是处理多个参数的优雅而专业的标准。 这些值是从单独的文件中加载的。 您可以在此链接中看到一些信息:

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

但是用谷歌搜索它更容易,因为它是一个标准,有数百条关于它的信息,你可以找到最适合你理解的。 ;)

最好的问候。

【讨论】:

嗨,Henrique,欢迎来到 SO,谢谢您的回答!在回答您的下一个问题之前,请务必阅读writing an answer!【参考方案10】:

javascript 一样,你也可以在 python 的一行中使用多个语句 a = 1; b = "Hello World"; c += 3

【讨论】:

以上是关于同时声明多个变量的更优雅的方式的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 应用程序中处理过滤值的更优雅的方式

ES6的强大变量声明

更简洁优雅的 VARIANT 类型使用方式

Go语言基础-变量

Spark基础学习笔记09:Scala基础

让Python更优雅更易理解