如果另一个类属性与列表中的另一个属性匹配,则添加类属性

时间:2017-04-17 21:21:54

标签: python list class list-comprehension

我有一个班级:

// https://github.com/KubaO/stackoverflown/tree/master/questions/scene-polygon-7727656
#include <QtWidgets>

class MainWindow : public QWidget
{
   Q_OBJECT
   QGridLayout m_layout{this};
   QPushButton m_new{"New"};
   QPushButton m_erase{"Erase All"};
   QLabel m_label;
   QGraphicsView m_view;
public:
   MainWindow() {
      m_layout.addWidget(&m_new, 0, 0);
      m_layout.addWidget(&m_erase, 0, 1);
      m_layout.addWidget(&m_label, 0, 2);
      m_layout.addWidget(&m_view, 1, 0, 1, 3);
      m_view.setBackgroundBrush(Qt::black);
      m_view.setAlignment(Qt::AlignBottom | Qt::AlignLeft);
      m_view.scale(1, -1);
      connect(&m_new, &QPushButton::clicked, this, &MainWindow::newItem);
      connect(&m_erase, &QPushButton::clicked, this, &MainWindow::clearScene);
   }
   void setScene(QGraphicsScene * scene) {
      m_view.setScene(scene);
   }
   Q_SIGNAL void newItem();
   Q_SIGNAL void clearScene();
   Q_SLOT void setText(const QString & text) { m_label.setText(text); }
};

class MyScene : public QGraphicsScene {
   Q_OBJECT
public:
   struct Status {
      int paths;
      int elements;
   };
private:
   bool m_newItem = {};
   Status m_status = {0, 0};
   QPainterPath m_path;
   QGraphicsPathItem m_pathItem;
   QGraphicsLineItem m_lineItem;
   struct PathUpdater {
      Q_DISABLE_COPY(PathUpdater)
      MyScene & s;
      PathUpdater(MyScene & scene) : s(scene) {
         s.m_pathItem.setPath({}); // avoid a copy-on-write
      }
      ~PathUpdater() {
         s.m_pathItem.setPath(s.m_path);
         s.m_status = {0, s.m_path.elementCount()};
         for (auto i = 0; i < s.m_status.elements; ++i) {
            auto element = s.m_path.elementAt(i);
            if (element.type == QPainterPath::MoveToElement)
               s.m_status.paths++;
         }
         emit s.statusChanged(s.m_status);
      }
   };
   void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
      PathUpdater updater(*this);
      auto pos = event->scenePos();
      m_lineItem.setLine(0, 0, pos.x(), pos.y());
      m_lineItem.setVisible(true);
      if (m_path.elementCount() == 0 || m_newItem)
         m_path.moveTo(pos);
      m_path.lineTo(pos.x()+1,pos.y()+1); // otherwise lineTo is a NOP
      m_newItem = {};
   }
   void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
      PathUpdater updater(*this);
      auto pos = event->scenePos();
      m_lineItem.setLine(0, 0, pos.x(), pos.y());
      m_path.setElementPositionAt(m_path.elementCount()-1, pos.x(), pos.y());
   }
   void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override {
      m_lineItem.setVisible(false);
   }
public:
   MyScene() {
      addItem(&m_pathItem);
      addItem(&m_lineItem);
      m_pathItem.setPen({Qt::red});
      m_pathItem.setBrush(Qt::NoBrush);
      m_lineItem.setPen({Qt::white});
      m_lineItem.setVisible(false);
   }
   Q_SLOT void clear() {
      PathUpdater updater(*this);
      m_path = {};
   }
   Q_SLOT void newItem() {
      m_newItem = true;
   }
   Q_SIGNAL void statusChanged(const MyScene::Status &);
   Status status() const { return m_status; }
};

int main(int argc, char *argv[])
{
   using Q = QObject;
   QApplication app{argc, argv};
   MainWindow w;
   MyScene scene;
   w.setMinimumSize(600, 600);
   w.setScene(&scene);
   Q::connect(&w, &MainWindow::clearScene, &scene, &MyScene::clear);
   Q::connect(&w, &MainWindow::newItem, &scene, &MyScene::newItem);
   auto onStatus = [&](const MyScene::Status & s){
      w.setText(QStringLiteral("Paths: %1 Elements: %2").arg(s.paths).arg(s.elements));
   };
   Q::connect(&scene, &MyScene::statusChanged, onStatus);
   onStatus(scene.status());
   w.show();
   return app.exec();
}
#include "main.moc"

我还有一个包含此类的多个实例的列表,例如:

class ex(object):

    def __init__(self, tag, quantity):
        self.tag = tag
        self.quantity = quantity

如果self.tag与列表中的另一个实例匹配,我试图想出一个添加self.quantiy属性的好方法。

输出将是:

[ex('a', 10.5), ex('b', 5.0),  ex('c', 2.0), ex('a', 5.0), ex('c', -10.0)]

感谢

编辑:最终实例可以是新实例或累积的第一个列表。列表的最大大小约为len()1000

3 个答案:

答案 0 :(得分:2)

O(n^2),但代码更简单。

class ex(object):
    def __init__(self, tag, quantity):
        self.tag = tag
        self.quantity = quantity

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.tag, self.quantity)

exes = [ex('a', 10.5), ex('b', 5.0), ex('c', 2.0), ex('a', 5.0), ex('c', -10.0)]
summed_exes = [ex(tag, sum(e.quantity for e in exes if e.tag == tag))
               for tag in sorted({e.tag for e in exes})]
print(summed_exes)

sorted是可选的。

输出:

[ex('a', 15.5), ex('b', 5.0), ex('c', -8.0)]

答案 1 :(得分:1)

我认为一个普通的循环实际上比这里的列表理解更好。通过跟踪已经看到的标签,可以实现O(n)的复杂性:

seen = {}

for instance in in_list:
    try:
        existing = seen[instance.tag]
    except KeyError:
        seen[instance.tag] = instance
    else:
        existing.quantity += instance.quantity

out_list = list(seen.values())  # can drop the "list" call on Python 2.x

将使用每个组中的第一个实例,如果再次看到该标记,则会在之后更新。

如果您希望保留代码的原始排序,请为collections.OrderedDict变量而不是普通字典输入seen。将会有轻微的性能损失,但解决方案仍然是O(n)复杂性。

答案 2 :(得分:1)

这是一个创建新对象的O(n)解决方案:

In [9]: class Ex(object):
   ...:     def __init__(self, tag, quantity):
   ...:         self.tag = tag
   ...:         self.quantity = quantity
   ...:
   ...:     def __repr__(self):
   ...:         return '%s(%r, %r)' % (self.__class__.__name__, self.tag, self.quantity)
   ...:

In [10]: objs = [Ex('a', 10.5), Ex('b', 5.0),  Ex('c', 2.0), Ex('a', 5.0), Ex('c', -10.0)]

In [11]: objs
Out[11]: [Ex('a', 10.5), Ex('b', 5.0), Ex('c', 2.0), Ex('a', 5.0), Ex('c', -10.0)]

In [12]: grouped = {}

In [13]: for obj in objs:
    ...:     grouped[obj.tag] = grouped.get(obj.tag, 0) + obj.quantity
    ...:

In [14]: new_objs = [Ex(k, v) for k,v in grouped.items()]

In [15]: new_objs
Out[15]: [Ex('b', 5.0), Ex('a', 15.5), Ex('c', -8.0)]

如果订单有问题,您可以使用defaultdictCounterOrderedDict来获得更高的效果。