在Python中动态创建类实例

时间:2013-12-02 02:43:59

标签: python csv

如何动态生成类实例?具体来说,我正在做的事情:

我从电子表格中提取数据,该数据表会转换为CSV格式。该行是标题。在标题行之后,每一行代表有关特定订单的数据(col1是订单ID,col2是客户名称,col3是日期,col4是数量等)。现在,我正在将CSV导入到词典列表中。因此,列表中的每个项目都存储一个字典,该字典从标题行中提取键。所以,我可以通过以下方式查找订单#5的数量:

orderDict[5]['quantity']

我是面向对象编程的新手......但是想让这些命令中的每一个都成为Order类的一个实例。所以,我想创建一个名为“Order”的类,然后让它从CSV的标题行中提取属性。所以,类似于(inputList是从CSV文件中提取的列表):

class Order(object):
        """Defines an individual order"""
        def __init__(self, inputList):
            for z in range(len(inputList[0]))
                self.inputList[0][z] = None

然后,我想要一些代码遍历从CSV导入的数据,并为每一行创建一个实例。

for a in range(len(inputList))
    if a != 0:
        orderName = 'order%d' % (a)
        orderName = Order() #I know this won't work... but not sure how I variably name this
        for b in range(len(inputList[a]))
            orderName.b = inputList[a][b]

结果将是一个名为order1,order2,order3,order4等的实例。创建的实例数取决于原始数据中的行数。可能是4 ......可能是数千人。

这样,如果我想找到订单5的数量,我可以致电:

order5.quantity

但是,到目前为止,我似乎需要手动显式创建每个实例:

order1 = Order()
order2 = Order()
order3 = Order()

处理数以千计的订单(并且不断增长)时,不是很方便或动态。似乎应该有一种方法可以根据输入程序的数据动态生成这些实例。

4 个答案:

答案 0 :(得分:2)

如果你真的希望实例具有全局变量名(例如order1,order2等),则可能(但非常难看)使用例如globals()['order' + num] = Order(...)。更清晰,更安全的方法是将实例存储在单个字典中。同样为了提高效率,您可以弹出标题以避免每次迭代测试为零,可以使用xrange而不是范围来避免啜饮完整的数据集,可以选择在对象的init中设置属性而不是之后(看到信息已经可用...),并且只能将标题和行传递给每个实例而不是所有数据。顺便说一下,你的for-lines两端缺少冒号:

class Order(object):
    """Defines an individual order"""
    def __init__(self, input_header, input_line):
        for z in xrange(len(input_header)):
            setattr(self, input_header[z], input_line[z])

orders = {}
input_header = input_list.pop(0)
for a in xrange(len(input_list)):
    orders[a] = Order(input_header, input_list[a])

答案 1 :(得分:1)

对此的一般解决方案是使用字典将字符串映射到类

classes = {"order":Order,"something":Something}

keys = ["order","order","something"]
my_instances = [classes[key]() for key in keys]

答案 2 :(得分:0)

您可以在实例化之后动态地将适当的属性添加到订单类的实例中,如下所示:

class Order(object):
    def __init__(self,pairs):
        for k,v in pairs:
            setattr(self,k,v)
    def __str__(self):
        return "oID: %s, custName: %s, Date: %s, Qty %s" %(self.orderID,self.custName,self.Date,self.Qty)

这将包含一个包含(key,value)元组的iterable。然后,它会根据keyvalue添加属性。

使用此类,我们可以从csv文件创建订单,如下所示:

orderID,custName,Date,Qty
1,Bob,2/3/2013,2
2,jane,2/4/2013,1

使用csv模块中的DictReader类:

from csv import DictReader

with open('orders.csv') as f:
    orders = []
    reader = DictReader(f)
    for row in reader:
        orders.append(Order(row.items()))

这将导致:

for order in orders:
    print(order)
>>> 
oID: 1, custName: Bob, Date: 2/3/2013, Qty 2
oID: 2, custName: jane, Date: 2/4/2013, Qty 1

答案 3 :(得分:0)

使用动态属性和动态变量名生成类可能不是一个好主意。您的程序如何知道它们是什么才能访问它们?例如:

 order5.quantity

假设订单至少有5个(或6个)且订单具有quantity属性。

从CSV文件头字段名称生成类属性名称是有问题的,因为它们可能不是有效的Python标识符。

虽然这并没有解决最后一个问题,但您应该考虑以下内容:

给定一个示例文件orders.csv,如下所示:

order_id,customer,date,product,quantity
01,Brian,2013-12-01,Spam,2
13,Eric,2013-11-28,Eggs,3
42,John,2013-11-30,Cheese,4

您可以使用以下内容:

from collections import namedtuple
import csv

class Orders(object):
    def __init__(self, csv_filename):
        with open(csv_filename, 'rb') as inputfile:
            csv_reader = csv.reader(inputfile)
            self.fields = csv_reader.next()  # read header row
            self.Order = namedtuple('Order', self.fields)
            self.records = [self.Order(*row) for row in csv_reader]

    def __getitem__(self, index):
        return self.records[index]

    def __len__(self):
        return len(self.records)

这将允许您想要的大部分用法:

orders = Orders('orders.csv')

if len(orders) >= 3:
    print orders[2].quantity  # assumes name of a field

for id, order in enumerate(orders):
    print 'order id {}: {}'.format(id, order)

输出:

 4
 order id 0: Order(order_id='01', customer='Brian', date='2013-12-01', product='Spam', quantity='2')
 order id 1: Order(order_id='13', customer='Eric', date='2013-11-28', product='Eggs', quantity='3')
 order id 2: Order(order_id='42', customer='John', date='2013-11-30', product='Cheese', quantity='4')