简体   繁体   English

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

[英]How do I store an object in a dynamically generated variable?

I have a text file with a series of clients. 我有一个带有一系列客户端的文本文件。 Each line has a different client. 每行都有一个不同的客户端。 Each client has an ID, a username, and a password. 每个客户端都有一个ID,一个用户名和一个密码。

I want to create a "Client" class, and generate objects in that class in a loop. 我想创建一个“客户端”类,并在一个循环中在该类中生成对象。 Each object would have a username and a password, and would be stored in a variable that contains the client's ID. 每个对象都有一个用户名和一个密码,并将被存储在包含客户端ID的变量中。 Client 1 would be stored in "client_1", Client 2 would be stored in "client_2", etc. 客户端1将存储在“ client_1”中,客户端2将存储在“ client_2”中,依此类推。

I created the method "read()" that opens the text file, breaks if there are empty lines, and retrieves the ID, username and password for each client (each line). 我创建了“ read()”方法,该方法打开文本文件,如果有空行则中断,并为每个客户端(每行)检索ID,用户名和密码。

What I can't figure out, is how to make it so that when the client's ID is "1", I create an object for that client and store it in the variable "client_1". 我不知道的是如何做到这一点,以便当客户端的ID为“ 1”时,我为该客户端创建一个对象并将其存储在变量“ client_1”中。 When the client's ID is "2", I store client's 2 object in the variable "client_2", and so on. 当客户端的ID为“ 2”时,我将客户端的2对象存储在变量“ client_2”中,依此类推。

But I want to do this automatically, instead of having 9000 clients and having to create 9000 variables myself. 但是我想自动执行此操作,而不是拥有9000个客户端并自己创建9000个变量。

Thanks 谢谢

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

My text file (ID, username, password - from left to right): 我的文本文件(ID,用户名,密码-从左到右):

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

Also, is there a problem if I'm using the "username" and "password" variables in read(), when I have already used them in the def init ? 另外,如果我已经在def init中使用了“ username”和“ password”变量,是否在read()中使用了问题?

Thanks 谢谢

Advices 忠告

  • In your loop, you are using break . 在循环中,您正在使用break Don't do that, what you want to use is continue that will skip this iteration instead of get you out of it. 不要那样做,您要使用的是continue ,它将跳过此迭代而不是使您脱离迭代。

  • You are only using strip('\\n') on your password. 您仅在密码上使用strip('\\n') You should do it on all items (to make sure they are all uniform). 您应该对所有物品都这样做(以确保它们都统一)。 But you were right to use strip('\\n') only on the password case because it's the only one that has \\n . 但是您只应在密码情况下使用strip('\\n')是正确的,因为它是唯一具有\\n的密码。 Don't put argument into strip() and it will take care of all the spaces, tabs, and other \\n , \\r and such. 不要将参数放入strip() ,它将处理所有空格,制表符以及其他\\n\\r等。

  • You should see the self parameter of a class as a box that lives inside it. 您应该将类​​的self参数视为位于其中的box It's the "environement" you can basically access everywhere inside your class. 这是您基本上可以在班级各处访问的“环境” And if you create something inside self , such as self.client , it will not be the same as a single variable named client . 并且,如果您在self内部创建某些内容(例如self.client ,则该名称将与名为client的单个变量不同。 What you probably wanted to do here is assign your client list you just read to the self , such as self.client_list = self.read() . 您在这里可能想要做的就是将刚刚读取的客户列表分配给self ,例如self.client_list = self.read()

About your program 关于您的程序

What you need to do is not create as many variables as there are users . 您需要做的是创建的变量不如用户那么多 But you are right in the philosophy, you want to have them stored in one place. 但是您的哲学正确,您想将它们存储在一个地方。 And that's what you did. 那就是你所做的。 Now, the point of your program is still obscure to us. 现在,您的程序的要点对我们还是很模糊。 But what you probably want to do is : 但是您可能想要做的是:

  1. Have a database in which you know how items are ordered. 有一个数据库您可以在其中了解如何订购商品。 You know that in each element of your users_list, you have the first item that is an id , the second its name and the third its password . 您知道,在users_list的每个元素中,都有第一项是id ,第二项是name ,第三项是password

  2. Make operations based on this database . 根据此数据库进行操作。

    • You want to "load" a client, check if he exists in your database, and match the password he entered with the one you have linked to it ! 您要“加载”一个客户端,检查该客户端是否存在于您的数据库中,并将其输入的密码与您链接的密码相匹配!
    • You want to delete one ? 您要删除一个?
    • Order an ice cream for someone already logged ? 为已经登录的人订购冰淇淋?

What is wrong with the list of clients that you already have? 您已经拥有的客户列表有什么问题? You can access the clients as clients[0] , clients[1] and so forth. 您可以作为clients[0]clients[1]等访问客户clients[0] The list is the abstraction for arbitrary many variables. 该列表是任意多个变量的抽象。 The lists are 0-indexed, so the first element has index 0 . 列表的索引为0,因此第一个元素的索引为0 This can be confusing, especially since some languages like R, FORTRAN or Wolfram Language are 1-indexed. 这可能会造成混淆,尤其是因为某些语言(例如R,FORTRAN或Wolfram语言)是1索引的。 I do not consider that a fundamental problem, you just have to be clear about it. 我不认为这是一个根本性问题,您只需要弄清楚这一点即可。 If it really bothers you, you could use a dict with numeric indices and just map whatever index you want to a customer. 如果确实困扰您,则可以使用带有数字索引的dict ,然后将想要的任何索引映射到客户。

Also I would make read_clients a free function. 另外,我会将read_clients免费功能。 It does not use anything except the public API of the Client class to function. 它仅使用Client类的公共API进行操作。 Therefore it should not be a member function. 因此,它不应该是成员函数。 But if you want to have it in that class, make it a @staticmethod at least because it is not tied to one particular Client . 但是,如果您想在该类中使用它,至少要使其成为@staticmethod ,至少是因为它没有绑定到一个特定的Client

What I mean with public API: Every class that you write has public methods and private methods. 公共API的含义:您编写的每个类都有公共方法和私有方法。 In Python there are no access specifiers, but the convention is that methods starting with an underscore ( _ ) are not be used externally. 在Python中,没有访问说明符,但是约定是,下划线( _ )开头的方法不能在外部使用。 The ones with two underscores (like __init__ ) are also not to be called directly but are called by syntactic sugar. 具有两个下划线的字符(如__init__ )也不能直接调用,而是由语法糖调用。 You want to have focused methods ( SRP ), so have the least amount of public functions possible. 您希望拥有重点突出的方法( SRP ),因此要使公共功能的数量最少。 Also consider this: Say somebody wants to use your Client class to read a different file format with usernames and passwords. 还要考虑以下问题:假设有人想使用您的Client类读取带有用户名和密码的其他文件格式。 That person would have to modify your code in order to add another read method. 该人员必须修改您的代码才能添加其他read方法。 But if the read_clients function was external and just used the __init__ of your Client class, that somebody could just add a new free function somewhere. 但是,如果read_clients函数是外部函数,并且仅使用Client类的__init__ ,则有人可以在某个地方添加一个新的免费函数。 This is the OCP . 这就是OCP

And Client could be just a collections.NamedTuple . 而且Client可能只是一个collections.NamedTuple So like this: 像这样:

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

You do not have to define the class Client yourself, and all Client objects (say client ) will have the attributes client.username and client.password . 您不必自己定义Client类,并且所有Client对象(例如client )都将具有属性client.usernameclient.password

Using password and username is not a problem because the parameter of __init__ are in a different scope. 使用passwordusername不是问题,因为__init__的参数在不同的范围内。 The members of your class can only be accessed via self. 您班上的成员只能通过self.访问self. , so that is not a problem either. ,所以这也不是问题。

If you really wanted to dynamically create variables, there are ways to do so, see here . 如果您真的想动态创建变量,可以使用多种方法, 请参见此处 But again you want to abstract away the actual number of clients and that is what the list is for. 但是,您再次想要抽象出客户的实际数量,这就是列表的目的。

Don't create variables dynamically! 不要动态创建变量! Instead use Python's built-in dictionary object, which allows you to look values up by key. 而是使用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)

It was somewhat confusing to have the read function as a method of the class, since calling it then required you to create a Client instance in order to call it. read函数作为类的方法有点令人困惑,因为调用它然后需要您创建一个Client实例才能调用它。 An alternative was to recast it as a classmethod , but that over-complicates things, so it made more sense as a simple function. 一种替代方法是将其重铸为classmethod ,但是这使事情变得过于复杂,因此作为一个简单函数更有意义。 Instead of returning a list it returns a dictionary of Client s, each keyed on its respective id . 而不是返回列表,而是返回Client的字典,每个字典都以其各自的id键。

I've simplified the processing somewhat (though there is still no error handling), and made the loop more robust to blank lines. 我已经稍微简化了处理过程(尽管仍然没有错误处理),并使循环对空白行更加健壮。

I also added a little bit of test code to allow you to verify that the clients have been correctly created from your test data, and to make your class and function import able should you choose. 我还添加了一些测试代码,以使您可以验证是否已从测试数据中正确创建了客户端,并在可以选择的情况下使类和函数import可能。 Replace with StringIO(data) as file: with with open("Clients.txt") as file: to use a real data file instead. with StringIO(data) as file:替换with StringIO(data) as file:with open("Clients.txt") as file:文件替换:而是使用真实的数据文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM