【问题标题】:Reduce number of lines by reusing code and passing member names to run tests on通过重用代码和传递成员名称来运行测试来减少行数
【发布时间】:2013-11-19 08:57:37
【问题描述】:

我有两个来自同一类的汽车对象和一个汽车列表。

class Car:
    def __init__(self):
        pass

cars = []

car1 = Car()
car1.color = 'black'
cars.append(car1)
car2 = Car()
car2.engine = 'V8'
cars.append(car2)

我还有两个函数,用于在匹配时从cars 中删除对象。

def rm_car_color(c):
    for car_n in cars:
        try:
            if car_n.color == c.color:
                cars.remove(car_n)
        except Exception, err:
            pass

def rm_car_engine(c):
    for car_n in cars:
        try:
            if car_n.engine == c.engine:
                cars.remove(car_n)
        except Exception, err:
            pass

如您所见,它们实际上是相同的。我想要的是能够将要检查的成员传递给函数,以便我可以删除这两个函数之一,并且只有一个在 Python 2.6.6 中同时处理这两个函数。这可能吗?

此代码仅用于说明。我正在处理的代码有许多这样的删除函数(总共 8 个),如果可以的话,我会显着减少行数。

【问题讨论】:

  • 另外,如果有人对这篇文章有更好的主题名称,请更改它。我很难找到一个好句子。
  • 警告:在您迭代列表时从列表中删除对象可能会跳过一些项目!您可能想要创建一个新列表,而不是修改现有列表。
  • @Blckknght 或者遍历一个反向列表。
  • @SteinarLima:嘿,我正要编辑我的评论以提及反向迭代。

标签: python algorithm


【解决方案1】:

从查看代码开始。正如您所说,这两个功能几乎相同。这两个功能的唯一区别是什么?属性(c.color vs c.engine):

if car_n.color == c.color:

if car_n.engine == c.engine:

因此,您可以使用内置的getattr 函数将此代码更改为:

def rm_car_attribute(c, attrib):
    comparison_attrib = getattr(c, attrib)
    for car_n in cars[:]:
    # Blender mentioned a good practice: not to modify the list you are iterating
    # cars[:] creates a temporary copy of cars to iterate over
        try: 
            if getattr(car_n, attrib) == comparison_attrib:
                cars.remove(car_n)
        except Exception, err:
            pass

可以这样使用:

rm_car_attribute(c, 'engine') # same as rm_car_engine(c) from above

另一方面,我不确定您期望的是什么类型的异常。我看到的唯一可能的错误是汽车的属性是否已明确定义(即car_instance.engine = 'V8'),但对于其中一辆车,属性未定义(在您的汽车列表中,其中一个实例没有@987654330 @ 属性)。您可以更改try/except 以专门捕获此错误:

def rm_car_attribute(c, attrib):
        comparison_attrib = getattr(c, attrib)
        for car_n in cars[:]:
            try: 
                if getattr(car_n, attrib) == comparison_attrib:
                    cars.remove(car_n)
            except AttributeError:
                pass

【讨论】:

    【解决方案2】:

    更好的选择是只使用过滤功能:

    def remove_cars(cars, predicate):
        for car in cars[:]:  # Don't iterate over and modify a list at the same time
            if predicate(car):
                cars.remove(car)
    

    或者:

    def remove_cars(cars, predicate):
        for car in filter(cars[:], predicate):
            cars.remove(car)
    

    然后调用它:

    remove_cars(cars, lambda c: c.color == car.color)
    

    或更详细地说:

    def f(c):
        return c.color == car.color
    
    remove_cars(cars, f)
    

    过滤功能的好处是您可以做的不仅仅是检查属性是否相等:

    remove_car(cars, lambda c: c.engine.startswith('V'))
    

    另外,只返回一个新列表而不是修改旧列表可能更容易:

    def remove_cars(cars, predicate):
        result = []
    
        for car in cars:
            if not predicate(car):
                result.append(car)
    
        return result
    

    但这基本上是内置的filter 函数。

    【讨论】:

      【解决方案3】:

      由于这两个函数之间只有一行不同,我建议你传递另一个函数来为你进行评估,然后如果它评估为True,则移除汽车。

      def rm_car_by_attr(c, func):
          # create a tmp list so you can iterate and remove at the same time
          tmp_cars_list = list(cars) 
          for car_n in tmp_cars_list:
              try:
                  if func(car_n, c):
                      cars.remove(car_n)
              except Exception, err:
                  pass
      
      def is_car_engine_the_same(car1, car2):
            return car1.engine == car2.engine
      
      def is_car_color_the_same(car1, car2):
            return car1.color == car2.color
      
      rm_car_by_attr(this_car, is_car_engine_the_same)
      rm_car_by_attr(this_car, is_car_color_the_same)
      

      【讨论】:

        【解决方案4】:

        考虑改变你的数据结构,也许使用dict 来存储键值对,然后你可以简单地传入你的比较类型。下面的代码未经测试,但应该可以帮助您了解这个想法

        car1.attributes['engine'] = 'V8'
        
        def rm_car(c, remWhat):
            for car_n in cars:
                try:
                    if car_n.attributes[remWhat] == c.attributes[remWhat]:
                        cars.remove(car_n)
                except Exception, err:
                    pass
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-06-10
          • 1970-01-01
          • 2019-03-01
          • 1970-01-01
          • 2022-10-15
          • 2022-11-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多