我目前正在使用标题中提到的书来学习Python。我刚刚读完有关列表,元组,函数和方法的内容,本章之后的项目之一是创建一个程序,该程序将列表作为输入并输出相应的字符串,如以下示例所示:

输入:['apples','bananas','tofu','cats']

输出:苹果,香蕉,豆腐和猫

此当然应该为任何列表工作。所以我想出了以下代码:

def listToString(someList):
    for i in range(len(someList)):
        newString = str(someList[i])

        if i == (len(someList)-1):
            print('and ' + str(someList[i]))
        else:
            print(newString, end=', ')

someList = ['apples','bananas','tofu','cats']
listToString(someList)


我觉得我并没有使用本章中讲授的所有内容(例如某些方法)解决问题。有没有更有效的方法来编码该项目?

评论

没有理由在Python中使用range(len(_))。只是遍历集合本身。如果还需要索引,请使用enumerate。

由于短名单的所有特殊情况,这实际上是一个棘手的练习。 (它似乎希望以标准的书面英语输出,但在这种情况下,“和”仅适用于2个或更多项目的列表,而牛津逗号应仅出现在3个或更多项目的列表中。介绍性练习,除非重点是如何处理复杂的规范。)

#1 楼

您可能已经使用了多种方法来进行列表切片。

我不知道您是否已经在书中学习过.join().format()方法,但我会使用。
<注释:


函数和变量应为snake_case而不是CamelCase


列表字符串应返回字符串而不是打印它,因为这就是名字的含义。


def list_to_string(a_list):
    return '{0} and {1}'.format(', '.join(a_list[:-1]), a_list[-1])

some_list = ['apples','bananas','tofu','cats']
print(list_to_string(some_list))


它是如何工作的? .format()替换为{0}', '.join(a_list[:-1])加入列表,直到最后一项[:-1]以及逗号和空格。将{1}替换为列表a_list[-1]中的最后一项。

评论


\ $ \ begingroup \ $
感谢您的提示,我真的需要习惯命名约定。我确实知道.format()函数! .join()我不知道。基本上,.join()函数从列表中获取元素并将其连接,直到给定索引? btw的format()函数非常聪明,这对我而言是没有发生的!谢谢你的时间!
\ $ \ endgroup \ $
–sar91
17-10-7在14:17



\ $ \ begingroup \ $
但是,这有三个缺点:1.仅适用于字符串列表2.对于长度为1的列表,例如[“ foo”],它返回“和foo”(OP的代码有相同的问题) 3.空列表将引发IndexError。
\ $ \ endgroup \ $
–地狱
17-10-7在15:24



\ $ \ begingroup \ $
值得一提的是,现代Python已格式化了字符串文字(即f'{','。join(a_list [:-1])}和{a_list [-1]}')
\ $ \ endgroup \ $
– gntskn
17-10-7在22:59



\ $ \ begingroup \ $
轻微的nitpick:{0}后需要一个逗号,因为该示例包括牛津逗号。
\ $ \ endgroup \ $
–user87373
17年10月8日在9:57

\ $ \ begingroup \ $
@gntskn您不是指f“ {','。join(a_list [:-1])}和{a_list [-1]}”吗?
\ $ \ endgroup \ $
– wizzwizz4
17-10-8在19:26

#2 楼

Ludisposed的答案已经有了很大的改进,但是它没有考虑一些特殊情况:


它仅适用于字符串列表。
对于长度为1的列表,例如["foo"] ,它返回" and foo"(您的代码有相同的问题)。
它在一个空列表上引发IndexError

此代码避免了以下问题:

def list_to_string(a_list):
    if not a_list:
        return ""
    elif len(a_list) == 1:
        return str(a_list[0])
    else:
        body = ", ".join(map(str, a_list[:-1]))
        return '{0} and {1}'.format(body, a_list[-1])


当前没有牛津逗号,如您的代码。但是您可以轻松地将其添加为另一种特殊情况(不要在len(a_list) == 2时添加,否则请添加)。感谢@DavidK在评论中指出了这一点。

评论


\ $ \ begingroup \ $
感谢您的深刻见解!您的第二点是绝对正确的,我没有只使用列表中的一个元素来测试我的代码。除了两件事,我了解了您的大多数代码:(i)第一个if语句,对于检查的内容我有些困惑? 'if not a_list'-是否检查a_list是否为空? (ii).join()函数中的“ map”语句。我现在将对其进行谷歌搜索,但是对此有任何见解将不胜感激!非常感谢您的宝贵时间。
\ $ \ endgroup \ $
–sar91
17-10-7在19:22



\ $ \ begingroup \ $
在python中,空列表被认为是Falsey。 map(str,list)将整个列表转换为字符串。
\ $ \ endgroup \ $
–处置不良
17-10-7在19:31

\ $ \ begingroup \ $
是的,如果检查以下陈述的真实性。一个空列表是虚假的,一个非空列表是虚假的,并且当然不会将其反转。 map接受一个函数(此处为str)并将其应用于可迭代对象的每个元素(如列表)。它返回Python中的地图对象,该对象可以再次迭代。在Python 2中,它只是返回了一个列表
\ $ \ endgroup \ $
–地狱
17-10-7在19:32

\ $ \ begingroup \ $
@ sar91大多数Python对象都是这种情况。空列表,字符串,元组,集合,字典...都是假的。通常,任何具有len(obj)== 0的对象都是假的(除非可以不同地指定它)。
\ $ \ endgroup \ $
–地狱
17-10-8在8:48

\ $ \ begingroup \ $
在问题和此处的所有答案(到目前为止)中,存在牛津逗号的用法问题,该逗号出现在3个或更多项目的列表中(缺少几个答案,包括此内容),但不在2个项目的列表中(但由OP和另一个答案插入)。我喜欢这个答案的原因是它很容易扩展,以区分2的列表和3的列表,并在每种情况下适当地处理逗号。
\ $ \ endgroup \ $
– David K
17-10-8在19:55



#3 楼

基本问题

考虑到您正在学习,我想评论一下我所看到的函数的基本问题,而不仅仅是编写代码的简洁性。

没有返回值

编程的很大一部分是使函数模块化,而我们通常通过将任务分解成可重复的部分来实现。像您一样使用打印语句将打印到控制台,甚至可能对您当前的问题有意义,但是当您想在其他地方使用它时会发生什么呢?如果要将奇特的字符串保存到磁盘上怎么办?

答案是像其他答案一样返回字符串。然后,有了字符串后,就可以执行所需的操作,例如将其打印或写入磁盘。

应该养成从函数中返回值的习惯。

名称误导

函数名称暗示它从列表中返回一个字符串。想象一下,您从现在开始四个月后返回代码,并尝试使用此函数写入文件,您将被证明编写这样的文件:

fancy_string = list_to_string(groceries)
with open('groceries.txt', 'w') as groceries_file:
    groceries_file.write(fancy_string)


但是,这将不起作用,因为这里没有返回值。

在编程中,命名是一件非常困难的事情,因此请仔细考虑您的名字。 br />
现在我想指出的是一些更小的事情,以使代码更具pythonic的含义。

for i in range(len(someList)):
    newString = str(someList[i])


可以替换为:

for i, newString in enumerate(someList):


现在也用newString替换someList [i]的实例:

def listToString(someList):
    for i, newString in enumerate(someList):
        if i == (len(someList)-1):
            print('and ' + str(newString))
        else:
            print(newString, end=', ')


即使在这种情况下,您应该在其他帖子的开头使用更多答案,但对于需要枚举的情况,最好了解枚举与列表的元素。我鼓励您使用Python for-loop构造,而不必尽可能索引所有内容。

PEP 8

正如其他人指出的那样,该代码不符合Python标准PEP8。
虽然我不知道严格遵循PEP 8的代码,但您应该看看它是什么样子。

评论


\ $ \ begingroup \ $
enumerate()函数非常酷。我一定会记住这一点。还要感谢您提供所有其他很棒的技巧,非常感谢!关于使函数模块化的非常好一点-使返回值的必要性非常明显!
\ $ \ endgroup \ $
–sar91
17年10月8日在7:12

#4 楼

其他一些答案给出了很好的指导,但是代码示例引入了一些我不建议使用的可读性问题。

将此示例与其他示例进行比较:

def list_to_string(list_object):
    # corner case 1: not a list object
    if not isinstance(list_object, list):
        raise ValueError('function parameter is not a list')

    # corner case 2: list object has 0 element
    if not list_object:
        raise ValueError('list is empty aka evaluated as false')

    # corner case 3: list object has 1 element
    if len(list_object)==1:
        return str(list_object[0])

    # actual use case
    else:
        # convert list elements to string
        str_list = [str(x) for x in list_object]
        bulk, last = str_list[:-1], str_list[-1]
        bulk_str = ', '.join(bulk)
        new_string = bulk_str + ' and ' + last
        return new_string


评论


\ $ \ begingroup \ $
我可能会补充说f字符串或.format()比字符串串联更干净。其次,对于@Grapiher处理的int列表,此操作失败
\ $ \ endgroup \ $
–处置不良
17-10-9在15:22

\ $ \ begingroup \ $
另外,您也可以一次性返回else中的字符串,而不是先将其分配给变量。像return f'{body}和{last}'
\ $ \ endgroup \ $
–处置不良
17-10-9在15:27

\ $ \ begingroup \ $
添加了用于处理更多极端情况的代码。这不是最简洁的代码,但我相信应该强调清晰度。
\ $ \ endgroup \ $
– BCR
17-10-9在15:48

#5 楼

您可以简单地将这个问题分为两部分,其中第一部分将在开始和开始之前打印列表项,然后打印列表的最后两项(包括列表)。

spam = ['apples', 'bananas', 'tofu', 'cats', 'mewoeto']
def andInsertedList(spamList):
    for i in range(len(spamList)-1):
        print(spamList[i], end=', ')  #First Part
    print('and ' + spamList[len(spamList)-1])  #Second Part  

andInsertedList(spam)


评论


\ $ \ begingroup \ $
最后一行:“ spamList [len(spamList)-1]”不需要计算最后一项“ len(spamList)-1”的索引值即可检索它。您可以简单地使用“ spamList [-1]”
\ $ \ endgroup \ $
– DataWriter
19年5月17日在18:16