繁体   English   中英

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

[英]More elegant way of declaring multiple variables at the same time

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

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

或者我错过了什么?

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

正如其他人所建议的那样,使用 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>


如果您经常使用这种模式,那么另一种方法可能是最直观的,它是将您的数据定义为映射到标志名称(单字符字符串)的标志值( 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 已经给了我们字典理解,以便有一种明显的方法来做到这一点。

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

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

这是对@ Jeff M和我的评论的详细说明。

当你这样做时:

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

它将为您提供与第一行完全相同的结果(如果您向一个部分添加值或变量但忘记更新另一个,则包括相同的异常)。 但是,在这种特定情况下, 严的回答可能是最好的。

如果您有一个值列表,您仍然可以解压缩它们。 您只需要先将其转换为元组。 例如,下面将分别为aja分配一个介于 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))

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

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

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

究竟有什么问题呢?

如果您真的需要或想要 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 个标识符? 如果它们发生变化,为什么要先定义标识符?,如果没有事先定义,它们将在需要时创建

你的问题对我来说很奇怪

听起来你对我来说以错误的方式解决你的问题。

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

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

  >> 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的索引,因为它们是链接的。 要解决这个问题,您可以这样做(来源)

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

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

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

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

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

此致。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM