这可能是一个非常直截了当的问题,但希望能简单解释一下为什么?
下面的代码需要一个列表才能获得一张随机卡片。
import random
card = random.choice (["hearts", "clubs", "frogs"])
我很困惑为什么它需要一个列表以及为什么我不能这样做。
import = random
card = random.choice("hearts" , "clubs", "frogs")
我很好,我不能这样做我只是想知道为什么?
因为墨菲定律:任何可以以错误方式完成的事情,总有一天会被某人以错误的方式完成。您建议的 API 需要
random.choice(*lst)
当要选择的值在列表(或其他序列)中时lst
。当有人写
random.choice(lst)
相反,他们总是会lst
返回而不是异常。“显式优于隐式”的 Python 原则要求我们必须输入一些额外的字符。
(诚然,random.choice("foobar")
其他人指出的结果可能会让初学者感到惊讶,但是一旦您习惯了该语言,您就会欣赏它的工作方式。)
问题是您random.choice
使用 3 个参数调用,而不是使用 3 个元素的单个参数。例如尝试random.choice(('one', 'two', 'three'))
。
任何具有长度和合适__getitem__
(用于索引)的序列都可以 - 因为它选择 0 和之间的数字len(something)
来选择元素。
因此,如果您愿意,可以使用元组。
因为,第一个片段
["hearts","clubs","frogs"]
只向函数发送一个参数(列表)
而第二个向函数发送三个字符串。该函数choice
被配备为只接受一个参数。因此,您必须将其作为列表或任何可以索引的内容发送,以便它选择一个随机索引来返回值
random.choice
适用于任何支持索引的序列。
>>> random.choice("foobar") #string
'o'
>>> random.choice(("foo","bar","spam")) #tuple
'spam'
>>> random.choice(["foo","bar","spam"]) #list
'spam'
不适用于套装:
>>> random.choice({"foo","bar","spam"})
Traceback (most recent call last): File "<ipython-input-313-e97c3088a7ef>", line 1, in <module> random.choice({"foo","bar","spam"}) File "/usr/lib/python2.7/random.py", line 274, in choice return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty TypeError: 'set' object does not support indexing
在random.choice("hearts" , "clubs", "frogs")
您实际上将三个参数传递给choice
,而random.choice
只需要一个参数并且它也必须支持索引。
但是random.choice
如果 dict 具有数字键(介于 0 到 len(dict)-1 之间),则可以为 dict 工作,因为它在内部执行如下操作:
dic[int(random() * len(seq))]
例子:
>>> dic = dict(zip([1, 2, 3, 4, 5, 6], "abcdef"))
>>> random.choice(dic)
'b'
>>> random.choice(dic)
'd'
>>> random.choice(dic)
'd'
>>> random.choice(dic) #fails as 0 was not found in dic
Traceback (most recent call last):
File "<ipython-input-366-5cfa0e5f2911>", line 1, in <module>
random.choice(dic)
File "/usr/lib/python2.7/random.py", line 274, in choice
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
KeyError: 0
random.choice
关于为什么按原样实施以及为什么它实际上是您可能想要的,上面有几个很好的答案。
如果您希望能够使用任意数量的参数调用选择,您可以很容易地自己包装它:
import random
def random_choice_of_arbitrary_args(*args):
return random.choice(args)
当然,您可能会将其命名为更简洁的名称。
这确实具有以下令人惊讶的行为:
>>> random_choice_of_arbitrary_args([1, 2, 3])
[1, 2, 3]
这是因为您最终是在告诉 random.choice 给您一个带有一个元素的序列的随机元素。所以更好的实现可能是:
import random
def my_choice(*args):
if len(args) == 1:
return random.choice(args[0])
else:
return random.choice(args)
简而言之,因为这不是random.choice
函数的定义方式。我的猜测是,做出这个决定只是因为接受一个可迭代的参数比仅仅接受可变数量的任意参数更干净。
因此定义这样的函数:
# This is (approximately) how it's actually written
def choice(iterable):
#choose from iterable
比这更干净:
# This is kind of ugly
def choice(*args):
#make sure *args exists, choose from among them.
它的实际编写方式允许您将任何类型的单个可迭代项传递给它,既干净又方便。如果用第二种方式定义,使用时很容易搞砸:有人可以打电话random.choice([1, 2, 3])
,他们总是会回来[1, 2, 3]
!