More elegant way of declaring multiple variables a

2019-01-12 16:55发布

To declare multiple variables at the "same time" I would do:

a, b = True, False

But if I had to declare much more variables, it turns less and less elegant:

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

Is there a better / elegant / convenient way to do this?

Thanks in advance!

Edit:

This must be very basic, but if I do used a list or a tuple for storing the variables, how would I have to approach so that I would be helpful since:

aList = [a,b]

Is not valid, I would have to do:

a, b = True, True

Or what am I missing?

8条回答
forever°为你锁心
2楼-- · 2019-01-12 17:02
a, b, c, d, e, g, h, i, j = (True,)*9
f = False
查看更多
不美不萌又怎样
3楼-- · 2019-01-12 17:05

Use a list/dictionary or define your own class to encapsulate the stuff you're defining, but if you need all those variables you can do:

a = b = c = d = e = g = h = i = j = True
f = False
查看更多
等我变得足够好
4楼-- · 2019-01-12 17:10

As others have suggested, it's unlikely that using 10 different local variables with Boolean values is the best way to write your routine (especially if they really have one-letter names :)

Depending on what you're doing, it may make sense to use a dictionary instead. For example, if you want to set up Boolean preset values for a set of one-letter flags, you could do this:

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

If you prefer, you can also do it with a single assignment statement:

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

The second parameter to dict isn't entirely designed for this: it's really meant to allow you to override individual elements of the dictionary using keyword arguments like d=False. The code above blows up the result of the expression following ** into a set of keyword arguments which are passed to the called function. This is certainly a reliable way to create dictionaries, and people seem to be at least accepting of this idiom, but I suspect that some may consider it Unpythonic. </disclaimer>


Yet another approach, which is likely the most intuitive if you will be using this pattern frequently, is to define your data as a list of flag values (True, False) mapped to flag names (single-character strings). You then transform this data definition into an inverted dictionary which maps flag names to flag values. This can be done quite succinctly with a nested list comprehension, but here's a very readable implementation:

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

The function invert_dict is a generator function. It generates, or yields — meaning that it repeatedly returns values of — key-value pairs. Those key-value pairs are the inverse of the contents of the two elements of the initial flags dictionary. They are fed into the dict constructor. In this case the dict constructor works differently from above because it's being fed an iterator rather than a dictionary as its argument.


Drawing on @Chris Lutz's comment: If you will really be using this for single-character values, you can actually do

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

This works because Python strings are iterable, meaning that they can be moved through value by value. In the case of a string, the values are the individual characters in the string. So when they are being interpreted as iterables, as in this case where they are being used in a for loop, ['a', 'b', 'c'] and 'abc' are effectively equivalent. Another example would be when they are being passed to a function that takes an iterable, like tuple.

I personally wouldn't do this because it doesn't read intuitively: when I see a string, I expect it to be used as a single value rather than as a list. So I look at the first line and think "Okay, so there's a True flag and a False flag." So although it's a possibility, I don't think it's the way to go. On the upside, it may help to explain the concepts of iterables and iterators more clearly.


Defining the function invert_dict such that it actually returns a dictionary is not a bad idea either; I mostly just didn't do that because it doesn't really help to explain how the routine works.


Apparently Python 2.7 has dictionary comprehensions, which would make for an extremely concise way to implement that function. This is left as an exercise to the reader, since I don't have Python 2.7 installed :)

You can also combine some functions from the ever-versatile itertools module. As they say, There's More Than One Way To Do It. Wait, the Python people don't say that. Well, it's true anyway in some cases. I would guess that Guido hath given unto us dictionary comprehensions so that there would be One Obvious Way to do this.

查看更多
混吃等死
5楼-- · 2019-01-12 17:15

Sounds like you're approaching your problem the wrong way to me.

Rewrite your code to use a tuple or write a class to store all of the data.

查看更多
ゆ 、 Hurt°
6楼-- · 2019-01-12 17:20

This is an elaboration on @Jeff M's and my comments.

When you do this:

a, b = c, d

It works with tuple packing and unpacking. You can separate the packing and unpacking steps:

_ = c, d
a, b = _

The first line creates a tuple called _ which has two elements, the first with the value of c and the second with the value of d. The second line unpacks the _ tuple into the variables a and b. This breaks down your one huge line:

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

Into two smaller lines:

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

It will give you the exact same result as the first line (including the same exception if you add values or variables to one part but forget to update the other). However, in this specific case, yan's answer is perhaps the best.

If you have a list of values, you can still unpack them. You just have to convert it to a tuple first. For example, the following will assign a value between 0 and 9 to each of a through j, respectively:

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

EDIT: Neat trick to assign all of them as true except element 5 (variable f):

a, b, c, d, e, f, g, h, i, j = tuple(x != 5 for x in range(10))
查看更多
神经病院院长
7楼-- · 2019-01-12 17:21

I like the top voted answer; however, it has problems with list as shown.

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

This is discussed in great details (here), but the gist is that a and b are the same object with a is b returning True (same for id(a) == id(b)). Therefore if you change an index, you are changing the index of both a and b, since they are linked. To solve this you can do (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]

This can then be used as a variant of the top answer, which has the "desired" intuitive results

>> a, b, c, d, e, g, h, i = (True for i in range(9))
>> f = (False for i in range(1)) #to be pedantic
查看更多
登录 后发表回答