改变对象列表的优雅和Pythonic方法

时间:2014-11-20 20:55:12

标签: python performance

我正在寻找一种高效的,pythonic方式来搜索和更改对象列表。

问题很简单:咖啡对象有三个属性:名称(" Frapublabla"),颜色("深棕色")和味道("苦涩&#34) )。每种咖啡都有一个独特的名称,可能有多种颜色和口味。 Barista()提供有关新咖啡的信息。一旦Barista()完成提供n个咖啡,代码就会输出所有咖啡。

显而易见的迭代方法是:

Define <coffee> - a class with three properties: name, color, taste.
Define coffeeList = [] , tempNameList = []

For n times:
     Get coffee name, color, taste from the user. Each time:

    Search tempNameList for the name of the coffee.
    If the coffee name is not there, then:
        Create a new coffee object, add to coffeeList.  The object includes name, color, and taste.
        Add coffee name to tempNameList.

    If the coffee name is found on tempNameList:
        Iterate through the coffeeList to find the right coffee object. Then:
            Add to that object another name or color.

这种方法不能很好地扩展。首先,每次添加咖啡时,我们都需要搜索整个coffeeList,获取每个对象的属性。

有更多的pythonic和优雅的方法来解决这个问题吗? collections.defaultdict()是我得到的最接近的,但似乎并不适用于这个问题。

2 个答案:

答案 0 :(得分:0)

Defaultdicts肯定是这种方式的一种方式,但是,如果你试图采用面向对象的方法来解决问题,我个人的偏好是避免嵌套字典。这是一种潜在的pythonic解决方案。

import collections

class Coffee():
    def __init__(self, name='', color='', taste=''):
        self.name = name
        self.color = color
        self.taste = taste

    def __repr__(self):
        return '{0} {1} {2}'.format(self.name, self.color, self.taste)

prompt = "Enter name,color,taste or ctrl-d when done: "
coffees = collections.defaultdict(Coffee)

while True:
    try:
        name, color, taste = input(prompt).split(',')
        coffees[name].name = name
        coffees[name].color = color
        coffees[name].taste = taste
    except ValueError:
        print("Try again!")
    except EOFError:
        print()
        break


print(coffees)

答案 1 :(得分:0)

我已尝试过三种不同的方法,如@Cyber​​,@ jme和@mdadm所述:(1)面向对象使用collections.defaultdict和(2)常规字典,和(3) )嵌套字典。时间结果几乎相同。我无法回答内存使用情况和其他CPU测量结果。由于可读性和OO性质,我和#1一起去。

以下是结果,以秒为单位。每个&#34;尝试&#34;迭代代码1M次。有20个随机生成的咖啡名称,每个咖啡有10种口味和10种颜色:

Try| Approach 1 | App 2 | App 3 |
+--+------------+-------|-------|
| 1|    41.1    | 42.1  | 42.1  |
| 2|    39.1    | 41.2  | 41.6  |
| 3|    38.6    | 41.1  | 40.1  |
| 4|    40.9    | 42.5  | 41.3  |
| 5|    39.5    | 40.1  | 39.7  |

代码如下。

方法1:

import collections, random, time

class Coffee():
    def __init__(self):
        self.name = name
        self.color = set([])
        self.taste = set([])

    def __repr__(self):
        return '{0} {1} {2}'.format(self.name, self.color, self.taste)

startTime = time.clock()

coffees = collections.defaultdict(Coffee)

for counter in range(1000000):

    name = "Name_" + str(random.randint(1, 20))
    taste = "taste_" + str(random.randint(1, 10))
    color = "color_" + str(random.randint(1, 10))

    coffees[name].name = name
    coffees[name].taste.add(taste)
    coffees[name].color.add(color)

print (time.clock() - startTime)

方法2:

import random, time

class Coffee():
    def __init__(self, name, color, taste):
        self.name = name
        self.color = set([])
        self.taste = set([])

    def __repr__(self):
        return '{0} {1} {2}'.format(self.name, self.color, self.taste)

startTime = time.clock()

coffeeList = {}

for counter in range(1000000):

    name = "Name_" + str(random.randint(1, 20))
    color = "color_" + str(random.randint(1, 10))
    taste = "taste_" + str(random.randint(1, 10))

    if name not in coffeeList:
        coffeeObj = Coffee(name, color, taste)
        coffeeList[name] = coffeeObj
    else:
        coffeeList[name].color.add(color)
        coffeeList[name].taste.add(taste)

print (time.clock() - startTime)

方法3:

import random, time

coffeeNestedDict = {}

startTime = time.clock()

for counter in range(1000000):

    name = "Name_" + str(random.randint(1, 20))
    color = "color_" + str(random.randint(1, 10))
    taste = "taste_" + str(random.randint(1, 10))

    if name not in coffeeNestedDict:
        coffeeNestedDict[name] = {"Color" : set([color]), "Taste" : set([taste])}

    else:
        coffeeNestedDict[name]["Color"].add(color)
        coffeeNestedDict[name]["Taste"].add(taste)

print (time.clock() - startTime)