【问题标题】:Printing employee object information from a dictionary of employees从员工字典中打印员工对象信息
【发布时间】:2018-07-12 18:50:24
【问题描述】:

我试图在不使用 for 循环的情况下从员工对象字典中打印员工对象信息。这是我到目前为止所做的:

employee_dict = {}

class Employee:

  def __init__(self, id, salary):
    self.id = id
    self.salary = salary
    self.employee_dictionary(self)

  def info(self):
    return "Employee ID:{} \nSalary:{}".format(self.id, self.salary)

  def employee_dictionary(self):
    employee_dict = {self.id: self}

emp = Employee(1, 10)
emp1 = Employee(2, 5)

employee = employee_dict[1]

print(employee.info())

有人能指出我正确的方向吗?我觉得我很亲近。这段代码给我的错误是:

Traceback (most recent call last):
  File "/home/rob/PycharmProjects/untitled4/sdfghsfdgjrtyrty.py", line 19, in <module>
    employee = employee_dict[1]
KeyError: 1

【问题讨论】:

  • 你在这个问题上的缩进是否正确?如,它代表你正在使用什么?
  • 你几乎肯定想要的是employee_dict[self.id] = self,而不是employee_dict = {self.id: self}。您想将此员工添加到字典中,而不是将字典替换为仅处理该员工的字典,对吗?如果是这样,这意味着您的 other 问题,即与本地与全局相关的问题,不会首先出现,因此您无需解决它。

标签: python python-3.x dictionary


【解决方案1】:

这里的根本问题是您没有正确使用字典。您不是将新员工添加到现有的全局 employee_dict,而是尝试用一个仅包含新员工的新 dict 替换该全局 dict:

def employee_dictionary(self):
  employee_dict = {self.id: self}

如果你解决了这个问题,你真正要问的问题甚至不会出现:

def employee_dictionary(self):
  employee_dict[self.id] = self

这将在字典中添加一个从self.idself 的新映射。因此,在你的两个 Employee 构造之后,ID 为 1 和 2,你最终会在字典中得到两个条目,分别是键 12,一切都会正常工作。


此外,您绝对应该至少考虑另外两个更改:

  • 用类属性替换全局变量,也可能用类方法替换employee_dictionary方法,详见scharette's answer
  • 将方法从 employee_dictionary 重命名为 (a) 标记为私有(以 _ 开头的方法按照惯例是私有的,这意味着您的用户知道他们不应该调用它,除非他们有一个奇怪的用例) ,并且(b)更能反映它的作用。也许是_register_add_to_dict

其中的第一个(同样,在 scharette 的答案中详述)已经解决了您所询问的问题。 (但你仍然需要主要修复。)


但您可能还是想了解更直接的范围问题——以及当您无法让它消失时如何解决它。

在 Python 中,您在函数中分配的任何名称都是局部变量。如果函数中有 spam = … (或 with eggs as spam: 或其他类型的东西也算作赋值),则该函数中对 spam 的每个引用都指向该本地。因此,employee_dict = {self.id: self} 创建了一个名为 employee_dict 的局部变量,为其赋值,然后返回,此时所有局部变量都消失了。它碰巧与全局同名这一事实对 Python 来说没有任何意义(尽管对于像你这样的人类读者来说,这显然是令人困惑的)。

你使用的任何名称​​没有在任何地方分配给它是一个本地或封闭或全局或内置变量。当您执行employee_dict[self.id] 时,因为在任何地方都没有employee_dict = …,Python 会搜索本地、封闭、全局和内置范围以查找您所说的employee_dict 的含义,然后它会找到全局。

您可以强制 Python 将名称视为全局变量,即使您已分配给它,也可以在函数顶部使用 global 语句:

def employee_dictionary(self):
  global employee_dict
  employee_dict = {self.id: self}

即使名称已经是全局名称,添加 global 也是安全的。这通常是没有意义的——但是如果你不确定某个东西是否算作赋值(或者只是不确定你的代码的未来读者会确定......),并且你想明确这个变量是一个全局变量,你可以声明它:

def employee_dictionary(self):
  global employee_dict
  employee_dict[self.id] = self

【讨论】:

  • 太棒了,感谢您的帮助!
  • 我真的不认为全球是这里的路。在我看来,推荐他使用 global 远非最佳。
  • @scharette 但是这个答案不建议使用全局。它 (1) 解释了添加到与替换 dict 之间的区别,这是关键问题,(2) 建议不相关的更改(包括使用类属性——我主要用指向您答案的链接替换),然后(3 ) 解释了当前的问题并展示了当您确实需要global 时您将如何 解决它。绝对不打算将其视为使用global 解决此问题的建议。如果真的是这样,您认为哪一部分需要改写或重新组织?
  • 抱歉,在您进行某些编辑之前,我有您答案的缓存版本。我错了你的建议对不起。我只是认为在上一段中解释全局过程可能会导致新用户使用它,因为它是有效的,即使它不是最佳的。无论如何,这可能只是我。感谢您的见解,我实际上通过您的回答了解了一些细节。
  • @scharette 我最初打算解释为什么类属性会更好,但后来你的编辑涵盖了同样的基础甚至更好,所以我重新组织了一些东西,只参考你的。如果中间编辑令人困惑,那可能是我提交编辑过于频繁的错……
【解决方案2】:

这是一个范围问题employee_dict = {} 的行为不像你想象的那样。

您没有在正确的范围内分配值。因此,您实际上从未向其添加Employee。导致KeyError

您正在寻找的可能是像这样使用 类变量

class Employee:
    employee_dict = {}
    def __init__(self, id, salary):
        self.id = id
        self.salary = salary
        self.employee_dictonary(self)

    def info(self):
        return "Employee ID:{} \nSalary:{}".format(self.id, self.salary)

    @classmethod
    def employee_dictonary(cls,current_employee):
        cls.employee_dict[current_employee.id]=current_employee

emp = Employee(1, 10)
emp1 = Employee(2, 5)

print(Employee.employee_dict[1].info())

基本上,为了解释起见,您正在创建一个employee_dict,它将在 Employee 的所有实例之间共享。

另外,你必须记住的最后一件事是,如果有人像这样创建 2 名员工,

emp = Employee(1, 10)
emp2 = Employee(1, 100)

调用print(Employee.employee_dict[1].info())会输出

Employee ID:1 
Salary:100

这可能是您想要的,但仍然认为强调可以覆盖员工是相关的。

【讨论】:

  • 这里的class属性肯定比global好,@classmethod也是个好主意……但是调用参数self有点奇怪。
  • @abarnert 为什么会这样?在这种特殊情况下,我们实际上是在添加他自己。不过,我愿意接受建议。
  • 但是self具有特定的惯用意义,作为普通方法的绑定参数。特别是因为,正如所写,这是类的公共接口的一部分,所以Employee.employee_dictionary(emp3) 应该是有意义的(即使没有理由调用它)。
  • (有 __new__ 方法的先例通常执行 self = object.__new__(cls),但我认为该成语与 __new__ 的特殊性及其用途密切相关。)
  • @abarnert 我明白了。非常感谢您的洞察力。我刚刚编辑。我真的很讨厌在没有测试的情况下发布代码,无论变化多么小。你能帮我一个忙并检查我的编辑吗?
【解决方案3】:

您应该将employee_dict 定义为全局

def employee_dictonary(self):
  global employee_dict

如果没有全局关键字,您将分配给局部变量。

第二个问题是使用employee_dict = {self.id: self} 分配给employee_dict。这样,您就可以在每次调用时覆盖变量。那行应该是employee_dict[self.id] = self

通过该更改,输出为:

Employee ID:1 
Salary:10

在大多数情况下强烈建议不要使用全局变量,请参阅Why are global variables evil?

【讨论】:

  • 使用global 来解决这个问题不是一个强烈的建议,IMO
  • 而且,事实上,无论函数是否缩进为类体的一部分,都不能解决问题。您可以在本地进行测试。
  • 是的,它现在可以工作了。您最初的答案中没有employee_dict[self.id] = self
  • 编辑后,这是误导。一旦你修复了employee_dict = {self.id: self} to be employee_dict[self.id] = self, it's no longer true that the OP should declare it as global, and no longer try that without the global he's assigning to a local. So the whole first half is wrong. And then the second half is correct, but it's confusing because it says he needs to change the code to use employee_dict[self.id] = self`,就在显示已经这样做的代码之后。
  • @abarnert 我已经从函数中删除了分配以使其更清晰
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-08
  • 1970-01-01
  • 2020-11-22
相关资源
最近更新 更多