什么是编写python3 zip的另一种方式

时间:2012-11-14 00:30:37

标签: python-3.x

我一直在研究一个读取文件文档中的行然后代码组织它们的代码。但是,我一度陷入困境,我的朋友告诉我我能用什么。代码有效,但似乎我不知道他在第7和第8行从底部做了什么。我使用####所以你们知道它是哪条线。

那么,基本上你怎么能重写那两行代码呢?为什么它们有效呢?我好像不懂字典     来自sys import argv

filename = input("Please enter the name of a file: ")
file_in=(open(filename, "r"))

print("Number of times each animal visited each station:")
print("Animal Id             Station 1              Station 2")

animaldictionary = dict()

for line in file_in:
    if '\n' == line[-1]:
        line = line[:-1]
    (a, b, c) = line.split(':')
    ac = (a,c)
    if ac not in animaldictionary:
        animaldictionary[ac] = 0
    animaldictionary[ac] += 1

alla = []
for key, value in animaldictionary:
    if key not in alla:
        alla.append(key)
print ("alla:",alla)
allc = []
for key, value in animaldictionary:
    if value not in allc:
        allc.append(value)    
print("allc", allc)

for a in sorted(alla):
    print('%9s'%a,end=' '*13)
    for c in sorted(allc):
        ac = (a,c)
        valc = 0
        if ac in animaldictionary:
            valc = animaldictionary[ac]
        print('%4d'%valc,end=' '*19)

    print()

print("="*60)
print("Animals that visited both stations at least 3 times: ")

for a in sorted(alla):
    x = 'false'
    for c in sorted(allc):
        ac = (a,c)
        count = 0
        if ac in animaldictionary:
            count = animaldictionary[ac]
            if count >= 3:
                x = 'true'
    if x is 'true':    
        print('%6s'%a, end=' ')
        print("")

print("="*60)
print("Average of the number visits in each month for each station:")

#(alla, allc) = 
#for s in zip(*animaldictionary.keys()):
#    (alla,allc).append(s)
#print(alla, allc)

(alla,allc,) = (set(s) for s in zip(*animaldictionary.keys())) ##### how else can you write this
##### how else can you rewrite the next code
print('\n'.join(['\t'.join((c,str(sum(animaldictionary.get(ac,0) for a in alla for ac in ((a,c,),))//12)))for c in sorted(allc)]))

print("="*60)
print("Month with the maximum number of visits for each station:")
print("Station             Month               Number")

print("1")
print("2")

1 个答案:

答案 0 :(得分:3)

你指出的两条线确实令人困惑。我会尽力解释它们,并建议其他实施方案。

第一个计算allaallc的值:

(alla,allc,) = (set(s) for s in zip(*animaldictionary.keys()))

这几乎等同于您上面已经完成的用于构建allaallc列表的循环。如果需要,您可以完全跳过它。但是,让我们解开它正在做的事情,这样你就可以真正理解它。

最里面的部分是animaldictionary.keys()。这将返回一个包含字典中所有键的可迭代对象。由于animaldictionary中的键是两值元组,因此您可以从迭代中获得。在大多数情况下,在处理字典时实际上没有必要调用keys,因为对键视图的操作通常与在字典上直接执行相同操作相同。

继续前进,通过使用zip调用zip(*keys)函数来解锁密钥。这里发生了两件事。首先,*语法将上面的iterable解包为单独的参数。因此,如果animaldictionary的密钥为("a1", "c1), ("a2", "c2"), ("a3", "c3"),则会将这三个元组称为zip作为单独的参数。现在,zip所做的是将几个可迭代的参数转换为单个迭代,产生一个元组,每个元组的第一个值,然后是一个元组,每个元组的第二个值,依此类推。因此,zip(("a1", "c1"), ("a2", "c2"), ("a3", "c3"))会返回一个生成("a1", "a2", "a3"),后跟("c1", "c2", "c3")

的生成器

下一部分是一个生成器表达式,它将zip表达式中的每个值传递给set构造函数。这有助于消除任何重复。 set个实例在其他方面也很有用(例如找到交叉点),但这里不需要。

最后,将两组ac值分配给变量allaallc。他们用你的名字替换你已经拥有的名单(和相同的内容!)。

您已经有了替代方案,您可以将allaallc计算为列表。使用集可能稍微有点效率,但对于少量数据可能并不重要。另一种更明确的方法是:

alla = set()
allc = set()
for key in animaldict:  # note, iterating over a dict yields the keys!
    a, c = key  # unpack the tuple key
    alla.add(a)
    allc.add(c)

你问的第二行做了一些平均,并将结果组合成一个打印出来的巨大字符串。把这么多的东西塞进一行是非常糟糕的编程风格。事实上,它做了一些不必要的东西,这让它更加令人困惑。在这里,添加了几个换行符,使其全部适合屏幕。

print('\n'.join(['\t'.join((c,str(sum(animaldictionary.get(ac,0)
                                      for a in alla for ac in ((a,c,),))//12)
                           )) for c in sorted(allc)]))

最里面的部分是for ac in ((a,c,),)。这很愚蠢,因为它是一个单元组元组的循环。这是将元组(a,c)重命名为ac的一种方式,但它非常混乱且不必要。

如果我们将ac的一次使用替换为明确写出的元组,则新的最内部部分为animaldictionary.get((a,c),0)。这是一种编写animaldictionary[(a, c)]的特殊方式,但如果KeyError不在字典中,则不会产生导致(a, c)的风险。相反,对于不存在的键,将返回默认值0(传入get)。

get来电已完成:(getcall for a in alla)。这是一个生成器表达式,它从键中获取给定c值的字典中的所有值 (如果该值不存在,则默认值为零。)

下一步是获取前一个生成器表达式中值的平均值:sum(genexp)//12。这非常简单,但您应该注意,使用//进行除法总是向下舍入到下一个整数。如果您想要更精确的浮点值,请仅使用/

下一部分是对'\t'.join的调用,其参数是单个(c, avg)元组。这是一个尴尬的结构,可以更清楚地写成c+"\t"+str(avg)"{}\t{}".format(c, avg)。所有这些都会产生一个字符串,其中包含c值,制表符和上面计算的平均字符串形式。

下一步是列表推导,[joinedstr for c in sorted(allc)](其中,joinstr是上一步中的join调用)。在这里使用列表理解有点奇怪,因为不需要列表(生成器表达式也可以这样做)。

最后,列表理解与换行符结合并打印:print("\n".join(listcomp))。这很简单。

无论如何,通过使用一些变量并在循环中分别打印每一行,可以更清晰地重写整个混乱:

for c in sorted(allc):
    total_values = sum(animaldictionary.get((a,c),0) for a in alla)
    average = total_values // 12

    print("{}\t{}".format(c, average))

要完成,我有一些一般的建议。

首先,您的数据结构可能不适合您对数据的使用。不是让animaldict成为具有(a,c)个键的字典,而是拥有嵌套结构可能更有意义,您可以在其中单独索引每个级别。也就是animaldict[a][c]。使第二个字典包含以相反顺序索引的相同值甚至是有意义的(例如,一个被索引[a][c]而另一个被索引[c][a])。使用这种方法,您可能不需要allaallc列表进行迭代(您只需直接遍历主词典的内容)。

我的第二个建议是代码风格。您的许多变量名称都很差,要么是因为他们的名字没有任何意义(例如c),要么名称暗示意义不正确。最明显的问题是您的keyvalue变量,实际上解包了两个密钥(AKA ac)。在其他情况下,您可以将键和值组合在一起,但只有当您在字典的items()视图上而不是直接在字典上进行迭代时才会获得。