如何将对象存储在动态生成的变量中?

时间:2019-02-09 12:20:51

标签: python python-3.x class oop object

我有一个带有一系列客户端的文本文件。 每行都有一个不同的客户端。 每个客户端都有一个ID,一个用户名和一个密码。

我想创建一个“客户端”类,并在一个循环中在该类中生成对象。 每个对象都有一个用户名和一个密码,并将被存储在包含客户端ID的变量中。 客户端1将存储在“ client_1”中,客户端2将存储在“ client_2”中,等等。

我创建了“ read()”方法,该方法打开文本文件,如果有空行则中断,并为每个客户端(每行)检索ID,用户名和密码。

我不知道要怎么做,以便当客户端的ID为“ 1”时,我为该客户端创建一个对象并将其存储在变量“ client_1”中。 当客户端的ID为“ 2”时,我将客户端的2对象存储在变量“ client_2”中,依此类推。

但是我想自动执行此操作,而不是拥有9000个客户端并自己创建9000个变量。

谢谢

class Client:

    def __init__(self, username, password):
        self.username = username
        self.password = password

    def read(self):
        clients = []
        with open("Clients.txt", "r") as file:
            lines = file.readlines()
            for line in lines:
                if not line:
                    break
                else:
                    client = line.split(" | ")
                    client_id = client[0]
                    #How do I create the variable "client_client[0]"?
                    username = client[1]
                    pre_password = client[2]
                    password = pre_password.strip("\n")
                    #client_client[0] = Client(username, password)
                    clients.append(#client_client[0])
            return clients

我的文本文件(ID,用户名,密码-从左到右):

1 | admin | Z9?zzz
2 | John | J1!jjj
3 | Steve | S1!sss

此外,如果我已经在def init 中使用它们,那么在read()中使用“用户名”和“密码”变量时是否会有问题?

谢谢

3 个答案:

答案 0 :(得分:2)

建议

  • 在循环中,您正在使用break。不要那样做,您要使用的是continue,它会跳过而不是让您退出迭代。

  • 您仅在密码上使用strip('\n')。您应该对所有物品都这样做(以确保它们都统一)。但是,仅在密码情况下才使用strip('\n')是正确的,因为它是唯一具有\n的密码。请勿在{{1​​}}中添加参数,它会处理所有空格,制表符以及其他strip()\n等。

  • 您应该将类​​的\r参数看作是驻留在其中的self。这是“环境” ,您基本上可以在类中的任何地方进行访问。而且,如果您在box中创建某些内容,例如self,则它将与名为self.client的单个变量不同。您可能想在这里做的就是将刚刚阅读的客户列表分配给client,例如self

关于您的程序

您要做的不是创建与用户一样多的变量。但是您的哲学正确,您想将它们存储在一个地方。那就是你所做的。现在,您的程序的要点对我们还是很模糊。但是您可能想要做的是:

  1. 拥有一个数据库,在其中知道如何订购商品。您知道,在users_list的每个元素中,都有第一项是self.client_list = self.read(),第二项是id,第三项是name

  2. 根据此数据库进行操作。

    • 您要“加载”一个客户端,检查该客户端是否存在于您的数据库中,并将其输入的密码与您链接的密码相匹配!
    • 您要删除一个?
    • 为已经登录的人订购冰淇淋吗?

答案 1 :(得分:1)

您已经拥有的客户列表有什么问题?您可以使用clients[0]clients[1]等访问客户端。该列表是任意多个变量的抽象。列表的索引为0,因此第一个元素的索引为0。这可能会造成混淆,尤其是因为某些语言(例如R,FORTRAN或Wolfram语言)是1索引的。我不认为这是一个根本性问题,您只需要弄清楚这一点即可。如果确实困扰您,则可以使用带有数字索引的dict,然后将所需的任何索引映射到客户。

我也将read_clients设为自由函数。它仅使用Client类的公共API来运行。因此,它不应该是成员函数。但是,如果您想在该类中使用它,至少应将它设为@staticmethod,因为它并不与某个特定的Client绑定。

公共API的含义:您编写的每个类都有公共方法和私有方法。在Python中,没有访问说明符,但是约定是,下划线(_)开头的方法不能在外部使用。带有两个下划线的字符(如__init__)也不能直接调用,而是由语法糖调用。您想拥有重点突出的方法(SRP),因此要使公共功能的数量最少。还要考虑一下:假设有人想使用您的Client类来读取具有用户名和密码的其他文件格式。该人必须修改您的代码,才能添加另一个read方法。但是,如果read_clients函数是外部函数,并且只使用了__init__类的Client,那么有人可以在某个地方 add 一个新的自由函数。这是OCP

Client可能只是collections.NamedTuple。像这样:

import collections

Client = collections.namedtuple('Client', ['username', 'password'])

def read_clients(filename):
    clients = []

    # open file
        # loop over all lines in the list
            # Parse the username and password.
            username = # …
            password = # …
            client = Client(username, password)
            clients.append(client)

    return clients

您不必自己定义类Client,并且所有Client对象(例如client)都将具有属性client.usernameclient.password

使用passwordusername没问题,因为__init__的参数在不同的范围内。您只能通过self.访问班级的成员,所以这也不是问题。

如果您真的想动态创建变量,可以使用see here的方法。但是,您还是想抽象出实际的客户数量,这就是列表的目的。

答案 2 :(得分:1)

不要动态创建变量!而是使用Python的内置字典对象,该对象可让您按键查找值。

class Client:

    def __init__(self, username, password):
        self.username = username
        self.password = password

def read(file):
    clients = {}
    for line in file:
        if not line:
            continue  # allows blank lines anywhere
        else:
            id, name, password = line.split(" | ")
            password = password.strip("\n")
            clients[id] = Client(name, password)
    return clients

if __name__ == '__main__':
    data = """\
1 | admin | Z9?zzz
2 | John | J1!jjj
3 | Steve | S1!sss
"""
    from io import StringIO
    with StringIO(data) as file:
        clients = read(file)
    for id, client in clients.items():
        print(id, client.username, client.password)

read函数用作类的方法有点令人困惑,因为调用它然后需要您创建一个Client实例才能调用它。一种替代方法是将其重铸为classmethod,但这会使事情复杂化,因此,将其作为一个简单的函数更有意义。而不是返回列表,它返回一个Client的字典,每个字典都以其各自的id为键。

我已经稍微简化了处理过程(尽管仍然没有错误处理),并使循环对空白行更健壮。

我还添加了一些测试代码,以使您能够验证是否已根据测试数据正确创建了客户端,并使您可以选择的类和函数import可用。将with StringIO(data) as file:替换为with open("Clients.txt") as file:可以使用真实的数据文件。